Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@ on: [push, pull_request]
jobs:
build:

runs-on: macos-latest
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'zulu'
distribution: 'temurin'
java-version: 17
- name: Build Android client
run: ./gradlew assembleDebug
- name: Build Compose Desktop client
run: ./gradlew :compose-desktop:assemble
- name: Build iOS shared code
run: ./gradlew :shared:compileKotlinIosArm64
- name: Tests
run: ./gradlew :shared:testDebugUnitTest
- name: Run shared tests
run: ./gradlew :shared:jvmTest
18 changes: 18 additions & 0 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Compose Desktop CI

on: [push, pull_request]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Build Compose Desktop client
run: ./gradlew :compose-desktop:assemble
25 changes: 25 additions & 0 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: iOS CI

on: [push, pull_request]

jobs:
build:

runs-on: macos-latest

steps:
- uses: actions/checkout@v4
- name: set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Build iOS app
run: |
xcodebuild -project iosApp/iosApp.xcodeproj \
-scheme iosApp \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
build
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ gradle-app.setting
xcschememanagement.plist
*.xcbkptlist
/wearApp/build/

# Kotlin build/session cache
.kotlin/
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WordMasterKMP

![kotlin-version](https://img.shields.io/badge/kotlin-2.2.0-blue?logo=kotlin)
![kotlin-version](https://img.shields.io/badge/kotlin-2.4.10-blue?logo=kotlin)

Kotlin Multiplatform sample heavily inspired by [Wordle](https://www.powerlanguage.co.uk/wordle/) game and also [Word Master](https://github.com/octokatherine/word-master) and [wordle-solver](https://github.com/dlew/wordle-solver) samples. The main game logic/state is included in shared KMP code with basic UI then in following clients
- iOS (SwiftUI)
Expand All @@ -15,19 +15,24 @@ Kotlin Multiplatform sample heavily inspired by [Wordle](https://www.powerlangua

### Shared KMP game logic/state

The shared `WordMasterService` class includes following `StateFlow`s representing the current set of guesses and updated status info for each letter.
The shared `WordMasterService` class exposes the game state as `StateFlow`s:

```
val boardGuesses = StateFlow<ArrayList<ArrayList<String>>>()
val boardStatus = StateFlow<ArrayList<ArrayList<LetterStatus>>>()
val boardGuesses = StateFlow<ArrayList<ArrayList<String>>>() // letters entered on each row
val boardStatus = StateFlow<ArrayList<ArrayList<LetterStatus>>>() // per-letter result
val keyStatus = StateFlow<Map<String, LetterStatus>>() // best status per key, colours the keyboard
val guessError = StateFlow<String?>() // validation feedback
```

The various clients call `WordService.setGuess()` when a user enters a letter and then `WordService.checkGuess()` after row of letters
are entered...UI then reflects any resulting updates to above `StateFlow`'s. The Compose clients for example do that using following (with any updates to those `StateFlow's` triggering recomposition)
Each client renders a board of read-only tiles plus an on-screen keyboard. Key presses call
`WordMasterService.addLetter()` / `removeLetter()`, and `submitGuess()` validates the row against the
word list (surfacing "Not enough letters" / "Not in word list" via `guessError`) before evaluating it
and updating the flows above. The Compose clients observe state as following (with any updates to those
`StateFlow`'s triggering recomposition)

```
val boardGuesses by wordMasterService.boardGuesses.collectAsState()
val boardStatus by wordMasterService.boardStatus.collectAsState()
val keyStatus by wordMasterService.keyStatus.collectAsState()
```


Expand All @@ -41,12 +46,12 @@ On iOS we're using the [KMP-NativeCoroutines](https://github.com/rickclephas/KMP
which are then updated using for example

```
let stream = asyncSequence(for: wordMasterService.boardStatusNative)
let stream = asyncSequence(for: wordMasterService.boardStatus)
for try await data in stream {
self.boardStatus = data as! [[LetterStatus]]
}

let stream = asyncSequence(for: wordMasterService.boardGuessesNative)
let stream = asyncSequence(for: wordMasterService.boardGuesses)
for try await data in stream {
self.boardGuesses = data as! [[String]]
}
Expand Down
1 change: 0 additions & 1 deletion androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
plugins {
id("com.android.application")
kotlin("android")
alias(libs.plugins.compose.compiler)
}

Expand Down
Loading
Loading