├── .github └── workflows │ ├── release.yml │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── logo.png ├── src ├── events │ └── mod.rs ├── main.rs ├── tui │ └── mod.rs ├── ui │ ├── grid.rs │ ├── keyboard.rs │ ├── layout.rs │ └── mod.rs ├── update.rs └── wordle │ ├── data.rs │ ├── files │ ├── answer.txt │ └── guess.txt │ ├── mod.rs │ ├── model.rs │ └── utils.rs ├── wordl.gif └── wordl.png /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | create-release: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: taiki-e/create-gh-release-action@v1 16 | with: 17 | # (required) GitHub token for creating GitHub Releases. 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | upload-assets: 21 | needs: create-release 22 | strategy: 23 | matrix: 24 | os: 25 | - ubuntu-latest 26 | - macos-latest 27 | - windows-latest 28 | runs-on: ${{ matrix.os }} 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: taiki-e/upload-rust-binary-action@v1 32 | with: 33 | # (required) Comma-separated list of binary names (non-extension portion of filename) to build and upload. 34 | # Note that glob pattern is not supported yet. 35 | bin: wordl 36 | # (optional) On which platform to distribute the `.tar.gz` file. 37 | # [default value: unix] 38 | # [possible values: all, unix, windows, none] 39 | tar: unix 40 | # (optional) On which platform to distribute the `.zip` file. 41 | # [default value: windows] 42 | # [possible values: all, unix, windows, none] 43 | zip: windows 44 | # (required) GitHub token for uploading assets to GitHub Releases. 45 | token: ${{ secrets.GITHUB_TOKEN }} 46 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### v0.3.0 4 | - Fix: White color text. Previously terminal theme's text color is used for text color. 5 | 6 | ### v0.2.0 7 | - Fix: Handle edge case in guess letter check when the match already happened initially. Fixes - https://github.com/palerdot/wordl-rs/issues/2 8 | 9 | ### v0.1.6 10 | - Fix: Handle edge cases in guess letter status check 11 | 12 | ### v0.1.5 13 | - Fix: Handle panic when terminal does not have minimum height for keyboard hints. 14 | 15 | ### v0.1.4 16 | - Fix: keyboard hints when multiple letters are present in the guess 17 | - Show version string in the UI 18 | - moved wordle data files to directory 19 | 20 | ### `v0.1.3` 21 | - Fix: Inlining wordle files with rust crate. 22 | 23 | ### `v0.1.2` 24 | - Removed changelog action from github workflow action 25 | 26 | ### `v0.1.1` 27 | - Added `description` to `Cargo.toml`. 28 | 29 | ### `v0.1.0` 30 | - `wordl-rs`: First release version 31 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.7" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "allocator-api2" 34 | version = "0.2.16" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" 37 | 38 | [[package]] 39 | name = "autocfg" 40 | version = "1.1.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 43 | 44 | [[package]] 45 | name = "backtrace" 46 | version = "0.3.69" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 49 | dependencies = [ 50 | "addr2line", 51 | "cc", 52 | "cfg-if", 53 | "libc", 54 | "miniz_oxide", 55 | "object", 56 | "rustc-demangle", 57 | ] 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "1.3.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "2.4.2" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 70 | 71 | [[package]] 72 | name = "bytes" 73 | version = "1.5.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 76 | 77 | [[package]] 78 | name = "cassowary" 79 | version = "0.3.0" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 82 | 83 | [[package]] 84 | name = "cc" 85 | version = "1.0.83" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 88 | dependencies = [ 89 | "libc", 90 | ] 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "1.0.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 97 | 98 | [[package]] 99 | name = "color-eyre" 100 | version = "0.6.2" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" 103 | dependencies = [ 104 | "backtrace", 105 | "color-spantrace", 106 | "eyre", 107 | "indenter", 108 | "once_cell", 109 | "owo-colors", 110 | "tracing-error", 111 | ] 112 | 113 | [[package]] 114 | name = "color-spantrace" 115 | version = "0.2.1" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" 118 | dependencies = [ 119 | "once_cell", 120 | "owo-colors", 121 | "tracing-core", 122 | "tracing-error", 123 | ] 124 | 125 | [[package]] 126 | name = "crossterm" 127 | version = "0.27.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" 130 | dependencies = [ 131 | "bitflags 2.4.2", 132 | "crossterm_winapi", 133 | "futures-core", 134 | "libc", 135 | "mio", 136 | "parking_lot", 137 | "signal-hook", 138 | "signal-hook-mio", 139 | "winapi", 140 | ] 141 | 142 | [[package]] 143 | name = "crossterm_winapi" 144 | version = "0.9.1" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 147 | dependencies = [ 148 | "winapi", 149 | ] 150 | 151 | [[package]] 152 | name = "either" 153 | version = "1.9.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 156 | 157 | [[package]] 158 | name = "eyre" 159 | version = "0.6.11" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" 162 | dependencies = [ 163 | "indenter", 164 | "once_cell", 165 | ] 166 | 167 | [[package]] 168 | name = "futures" 169 | version = "0.3.30" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 172 | dependencies = [ 173 | "futures-channel", 174 | "futures-core", 175 | "futures-executor", 176 | "futures-io", 177 | "futures-sink", 178 | "futures-task", 179 | "futures-util", 180 | ] 181 | 182 | [[package]] 183 | name = "futures-channel" 184 | version = "0.3.30" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 187 | dependencies = [ 188 | "futures-core", 189 | "futures-sink", 190 | ] 191 | 192 | [[package]] 193 | name = "futures-core" 194 | version = "0.3.30" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 197 | 198 | [[package]] 199 | name = "futures-executor" 200 | version = "0.3.30" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 203 | dependencies = [ 204 | "futures-core", 205 | "futures-task", 206 | "futures-util", 207 | ] 208 | 209 | [[package]] 210 | name = "futures-io" 211 | version = "0.3.30" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 214 | 215 | [[package]] 216 | name = "futures-macro" 217 | version = "0.3.30" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 220 | dependencies = [ 221 | "proc-macro2", 222 | "quote", 223 | "syn 2.0.48", 224 | ] 225 | 226 | [[package]] 227 | name = "futures-sink" 228 | version = "0.3.30" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 231 | 232 | [[package]] 233 | name = "futures-task" 234 | version = "0.3.30" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 237 | 238 | [[package]] 239 | name = "futures-util" 240 | version = "0.3.30" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 243 | dependencies = [ 244 | "futures-channel", 245 | "futures-core", 246 | "futures-io", 247 | "futures-macro", 248 | "futures-sink", 249 | "futures-task", 250 | "memchr", 251 | "pin-project-lite", 252 | "pin-utils", 253 | "slab", 254 | ] 255 | 256 | [[package]] 257 | name = "getrandom" 258 | version = "0.2.12" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 261 | dependencies = [ 262 | "cfg-if", 263 | "libc", 264 | "wasi", 265 | ] 266 | 267 | [[package]] 268 | name = "gimli" 269 | version = "0.28.1" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 272 | 273 | [[package]] 274 | name = "hashbrown" 275 | version = "0.14.3" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 278 | dependencies = [ 279 | "ahash", 280 | "allocator-api2", 281 | ] 282 | 283 | [[package]] 284 | name = "heck" 285 | version = "0.4.1" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 288 | 289 | [[package]] 290 | name = "hermit-abi" 291 | version = "0.3.4" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" 294 | 295 | [[package]] 296 | name = "indenter" 297 | version = "0.3.3" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 300 | 301 | [[package]] 302 | name = "indoc" 303 | version = "2.0.4" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" 306 | 307 | [[package]] 308 | name = "itertools" 309 | version = "0.12.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" 312 | dependencies = [ 313 | "either", 314 | ] 315 | 316 | [[package]] 317 | name = "lazy_static" 318 | version = "1.4.0" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 321 | 322 | [[package]] 323 | name = "libc" 324 | version = "0.2.152" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" 327 | 328 | [[package]] 329 | name = "lock_api" 330 | version = "0.4.11" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 333 | dependencies = [ 334 | "autocfg", 335 | "scopeguard", 336 | ] 337 | 338 | [[package]] 339 | name = "log" 340 | version = "0.4.20" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 343 | 344 | [[package]] 345 | name = "lru" 346 | version = "0.12.1" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" 349 | dependencies = [ 350 | "hashbrown", 351 | ] 352 | 353 | [[package]] 354 | name = "memchr" 355 | version = "2.7.1" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 358 | 359 | [[package]] 360 | name = "miniz_oxide" 361 | version = "0.7.1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 364 | dependencies = [ 365 | "adler", 366 | ] 367 | 368 | [[package]] 369 | name = "mio" 370 | version = "0.8.10" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" 373 | dependencies = [ 374 | "libc", 375 | "log", 376 | "wasi", 377 | "windows-sys", 378 | ] 379 | 380 | [[package]] 381 | name = "num_cpus" 382 | version = "1.16.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 385 | dependencies = [ 386 | "hermit-abi", 387 | "libc", 388 | ] 389 | 390 | [[package]] 391 | name = "object" 392 | version = "0.32.2" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 395 | dependencies = [ 396 | "memchr", 397 | ] 398 | 399 | [[package]] 400 | name = "once_cell" 401 | version = "1.19.0" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 404 | 405 | [[package]] 406 | name = "owo-colors" 407 | version = "3.5.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 410 | 411 | [[package]] 412 | name = "parking_lot" 413 | version = "0.12.1" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 416 | dependencies = [ 417 | "lock_api", 418 | "parking_lot_core", 419 | ] 420 | 421 | [[package]] 422 | name = "parking_lot_core" 423 | version = "0.9.9" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 426 | dependencies = [ 427 | "cfg-if", 428 | "libc", 429 | "redox_syscall", 430 | "smallvec", 431 | "windows-targets", 432 | ] 433 | 434 | [[package]] 435 | name = "paste" 436 | version = "1.0.14" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 439 | 440 | [[package]] 441 | name = "pin-project-lite" 442 | version = "0.2.13" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 445 | 446 | [[package]] 447 | name = "pin-utils" 448 | version = "0.1.0" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 451 | 452 | [[package]] 453 | name = "ppv-lite86" 454 | version = "0.2.17" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 457 | 458 | [[package]] 459 | name = "proc-macro2" 460 | version = "1.0.76" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" 463 | dependencies = [ 464 | "unicode-ident", 465 | ] 466 | 467 | [[package]] 468 | name = "quote" 469 | version = "1.0.35" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 472 | dependencies = [ 473 | "proc-macro2", 474 | ] 475 | 476 | [[package]] 477 | name = "rand" 478 | version = "0.8.5" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 481 | dependencies = [ 482 | "libc", 483 | "rand_chacha", 484 | "rand_core", 485 | ] 486 | 487 | [[package]] 488 | name = "rand_chacha" 489 | version = "0.3.1" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 492 | dependencies = [ 493 | "ppv-lite86", 494 | "rand_core", 495 | ] 496 | 497 | [[package]] 498 | name = "rand_core" 499 | version = "0.6.4" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 502 | dependencies = [ 503 | "getrandom", 504 | ] 505 | 506 | [[package]] 507 | name = "ratatui" 508 | version = "0.25.0" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb" 511 | dependencies = [ 512 | "bitflags 2.4.2", 513 | "cassowary", 514 | "crossterm", 515 | "indoc", 516 | "itertools", 517 | "lru", 518 | "paste", 519 | "stability", 520 | "strum", 521 | "unicode-segmentation", 522 | "unicode-width", 523 | ] 524 | 525 | [[package]] 526 | name = "redox_syscall" 527 | version = "0.4.1" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 530 | dependencies = [ 531 | "bitflags 1.3.2", 532 | ] 533 | 534 | [[package]] 535 | name = "rustc-demangle" 536 | version = "0.1.23" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 539 | 540 | [[package]] 541 | name = "rustversion" 542 | version = "1.0.14" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" 545 | 546 | [[package]] 547 | name = "scopeguard" 548 | version = "1.2.0" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 551 | 552 | [[package]] 553 | name = "sharded-slab" 554 | version = "0.1.7" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 557 | dependencies = [ 558 | "lazy_static", 559 | ] 560 | 561 | [[package]] 562 | name = "signal-hook" 563 | version = "0.3.17" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 566 | dependencies = [ 567 | "libc", 568 | "signal-hook-registry", 569 | ] 570 | 571 | [[package]] 572 | name = "signal-hook-mio" 573 | version = "0.2.3" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 576 | dependencies = [ 577 | "libc", 578 | "mio", 579 | "signal-hook", 580 | ] 581 | 582 | [[package]] 583 | name = "signal-hook-registry" 584 | version = "1.4.1" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 587 | dependencies = [ 588 | "libc", 589 | ] 590 | 591 | [[package]] 592 | name = "slab" 593 | version = "0.4.9" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 596 | dependencies = [ 597 | "autocfg", 598 | ] 599 | 600 | [[package]] 601 | name = "smallvec" 602 | version = "1.12.0" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" 605 | 606 | [[package]] 607 | name = "socket2" 608 | version = "0.5.5" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" 611 | dependencies = [ 612 | "libc", 613 | "windows-sys", 614 | ] 615 | 616 | [[package]] 617 | name = "stability" 618 | version = "0.1.1" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" 621 | dependencies = [ 622 | "quote", 623 | "syn 1.0.109", 624 | ] 625 | 626 | [[package]] 627 | name = "strum" 628 | version = "0.25.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" 631 | dependencies = [ 632 | "strum_macros", 633 | ] 634 | 635 | [[package]] 636 | name = "strum_macros" 637 | version = "0.25.3" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" 640 | dependencies = [ 641 | "heck", 642 | "proc-macro2", 643 | "quote", 644 | "rustversion", 645 | "syn 2.0.48", 646 | ] 647 | 648 | [[package]] 649 | name = "syn" 650 | version = "1.0.109" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 653 | dependencies = [ 654 | "proc-macro2", 655 | "quote", 656 | "unicode-ident", 657 | ] 658 | 659 | [[package]] 660 | name = "syn" 661 | version = "2.0.48" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 664 | dependencies = [ 665 | "proc-macro2", 666 | "quote", 667 | "unicode-ident", 668 | ] 669 | 670 | [[package]] 671 | name = "thread_local" 672 | version = "1.1.7" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" 675 | dependencies = [ 676 | "cfg-if", 677 | "once_cell", 678 | ] 679 | 680 | [[package]] 681 | name = "tokio" 682 | version = "1.35.1" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" 685 | dependencies = [ 686 | "backtrace", 687 | "bytes", 688 | "libc", 689 | "mio", 690 | "num_cpus", 691 | "parking_lot", 692 | "pin-project-lite", 693 | "signal-hook-registry", 694 | "socket2", 695 | "tokio-macros", 696 | "windows-sys", 697 | ] 698 | 699 | [[package]] 700 | name = "tokio-macros" 701 | version = "2.2.0" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" 704 | dependencies = [ 705 | "proc-macro2", 706 | "quote", 707 | "syn 2.0.48", 708 | ] 709 | 710 | [[package]] 711 | name = "tokio-util" 712 | version = "0.7.10" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 715 | dependencies = [ 716 | "bytes", 717 | "futures-core", 718 | "futures-sink", 719 | "pin-project-lite", 720 | "tokio", 721 | ] 722 | 723 | [[package]] 724 | name = "tracing" 725 | version = "0.1.40" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 728 | dependencies = [ 729 | "pin-project-lite", 730 | "tracing-core", 731 | ] 732 | 733 | [[package]] 734 | name = "tracing-core" 735 | version = "0.1.32" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 738 | dependencies = [ 739 | "once_cell", 740 | "valuable", 741 | ] 742 | 743 | [[package]] 744 | name = "tracing-error" 745 | version = "0.2.0" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" 748 | dependencies = [ 749 | "tracing", 750 | "tracing-subscriber", 751 | ] 752 | 753 | [[package]] 754 | name = "tracing-subscriber" 755 | version = "0.3.18" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 758 | dependencies = [ 759 | "sharded-slab", 760 | "thread_local", 761 | "tracing-core", 762 | ] 763 | 764 | [[package]] 765 | name = "unicode-ident" 766 | version = "1.0.12" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 769 | 770 | [[package]] 771 | name = "unicode-segmentation" 772 | version = "1.10.1" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 775 | 776 | [[package]] 777 | name = "unicode-width" 778 | version = "0.1.11" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 781 | 782 | [[package]] 783 | name = "valuable" 784 | version = "0.1.0" 785 | source = "registry+https://github.com/rust-lang/crates.io-index" 786 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 787 | 788 | [[package]] 789 | name = "version_check" 790 | version = "0.9.4" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 793 | 794 | [[package]] 795 | name = "wasi" 796 | version = "0.11.0+wasi-snapshot-preview1" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 799 | 800 | [[package]] 801 | name = "winapi" 802 | version = "0.3.9" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 805 | dependencies = [ 806 | "winapi-i686-pc-windows-gnu", 807 | "winapi-x86_64-pc-windows-gnu", 808 | ] 809 | 810 | [[package]] 811 | name = "winapi-i686-pc-windows-gnu" 812 | version = "0.4.0" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 815 | 816 | [[package]] 817 | name = "winapi-x86_64-pc-windows-gnu" 818 | version = "0.4.0" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 821 | 822 | [[package]] 823 | name = "windows-sys" 824 | version = "0.48.0" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 827 | dependencies = [ 828 | "windows-targets", 829 | ] 830 | 831 | [[package]] 832 | name = "windows-targets" 833 | version = "0.48.5" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 836 | dependencies = [ 837 | "windows_aarch64_gnullvm", 838 | "windows_aarch64_msvc", 839 | "windows_i686_gnu", 840 | "windows_i686_msvc", 841 | "windows_x86_64_gnu", 842 | "windows_x86_64_gnullvm", 843 | "windows_x86_64_msvc", 844 | ] 845 | 846 | [[package]] 847 | name = "windows_aarch64_gnullvm" 848 | version = "0.48.5" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 851 | 852 | [[package]] 853 | name = "windows_aarch64_msvc" 854 | version = "0.48.5" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 857 | 858 | [[package]] 859 | name = "windows_i686_gnu" 860 | version = "0.48.5" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 863 | 864 | [[package]] 865 | name = "windows_i686_msvc" 866 | version = "0.48.5" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 869 | 870 | [[package]] 871 | name = "windows_x86_64_gnu" 872 | version = "0.48.5" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 875 | 876 | [[package]] 877 | name = "windows_x86_64_gnullvm" 878 | version = "0.48.5" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 881 | 882 | [[package]] 883 | name = "windows_x86_64_msvc" 884 | version = "0.48.5" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 887 | 888 | [[package]] 889 | name = "wordl" 890 | version = "0.2.0" 891 | dependencies = [ 892 | "color-eyre", 893 | "crossterm", 894 | "futures", 895 | "rand", 896 | "ratatui", 897 | "tokio", 898 | "tokio-util", 899 | ] 900 | 901 | [[package]] 902 | name = "zerocopy" 903 | version = "0.7.32" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 906 | dependencies = [ 907 | "zerocopy-derive", 908 | ] 909 | 910 | [[package]] 911 | name = "zerocopy-derive" 912 | version = "0.7.32" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 915 | dependencies = [ 916 | "proc-macro2", 917 | "quote", 918 | "syn 2.0.48", 919 | ] 920 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wordl" 3 | version = "0.3.0" 4 | edition = "2021" 5 | description = "Terminal Wordle game. Web like with keyboard hints and slow reveal animations." 6 | license = "GPL-3.0" 7 | repository = "https://github.com/palerdot/wordl-rs" 8 | documentation = "https://github.com/palerdot/wordl-rs" 9 | homepage = "https://github.com/palerdot/wordl-rs" 10 | readme = "README.md" 11 | keywords = ["wordle", "terminal", "games"] 12 | categories = ["command-line-utilities", "games"] 13 | exclude = [ 14 | "logo.png", 15 | "wordl.gif", 16 | "wordl.png", 17 | ] 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [dependencies] 22 | color-eyre = "0.6.2" 23 | crossterm = { version = "0.27.0", features = ["event-stream"] } 24 | futures = "0.3.30" 25 | rand = "0.8.5" 26 | ratatui = "0.25.0" 27 | tokio = { version = "1.35.1", features = ["full"] } 28 | tokio-util = "0.7.10" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wordl 2 | 3 | `Terminal wordle`. Web like terminal wordle game with keyboard hints, slow reveal animations etc. 4 | 5 |

6 | Wordl - Web like Terminal Wordle 7 |

8 | 9 | ![Status](https://github.com/palerdot/wordl-rs/actions/workflows/rust.yml/badge.svg) 10 | ![Crates.io Version](https://img.shields.io/crates/v/wordl) 11 | 12 | > Terminal Wordle. 13 | 14 | **WORDL** aims to provide web like `Wordle` experience in the terminal, mainly 15 | - `Slow Reveal` letter colors after guessing. 16 | - Keyboard Hints 17 | 18 | Wordl - Web like Terminal Wordle 19 | 20 | ### Installation 21 | 22 | Install from [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html). 23 | 24 | ``` 25 | cargo install wordl 26 | ``` 27 | 28 | Precompiled binaries are available in [Latest Releases page](https://github.com/palerdot/wordl-rs/releases/latest). Please note, the precompiled binaries are **not signed**. For example, in `MacOS`, you have to manually allow running the binary in `Security & Privacy` settings. 29 | 30 | ### Update 31 | 32 | Update installed binary with [cargo-update](https://github.com/nabijaczleweli/cargo-update) like 33 | 34 | ``` 35 | cargo install-update wordl 36 | ``` 37 | 38 | If you don't want to install `cargo-update`, you can manually uninstall and install the latest binary from cargo. 39 | 40 | ### About 41 | 42 | Rules are pretty simple. 43 | - Type your guess and press `enter`. Press `Backspace` to clear already typed letter. 44 | - Press `Ctrl-N` to start a new wordle guess. Please note, this only works if the game is over. i.e. You have guessed correctly or you ran out of 6 attempts. 45 | - Press `Esc` or `Ctrl-C` to exit the game. 46 | 47 | If the terminal window is too small, keyboard hints are not shown. Please make sure your terminal has minimum required height for the keyboard hints to be shown. 48 | 49 | `wordl-rs` is built with [Rust Ratatui library](https://github.com/ratatui-org/ratatui). 50 | 51 | #### Wordle Words list 52 | 53 | Data for Wordle words are present as `.txt` files in [`/files`](./src/wordle/files) directory. The data was initially seeded from [here](https://gist.github.com/cfreshman/a7b776506c73284511034e63af1017ee) and [here](https://gist.github.com/cfreshman/d5fb56316158a1575898bba1eed3b5da). Right now, wordle words list is not synced. If you want any words to be added or removed, please submit a PR. 54 | 55 | 56 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palerdot/wordl-rs/ebdbd05c52c394feb6bbb90d4665a1284ddc73a5/logo.png -------------------------------------------------------------------------------- /src/events/mod.rs: -------------------------------------------------------------------------------- 1 | // ref: https://ratatui.rs/tutorials/counter-app/multiple-files/event/ 2 | use crossterm::event::{Event as CrosstermEvent, KeyEvent, MouseEvent}; 3 | use futures::{FutureExt, StreamExt}; 4 | use std::time::Duration; 5 | use tokio::sync::mpsc; 6 | 7 | use crate::wordle::model::Message; 8 | 9 | /// terminal events 10 | #[derive(Clone, Debug)] 11 | pub enum Event { 12 | /// Terminal tick 13 | Tick, 14 | /// Keyboard events 15 | Key(KeyEvent), 16 | /// Handle state updates directly 17 | StateUpdate(Message), 18 | /// Mouse events 19 | Mouse(MouseEvent), 20 | /// resize events 21 | Resize(u16, u16), 22 | } 23 | 24 | /// Terminal event handler 25 | #[derive(Debug)] 26 | pub struct EventHandler { 27 | /// event sender channel 28 | #[allow(dead_code)] 29 | sender: mpsc::UnboundedSender, 30 | /// event receiver channel 31 | receiver: mpsc::UnboundedReceiver, 32 | #[allow(dead_code)] 33 | /// event handler thread 34 | handler: tokio::task::JoinHandle<()>, 35 | } 36 | 37 | impl EventHandler { 38 | /// constructs new instance of 'Event Handler' 39 | pub fn new(tick_rate: u64) -> Self { 40 | let tick_rate = Duration::from_millis(tick_rate); 41 | let (sender, receiver) = mpsc::unbounded_channel(); 42 | let _sender = sender.clone(); 43 | let handler = tokio::spawn(async move { 44 | let mut reader = crossterm::event::EventStream::new(); 45 | let mut tick = tokio::time::interval(tick_rate); 46 | 47 | loop { 48 | let tick_delay = tick.tick(); 49 | let crossterm_event = reader.next().fuse(); 50 | tokio::select! { 51 | _ = tick_delay => { 52 | _sender.send(Event::Tick).unwrap(); 53 | } 54 | Some(Ok(evt)) = crossterm_event => { 55 | match evt { 56 | CrosstermEvent::Key(key) => { 57 | // handle only press events 58 | if key.kind == crossterm::event::KeyEventKind::Press { 59 | _sender.send(Event::Key(key)).unwrap(); 60 | } 61 | }, 62 | 63 | CrosstermEvent::Mouse(mouse) => { 64 | _sender.send(Event::Mouse(mouse)).unwrap(); 65 | }, 66 | 67 | CrosstermEvent::Resize(w,h) => { 68 | _sender.send(Event::Resize(w,h)).unwrap(); 69 | }, 70 | 71 | CrosstermEvent::FocusGained => {}, 72 | CrosstermEvent::FocusLost => {}, 73 | CrosstermEvent::Paste(_) => {}, 74 | } 75 | } 76 | }; 77 | } 78 | }); 79 | 80 | Self { 81 | sender, 82 | receiver, 83 | handler, 84 | } 85 | } 86 | 87 | // Receives next event from the handler thread 88 | pub async fn next(&mut self) -> color_eyre::Result { 89 | self.receiver 90 | .recv() 91 | .await 92 | .ok_or(color_eyre::eyre::eyre!("Unable to get event")) 93 | } 94 | 95 | // send delayed state update message 96 | pub async fn send_delayed_message(&self, delay: u64, message: Message) { 97 | let sender = self.sender.clone(); 98 | 99 | tokio::spawn(async move { 100 | tokio::time::sleep(Duration::from_millis(delay)).await; 101 | sender.send(Event::StateUpdate(message)).unwrap(); 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // ref: https://ratatui.rs/concepts/application-patterns/the-elm-architecture/ 2 | 3 | use ratatui::{backend::CrosstermBackend, Terminal}; 4 | 5 | use events::{Event, EventHandler}; 6 | use tui::Tui; 7 | use update::{handle_key_event, update}; 8 | use wordle::model::{Model, RunningState}; 9 | 10 | pub mod events; 11 | pub mod tui; 12 | pub mod ui; 13 | pub mod update; 14 | pub mod wordle; 15 | 16 | #[tokio::main] 17 | async fn main() -> color_eyre::Result<()> { 18 | color_eyre::install()?; 19 | let result = run().await; 20 | result?; 21 | 22 | Ok(()) 23 | } 24 | 25 | async fn run() -> color_eyre::Result<()> { 26 | // TEA - The ELM architecture 27 | // Model | Update | View 28 | let mut model = Model::new(); 29 | 30 | // init terminal 31 | let backend = CrosstermBackend::new(std::io::stderr()); 32 | let terminal = Terminal::new(backend)?; 33 | let events = EventHandler::new(250); 34 | 35 | let mut tui = Tui::new(terminal, events); 36 | tui.enter()?; 37 | 38 | // set up terminal and listen for events 39 | while model.running_state != RunningState::Done { 40 | // render user interface 41 | tui.draw(&mut model)?; 42 | // Handle events (we will sending tick events periodically) 43 | match tui.events.next().await? { 44 | Event::Tick => {} 45 | Event::Key(key_event) => { 46 | if let Some(message) = handle_key_event(key_event) { 47 | update(&mut model, message, &tui.events).await; 48 | } 49 | } 50 | Event::StateUpdate(message) => { 51 | update(&mut model, message, &tui.events).await; 52 | } 53 | Event::Mouse(_) => {} 54 | Event::Resize(_, _) => {} 55 | } 56 | } 57 | 58 | // exit the terminal 59 | tui.exit()?; 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /src/tui/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{io, panic}; 2 | 3 | use color_eyre::Result; 4 | 5 | use crossterm::{ 6 | event::{DisableMouseCapture, EnableMouseCapture}, 7 | terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}, 8 | }; 9 | 10 | use crate::events::EventHandler; 11 | use crate::ui; 12 | use crate::wordle::model::Model; 13 | 14 | pub type CrosstermTerminal = ratatui::Terminal>; 15 | 16 | // Representation of terminal user interface 17 | // Sets up terminal and handles events 18 | pub struct Tui { 19 | // interface to the terminal 20 | terminal: CrosstermTerminal, 21 | // event handler 22 | pub events: EventHandler, 23 | } 24 | 25 | impl Tui { 26 | // constructs new instance of 'Tui' 27 | pub fn new(terminal: CrosstermTerminal, events: EventHandler) -> Self { 28 | Self { terminal, events } 29 | } 30 | 31 | // Initializes the terminal interface 32 | // enables raw mode and sets terminal properties 33 | pub fn enter(&mut self) -> Result<()> { 34 | terminal::enable_raw_mode()?; 35 | crossterm::execute!(io::stderr(), EnterAlternateScreen, EnableMouseCapture)?; 36 | 37 | // define custom panic hook to reset terminal properties 38 | let panic_hook = panic::take_hook(); 39 | panic::set_hook(Box::new(move |panic| { 40 | Self::reset().expect("failed to reset the terminal"); 41 | panic_hook(panic); 42 | })); 43 | 44 | self.terminal.hide_cursor()?; 45 | self.terminal.clear()?; 46 | Ok(()) 47 | } 48 | 49 | // Resets the terminal interface 50 | // also used by panic hook to revert terminal properties during unexpected error 51 | fn reset() -> Result<()> { 52 | terminal::disable_raw_mode()?; 53 | crossterm::execute!(io::stderr(), LeaveAlternateScreen, DisableMouseCapture)?; 54 | 55 | Ok(()) 56 | } 57 | 58 | // exits the terminal interface 59 | // disables raw mode and reverts back terminal properties 60 | pub fn exit(&mut self) -> Result<()> { 61 | Self::reset()?; 62 | self.terminal.show_cursor()?; 63 | 64 | Ok(()) 65 | } 66 | 67 | // draw the terminal interface 68 | pub fn draw(&mut self, model: &mut Model) -> Result<()> { 69 | self.terminal.draw(|frame| ui::view(model, frame))?; 70 | Ok(()) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/ui/grid.rs: -------------------------------------------------------------------------------- 1 | // use ratatui::widgets::block::Title; 2 | use crate::wordle::model::{LetterState, LetterStatus, Model}; 3 | use ratatui::{prelude::*, widgets::*}; 4 | 5 | use super::get_grid_color; 6 | 7 | pub fn draw(frame: &mut Frame, rect: Rect, model: &mut Model) { 8 | // draw empty grid 9 | for row in 0..=5 { 10 | for column in 0..5 { 11 | render( 12 | frame, 13 | rect, 14 | Grid { 15 | row, 16 | column, 17 | letter_status: LetterStatus { 18 | letter: ' ', 19 | status: LetterState::Unknown, 20 | }, 21 | }, 22 | ); 23 | } 24 | } 25 | 26 | for (row, guess) in model.guesses.iter().enumerate() { 27 | for (column, guess_status) in guess.into_iter().enumerate() { 28 | let grid = Grid { 29 | row, 30 | column, 31 | letter_status: guess_status.clone(), 32 | }; 33 | render(frame, rect, grid); 34 | } 35 | } 36 | 37 | // render active guess 38 | if model.active_guess.len() > 0 { 39 | let row = model.guesses.len(); 40 | for (column, letter) in model.active_guess.chars().enumerate() { 41 | let grid = Grid { 42 | row, 43 | column, 44 | letter_status: LetterStatus { 45 | letter, 46 | status: LetterState::Unknown, 47 | }, 48 | }; 49 | render(frame, rect, grid); 50 | } 51 | } 52 | } 53 | 54 | struct Grid { 55 | row: usize, 56 | column: usize, 57 | letter_status: LetterStatus, 58 | } 59 | 60 | fn render(frame: &mut Frame, rect: Rect, grid: Grid) { 61 | let width = 5; 62 | let height = 3; 63 | let row = grid.row; 64 | let column = grid.column; 65 | 66 | let letter = grid.letter_status.letter.to_uppercase().to_string(); 67 | let offset: u16 = u16::from((rect.width - rect.left()) / 2) - 12; 68 | let x = rect.left() + u16::from(column as u16) * width + offset; 69 | let area = Rect { 70 | x, 71 | y: rect.top() + (row as u16 * height) + 1, 72 | width, 73 | height, 74 | }; 75 | 76 | let bg = get_grid_color(grid.letter_status.status); 77 | 78 | let block = Block::new() 79 | .borders(Borders::ALL) 80 | .border_type(BorderType::QuadrantOutside) 81 | .border_style(Style::new().fg(Color::Rgb(0, 0, 0))) 82 | // .padding(Padding::new(1, 1, 1, 1)) 83 | // .style(Style::new().white().on_black().bg(Color::Rgb(0, 0, 0))); 84 | .style( 85 | Style::new() 86 | .white() 87 | .on_black() 88 | .bg(bg) 89 | .fg(Color::Rgb(255, 255, 255)) 90 | .bold(), 91 | ); 92 | 93 | frame.render_widget( 94 | Paragraph::new(format!("{}", letter.to_string())) 95 | .block(block) 96 | .style( 97 | Style::new() 98 | .white() 99 | .on_black() 100 | .bg(bg) 101 | .fg(Color::Rgb(255, 255, 255)), 102 | ) 103 | .alignment(Alignment::Center), 104 | area, 105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /src/ui/keyboard.rs: -------------------------------------------------------------------------------- 1 | use ratatui::widgets::block::Title; 2 | use ratatui::{prelude::*, widgets::*}; 3 | 4 | use super::get_grid_color; 5 | use crate::wordle::model::{KeyboardHints, LetterState}; 6 | 7 | pub fn draw(frame: &mut Frame, rect: Rect, hints: &mut KeyboardHints) { 8 | let github_link = String::from("https://github.com/palerdot/wordl-rs"); 9 | let version = env!("CARGO_PKG_VERSION"); 10 | 11 | let version_string = if version.is_empty() { 12 | "".into() 13 | } else { 14 | format!(" (v{})", version) 15 | }; 16 | 17 | let title_text = format!(" {}{} ", github_link, version_string); 18 | 19 | let master_block = Block::new() 20 | .title(Title::from(title_text).alignment(Alignment::Center)) 21 | .borders(Borders::TOP) 22 | // .border_style(Style::new().fg(Color::Rgb(255, 0, 0))) 23 | .border_style(Style::new().fg(Color::Cyan)) 24 | .style(Style::new().white().on_black().bg(Color::Rgb(0, 0, 0))); 25 | 26 | frame.render_widget(master_block, rect); 27 | 28 | let keyboard_size = (10 * 5) / 2; // 10 letters max and width 5 29 | 30 | let letters = vec!["qwertyuiop".chars(), "asdfghjkl".chars(), "zxcvbnm".chars()]; 31 | 32 | let width = 5; 33 | let height = 3; 34 | 35 | for (row_index, row) in letters.iter().enumerate() { 36 | for (index, letter) in row.clone().enumerate() { 37 | let offset: u16 = u16::from((rect.width - rect.left()) / 2) - keyboard_size; 38 | let x = rect.left() + u16::from(index as u16) * width + offset + (row_index as u16 * 3); 39 | let area = Rect { 40 | x, 41 | y: rect.top() + (row_index as u16 * height + 1), 42 | width, 43 | height, 44 | }; 45 | 46 | let letter_hint = hints.get(&letter); 47 | let letter_status = if letter_hint.is_some() { 48 | letter_hint.unwrap().clone() 49 | } else { 50 | LetterState::Unknown 51 | }; 52 | 53 | let bg = get_grid_color(letter_status); 54 | 55 | let block = Block::new() 56 | .borders(Borders::ALL) 57 | .border_type(BorderType::QuadrantOutside) 58 | .border_style(Style::new().fg(Color::Rgb(0, 0, 0))) 59 | .style(Style::new().white().on_black().bg(bg).bold()); 60 | 61 | frame.render_widget( 62 | Paragraph::new(format!("{}", letter.to_string())) 63 | .block(block) 64 | .style( 65 | Style::new() 66 | .white() 67 | .on_black() 68 | .bg(bg) 69 | .fg(Color::Rgb(255, 255, 255)), 70 | ) 71 | .alignment(Alignment::Center), 72 | area, 73 | ); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ui/layout.rs: -------------------------------------------------------------------------------- 1 | use ratatui::prelude::*; 2 | use std::rc::Rc; 3 | 4 | pub fn master_layout(frame: &mut Frame) -> Rc<[ratatui::layout::Rect]> { 5 | let size = frame.size(); 6 | 7 | let constraints = if size.height >= 31 { 8 | vec![Constraint::Percentage(66), Constraint::Percentage(34)] 9 | } else { 10 | vec![Constraint::Percentage(100)] 11 | }; 12 | 13 | Layout::default() 14 | .direction(Direction::Vertical) 15 | .constraints(constraints) 16 | .split(frame.size()) 17 | } 18 | -------------------------------------------------------------------------------- /src/ui/mod.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{ 2 | prelude::{Alignment, Color, Frame, Rect, Span}, 3 | style::{Style, Stylize}, 4 | widgets::{block::Position, Block, Paragraph, Wrap}, 5 | }; 6 | 7 | use crate::wordle::model::{GameResult, LetterState, Model, RunningState}; 8 | 9 | mod grid; 10 | mod keyboard; 11 | mod layout; 12 | 13 | // [ELM VIEW] view is a function of model 14 | pub fn view(model: &mut Model, f: &mut Frame) { 15 | let block = Block::default() 16 | // .title(format!("{}", model.wordle,)) 17 | .title_alignment(Alignment::Center) 18 | .border_style(Style::default().fg(Color::White)) 19 | .style(Style::default().white().bg(Color::Rgb(0, 0, 0))) 20 | .title(get_status(model)) 21 | .title_position(Position::Top); 22 | 23 | let master_layout = layout::master_layout(f); 24 | 25 | let common_text = 26 | "Type and enter the guess. Backspace to clear. Ctrl-N for new wordle. Esc/Ctrl-C to quit." 27 | .to_string(); 28 | let help_text = if master_layout.len() == 1 { 29 | format!( 30 | "{}. Check https://github.com/palerdot/wordl-rs for more info.", 31 | common_text 32 | ) 33 | } else { 34 | common_text 35 | }; 36 | let help_text_block = Paragraph::new(help_text) 37 | .alignment(Alignment::Center) 38 | .wrap(Wrap { trim: true }) 39 | .style(Style::new().fg(Color::Rgb(189, 189, 189))); 40 | 41 | let has_min_height = master_layout.len() == 2; 42 | 43 | // top layout 44 | f.render_widget(block, master_layout[0]); 45 | // main grid 46 | grid::draw(f, master_layout[0], model); 47 | // status text 48 | f.render_widget( 49 | help_text_block, 50 | Rect { 51 | x: 0, 52 | y: master_layout[0].height - if has_min_height { 1 } else { 3 }, 53 | height: 2, 54 | width: f.size().width - 5, // ..master_layout[0] 55 | }, 56 | ); 57 | 58 | if has_min_height { 59 | // keyboard layout 60 | keyboard::draw(f, master_layout[1], &mut model.keyboard_hints); 61 | } 62 | } 63 | 64 | fn get_status(model: &mut Model) -> Span { 65 | let step = model.guesses.len(); 66 | 67 | match &model.running_state { 68 | RunningState::Waiting => Span::styled( 69 | format!("{}/6: Enter your guess", step), 70 | Style::default() 71 | .fg(Color::Rgb(189, 189, 189)) 72 | .bg(Color::Rgb(0, 0, 0)), 73 | ), 74 | RunningState::Calculating => Span::styled( 75 | format!("{}/6: Checking", step), 76 | Style::default() 77 | .fg(Color::Rgb(189, 189, 189)) 78 | .bg(Color::Rgb(0, 0, 0)), 79 | ), 80 | RunningState::Over(result) => { 81 | let is_correct = if *result == GameResult::CorrectGuess { 82 | true 83 | } else { 84 | false 85 | }; 86 | 87 | let answer = model.wordle.to_uppercase().to_string(); 88 | 89 | Span::styled( 90 | if is_correct { 91 | "Correct 😇".into() 92 | } else { 93 | format!("{} is the correct word", answer) 94 | }, 95 | Style::default() 96 | .fg(if is_correct { 97 | Color::Rgb(0, 255, 0) 98 | } else { 99 | Color::Rgb(255, 95, 135) 100 | }) 101 | .bg(Color::Rgb(0, 0, 0)), 102 | ) 103 | } 104 | _ => Span::styled( 105 | "", 106 | Style::default() 107 | .fg(Color::Rgb(0, 255, 0)) 108 | .bg(Color::Rgb(0, 0, 0)), 109 | ), 110 | } 111 | } 112 | 113 | // helper function to get grid color 114 | pub fn get_grid_color(letter_state: LetterState) -> Color { 115 | match letter_state { 116 | LetterState::Correct => Color::Rgb(0, 135, 0), 117 | LetterState::Incorrect => Color::Rgb(215, 175, 0), 118 | LetterState::NotPresent => Color::Rgb(88, 88, 88), 119 | LetterState::Unknown => Color::Rgb(48, 48, 48), 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/update.rs: -------------------------------------------------------------------------------- 1 | use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; 2 | use wordle::model::{Message, Model, RunningState}; 3 | 4 | use crate::events::EventHandler; 5 | use crate::wordle; 6 | use crate::wordle::model::GameResult; 7 | // use crate::wordle::model::{LetterState, LetterStatus}; 8 | 9 | pub async fn update(model: &mut Model, msg: Message, event_handler: &EventHandler) { 10 | match msg { 11 | Message::Listen(letter) => { 12 | // we will listen only if it is in waiting state 13 | if model.running_state != RunningState::Waiting { 14 | return; 15 | } 16 | 17 | // we have to handle the letter only if active guess is incomplete 18 | if model.active_guess.len() < 5 { 19 | let updated_guess = format!("{}{}", model.active_guess, letter.to_lowercase()) 20 | .to_lowercase() 21 | .into(); 22 | model.active_guess = updated_guess; 23 | } 24 | } 25 | Message::CalculateStart => { 26 | // start calculation only if the guess has 5 letters 27 | if model.active_guess.len() != 5 { 28 | return; 29 | } 30 | 31 | // we have to check if active guess is valid 32 | let is_valid_active_guess = model.valid_wordles.contains(&model.active_guess) 33 | || model.valid_guesses.contains(&model.active_guess); 34 | 35 | if !is_valid_active_guess { 36 | // reset active guess and abort 37 | model.active_guess = "".into(); 38 | 39 | return; 40 | } 41 | 42 | // first change state to calculating 43 | model.running_state = RunningState::Calculating; 44 | let guess = 45 | wordle::utils::check(model.wordle.to_string(), model.active_guess.to_string()); 46 | // reset active guess 47 | model.active_guess = "".into(); 48 | let latest_position = model.guesses.len(); 49 | // // insert empty vector 50 | model.guesses.insert(latest_position, Vec::new()); 51 | 52 | event_handler 53 | .send_delayed_message(15, Message::AnimateGuess(0, guess)) 54 | .await; 55 | } 56 | 57 | Message::AnimateGuess(guess_position, guess) => { 58 | let latest_position = model.guesses.len() - 1; 59 | 60 | if let Some(current) = model.guesses.get_mut(latest_position) { 61 | if let Some(guess_letter) = guess.get(guess_position) { 62 | current.push(guess_letter.clone()); 63 | // once last letter is animated don't send delayed event 64 | let is_last_letter = guess_position + 1 == guess.len(); 65 | 66 | if is_last_letter { 67 | event_handler 68 | .send_delayed_message(0, Message::CalculateEnd(guess)) 69 | .await; 70 | } else { 71 | event_handler 72 | .send_delayed_message( 73 | 515, 74 | Message::AnimateGuess(guess_position + 1, guess), 75 | ) 76 | .await; 77 | } 78 | } 79 | } 80 | } 81 | Message::CalculateEnd(guess) => { 82 | // update keyboard hint 83 | wordle::utils::update_keyboard_hints(&mut model.keyboard_hints, guess.clone()); 84 | 85 | let is_correct_guess = wordle::utils::is_correct_guess(guess.clone()); 86 | let is_attempts_over = model.guesses.len() == 6; 87 | let is_over = is_correct_guess || is_attempts_over; 88 | 89 | if is_over { 90 | model.running_state = RunningState::Over(if is_correct_guess { 91 | GameResult::CorrectGuess 92 | } else { 93 | GameResult::WrongGuess 94 | }); 95 | } else { 96 | model.running_state = RunningState::Waiting; 97 | } 98 | } 99 | 100 | Message::Erase => { 101 | // we will listen only if it is in waiting state 102 | if model.running_state != RunningState::Waiting { 103 | return; 104 | } 105 | 106 | if model.active_guess.len() > 0 { 107 | model.active_guess.pop(); 108 | } 109 | } 110 | Message::Reset => { 111 | // reset only if the game is in over stage 112 | match model.running_state { 113 | RunningState::Over(_) => { 114 | // we will reset only if the game is over 115 | model.reset(); 116 | } 117 | _ => {} 118 | } 119 | } 120 | Message::Quit => { 121 | model.running_state = RunningState::Done; 122 | } 123 | } 124 | } 125 | 126 | pub fn handle_key_event(key_event: KeyEvent) -> Option { 127 | match key_event.code { 128 | // https://ratatui.rs/templates/async/config-rs/ 129 | KeyCode::Char('n') if key_event.modifiers.contains(KeyModifiers::CONTROL) => { 130 | Some(Message::Reset) 131 | } 132 | KeyCode::Char('c') if key_event.modifiers.contains(KeyModifiers::CONTROL) => { 133 | Some(Message::Quit) 134 | } 135 | KeyCode::Esc => Some(Message::Quit), 136 | KeyCode::Char(letter) => { 137 | if letter.is_ascii_alphabetic() { 138 | Some(Message::Listen(letter)) 139 | } else { 140 | None 141 | } 142 | } 143 | KeyCode::Backspace | KeyCode::Delete => Some(Message::Erase), 144 | KeyCode::Enter => Some(Message::CalculateStart), 145 | _code => None, 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/wordle/data.rs: -------------------------------------------------------------------------------- 1 | pub fn valid_guesses() -> Vec { 2 | let file_data = include_str!("./files/guess.txt"); 3 | let words = file_data.split("\n"); 4 | 5 | words.map(|x| x.to_string()).collect() 6 | } 7 | 8 | pub fn valid_wordles() -> Vec { 9 | let file_data = include_str!("./files/answer.txt"); 10 | let words = file_data.split("\n"); 11 | 12 | words.map(|x| x.to_string()).collect() 13 | } 14 | -------------------------------------------------------------------------------- /src/wordle/files/answer.txt: -------------------------------------------------------------------------------- 1 | aback 2 | abase 3 | abate 4 | abbey 5 | abbot 6 | abhor 7 | abide 8 | abled 9 | abode 10 | abort 11 | about 12 | above 13 | abuse 14 | abyss 15 | acorn 16 | acrid 17 | actor 18 | acute 19 | adage 20 | adapt 21 | adept 22 | admin 23 | admit 24 | adobe 25 | adopt 26 | adore 27 | adorn 28 | adult 29 | affix 30 | afire 31 | afoot 32 | afoul 33 | after 34 | again 35 | agape 36 | agate 37 | agent 38 | agile 39 | aging 40 | aglow 41 | agony 42 | agree 43 | ahead 44 | aider 45 | aisle 46 | alarm 47 | album 48 | alert 49 | algae 50 | alibi 51 | alien 52 | align 53 | alike 54 | alive 55 | allay 56 | alley 57 | allot 58 | allow 59 | alloy 60 | aloft 61 | alone 62 | along 63 | aloof 64 | aloud 65 | alpha 66 | altar 67 | alter 68 | amass 69 | amaze 70 | amber 71 | amble 72 | amend 73 | amiss 74 | amity 75 | among 76 | ample 77 | amply 78 | amuse 79 | angel 80 | anger 81 | angle 82 | angry 83 | angst 84 | anime 85 | ankle 86 | annex 87 | annoy 88 | annul 89 | anode 90 | antic 91 | anvil 92 | aorta 93 | apart 94 | aphid 95 | aping 96 | apnea 97 | apple 98 | apply 99 | apron 100 | aptly 101 | arbor 102 | ardor 103 | arena 104 | argue 105 | arise 106 | armor 107 | aroma 108 | arose 109 | array 110 | arrow 111 | arson 112 | artsy 113 | ascot 114 | ashen 115 | aside 116 | askew 117 | assay 118 | asset 119 | atoll 120 | atone 121 | attic 122 | audio 123 | audit 124 | augur 125 | aunty 126 | avail 127 | avert 128 | avian 129 | avoid 130 | await 131 | awake 132 | award 133 | aware 134 | awash 135 | awful 136 | awoke 137 | axial 138 | axiom 139 | axion 140 | azure 141 | bacon 142 | badge 143 | badly 144 | bagel 145 | baggy 146 | baker 147 | baler 148 | balmy 149 | banal 150 | banjo 151 | barge 152 | baron 153 | basal 154 | basic 155 | basil 156 | basin 157 | basis 158 | baste 159 | batch 160 | bathe 161 | baton 162 | batty 163 | bawdy 164 | bayou 165 | beach 166 | beady 167 | beard 168 | beast 169 | beech 170 | beefy 171 | befit 172 | began 173 | begat 174 | beget 175 | begin 176 | begun 177 | being 178 | belch 179 | belie 180 | belle 181 | belly 182 | below 183 | bench 184 | beret 185 | berry 186 | berth 187 | beset 188 | betel 189 | bevel 190 | bezel 191 | bible 192 | bicep 193 | biddy 194 | bigot 195 | bilge 196 | billy 197 | binge 198 | bingo 199 | biome 200 | birch 201 | birth 202 | bison 203 | bitty 204 | black 205 | blade 206 | blame 207 | bland 208 | blank 209 | blare 210 | blast 211 | blaze 212 | bleak 213 | bleat 214 | bleed 215 | bleep 216 | blend 217 | bless 218 | blimp 219 | blind 220 | blink 221 | bliss 222 | blitz 223 | bloat 224 | block 225 | bloke 226 | blond 227 | blood 228 | bloom 229 | blown 230 | bluer 231 | bluff 232 | blunt 233 | blurb 234 | blurt 235 | blush 236 | board 237 | boast 238 | bobby 239 | boney 240 | bongo 241 | bonus 242 | booby 243 | boost 244 | booth 245 | booty 246 | booze 247 | boozy 248 | borax 249 | borne 250 | bosom 251 | bossy 252 | botch 253 | bough 254 | boule 255 | bound 256 | bowel 257 | boxer 258 | brace 259 | braid 260 | brain 261 | brake 262 | brand 263 | brash 264 | brass 265 | brave 266 | bravo 267 | brawl 268 | brawn 269 | bread 270 | break 271 | breed 272 | briar 273 | bribe 274 | brick 275 | bride 276 | brief 277 | brine 278 | bring 279 | brink 280 | briny 281 | brisk 282 | broad 283 | broil 284 | broke 285 | brood 286 | brook 287 | broom 288 | broth 289 | brown 290 | brunt 291 | brush 292 | brute 293 | buddy 294 | budge 295 | buggy 296 | bugle 297 | build 298 | built 299 | bulge 300 | bulky 301 | bully 302 | bunch 303 | bunny 304 | burly 305 | burnt 306 | burst 307 | bused 308 | bushy 309 | butch 310 | butte 311 | buxom 312 | buyer 313 | bylaw 314 | cabal 315 | cabby 316 | cabin 317 | cable 318 | cacao 319 | cache 320 | cacti 321 | caddy 322 | cadet 323 | cagey 324 | cairn 325 | camel 326 | cameo 327 | canal 328 | candy 329 | canny 330 | canoe 331 | canon 332 | caper 333 | caput 334 | carat 335 | cargo 336 | carol 337 | carry 338 | carve 339 | caste 340 | catch 341 | cater 342 | catty 343 | caulk 344 | cause 345 | cavil 346 | cease 347 | cedar 348 | cello 349 | chafe 350 | chaff 351 | chain 352 | chair 353 | chalk 354 | champ 355 | chant 356 | chaos 357 | chard 358 | charm 359 | chart 360 | chase 361 | chasm 362 | cheap 363 | cheat 364 | check 365 | cheek 366 | cheer 367 | chess 368 | chest 369 | chick 370 | chide 371 | chief 372 | child 373 | chili 374 | chill 375 | chime 376 | china 377 | chirp 378 | chock 379 | choir 380 | choke 381 | chord 382 | chore 383 | chose 384 | chuck 385 | chump 386 | chunk 387 | churn 388 | chute 389 | cider 390 | cigar 391 | cinch 392 | circa 393 | civic 394 | civil 395 | clack 396 | claim 397 | clamp 398 | clang 399 | clank 400 | clash 401 | clasp 402 | class 403 | clean 404 | clear 405 | cleat 406 | cleft 407 | clerk 408 | click 409 | cliff 410 | climb 411 | cling 412 | clink 413 | cloak 414 | clock 415 | clone 416 | close 417 | cloth 418 | cloud 419 | clout 420 | clove 421 | clown 422 | cluck 423 | clued 424 | clump 425 | clung 426 | coach 427 | coast 428 | cobra 429 | cocoa 430 | colon 431 | color 432 | comet 433 | comfy 434 | comic 435 | comma 436 | conch 437 | condo 438 | conic 439 | copse 440 | coral 441 | corer 442 | corny 443 | couch 444 | cough 445 | could 446 | count 447 | coupe 448 | court 449 | coven 450 | cover 451 | covet 452 | covey 453 | cower 454 | coyly 455 | crack 456 | craft 457 | cramp 458 | crane 459 | crank 460 | crash 461 | crass 462 | crate 463 | crave 464 | crawl 465 | craze 466 | crazy 467 | creak 468 | cream 469 | credo 470 | creed 471 | creek 472 | creep 473 | creme 474 | crepe 475 | crept 476 | cress 477 | crest 478 | crick 479 | cried 480 | crier 481 | crime 482 | crimp 483 | crisp 484 | croak 485 | crock 486 | crone 487 | crony 488 | crook 489 | cross 490 | croup 491 | crowd 492 | crown 493 | crude 494 | cruel 495 | crumb 496 | crump 497 | crush 498 | crust 499 | crypt 500 | cubic 501 | cumin 502 | curio 503 | curly 504 | curry 505 | curse 506 | curve 507 | curvy 508 | cutie 509 | cyber 510 | cycle 511 | cynic 512 | daddy 513 | daily 514 | dairy 515 | daisy 516 | dally 517 | dance 518 | dandy 519 | datum 520 | daunt 521 | dealt 522 | death 523 | debar 524 | debit 525 | debug 526 | debut 527 | decal 528 | decay 529 | decor 530 | decoy 531 | decry 532 | defer 533 | deign 534 | deity 535 | delay 536 | delta 537 | delve 538 | demon 539 | demur 540 | denim 541 | dense 542 | depot 543 | depth 544 | derby 545 | deter 546 | detox 547 | deuce 548 | devil 549 | diary 550 | dicey 551 | digit 552 | dilly 553 | dimly 554 | diner 555 | dingo 556 | dingy 557 | diode 558 | dirge 559 | dirty 560 | disco 561 | ditch 562 | ditto 563 | ditty 564 | diver 565 | dizzy 566 | dodge 567 | dodgy 568 | dogma 569 | doing 570 | dolly 571 | donor 572 | donut 573 | dopey 574 | doubt 575 | dough 576 | dowdy 577 | dowel 578 | downy 579 | dowry 580 | dozen 581 | draft 582 | drain 583 | drake 584 | drama 585 | drank 586 | drape 587 | drawl 588 | drawn 589 | dread 590 | dream 591 | dress 592 | dried 593 | drier 594 | drift 595 | drill 596 | drink 597 | drive 598 | droit 599 | droll 600 | drone 601 | drool 602 | droop 603 | dross 604 | drove 605 | drown 606 | druid 607 | drunk 608 | dryer 609 | dryly 610 | duchy 611 | dully 612 | dummy 613 | dumpy 614 | dunce 615 | dusky 616 | dusty 617 | dutch 618 | duvet 619 | dwarf 620 | dwell 621 | dwelt 622 | dying 623 | eager 624 | eagle 625 | early 626 | earth 627 | easel 628 | eaten 629 | eater 630 | ebony 631 | eclat 632 | edict 633 | edify 634 | eerie 635 | egret 636 | eight 637 | eject 638 | eking 639 | elate 640 | elbow 641 | elder 642 | elect 643 | elegy 644 | elfin 645 | elide 646 | elite 647 | elope 648 | elude 649 | email 650 | embed 651 | ember 652 | emcee 653 | empty 654 | enact 655 | endow 656 | enema 657 | enemy 658 | enjoy 659 | ennui 660 | ensue 661 | enter 662 | entry 663 | envoy 664 | epoch 665 | epoxy 666 | equal 667 | equip 668 | erase 669 | erect 670 | erode 671 | error 672 | erupt 673 | essay 674 | ester 675 | ether 676 | ethic 677 | ethos 678 | etude 679 | evade 680 | event 681 | every 682 | evict 683 | evoke 684 | exact 685 | exalt 686 | excel 687 | exert 688 | exile 689 | exist 690 | expel 691 | extol 692 | extra 693 | exult 694 | eying 695 | fable 696 | facet 697 | faint 698 | fairy 699 | faith 700 | false 701 | fancy 702 | fanny 703 | farce 704 | fatal 705 | fatty 706 | fault 707 | fauna 708 | favor 709 | feast 710 | fecal 711 | feign 712 | fella 713 | felon 714 | femme 715 | femur 716 | fence 717 | feral 718 | ferry 719 | fetal 720 | fetch 721 | fetid 722 | fetus 723 | fever 724 | fewer 725 | fiber 726 | ficus 727 | field 728 | fiend 729 | fiery 730 | fifth 731 | fifty 732 | fight 733 | filer 734 | filet 735 | filly 736 | filmy 737 | filth 738 | final 739 | finch 740 | finer 741 | first 742 | fishy 743 | fixer 744 | fizzy 745 | fjord 746 | flack 747 | flail 748 | flair 749 | flake 750 | flaky 751 | flame 752 | flank 753 | flare 754 | flash 755 | flask 756 | fleck 757 | fleet 758 | flesh 759 | flick 760 | flier 761 | fling 762 | flint 763 | flirt 764 | float 765 | flock 766 | flood 767 | floor 768 | flora 769 | floss 770 | flour 771 | flout 772 | flown 773 | fluff 774 | fluid 775 | fluke 776 | flume 777 | flung 778 | flunk 779 | flush 780 | flute 781 | flyer 782 | foamy 783 | focal 784 | focus 785 | foggy 786 | foist 787 | folio 788 | folly 789 | foray 790 | force 791 | forge 792 | forgo 793 | forte 794 | forth 795 | forty 796 | forum 797 | found 798 | foyer 799 | frail 800 | frame 801 | frank 802 | fraud 803 | freak 804 | freed 805 | freer 806 | fresh 807 | friar 808 | fried 809 | frill 810 | frisk 811 | fritz 812 | frock 813 | frond 814 | front 815 | frost 816 | froth 817 | frown 818 | froze 819 | fruit 820 | fudge 821 | fugue 822 | fully 823 | fungi 824 | funky 825 | funny 826 | furor 827 | furry 828 | fussy 829 | fuzzy 830 | gaffe 831 | gaily 832 | gamer 833 | gamma 834 | gamut 835 | gassy 836 | gaudy 837 | gauge 838 | gaunt 839 | gauze 840 | gavel 841 | gawky 842 | gayer 843 | gayly 844 | gazer 845 | gecko 846 | geeky 847 | geese 848 | genie 849 | genre 850 | ghost 851 | ghoul 852 | giant 853 | giddy 854 | gipsy 855 | girly 856 | girth 857 | given 858 | giver 859 | glade 860 | gland 861 | glare 862 | glass 863 | glaze 864 | gleam 865 | glean 866 | glide 867 | glint 868 | gloat 869 | globe 870 | gloom 871 | glory 872 | gloss 873 | glove 874 | glyph 875 | gnash 876 | gnome 877 | godly 878 | going 879 | golem 880 | golly 881 | gonad 882 | goner 883 | goody 884 | gooey 885 | goofy 886 | goose 887 | gorge 888 | gouge 889 | gourd 890 | grace 891 | grade 892 | graft 893 | grail 894 | grain 895 | grand 896 | grant 897 | grape 898 | graph 899 | grasp 900 | grass 901 | grate 902 | grave 903 | gravy 904 | graze 905 | great 906 | greed 907 | green 908 | greet 909 | grief 910 | grill 911 | grime 912 | grimy 913 | grind 914 | gripe 915 | groan 916 | groin 917 | groom 918 | grope 919 | gross 920 | group 921 | grout 922 | grove 923 | growl 924 | grown 925 | gruel 926 | gruff 927 | grunt 928 | guard 929 | guava 930 | guess 931 | guest 932 | guide 933 | guild 934 | guile 935 | guilt 936 | guise 937 | gulch 938 | gully 939 | gumbo 940 | gummy 941 | guppy 942 | gusto 943 | gusty 944 | gypsy 945 | habit 946 | hairy 947 | halve 948 | handy 949 | happy 950 | hardy 951 | harem 952 | harpy 953 | harry 954 | harsh 955 | haste 956 | hasty 957 | hatch 958 | hater 959 | haunt 960 | haute 961 | haven 962 | havoc 963 | hazel 964 | heady 965 | heard 966 | heart 967 | heath 968 | heave 969 | heavy 970 | hedge 971 | hefty 972 | heist 973 | helix 974 | hello 975 | hence 976 | heron 977 | hilly 978 | hinge 979 | hippo 980 | hippy 981 | hitch 982 | hoard 983 | hobby 984 | hoist 985 | holly 986 | homer 987 | honey 988 | honor 989 | horde 990 | horny 991 | horse 992 | hotel 993 | hotly 994 | hound 995 | house 996 | hovel 997 | hover 998 | howdy 999 | human 1000 | humid 1001 | humor 1002 | humph 1003 | humus 1004 | hunch 1005 | hunky 1006 | hurry 1007 | husky 1008 | hussy 1009 | hutch 1010 | hydro 1011 | hyena 1012 | hymen 1013 | hyper 1014 | icily 1015 | icing 1016 | ideal 1017 | idiom 1018 | idiot 1019 | idler 1020 | idyll 1021 | igloo 1022 | iliac 1023 | image 1024 | imbue 1025 | impel 1026 | imply 1027 | inane 1028 | inbox 1029 | incur 1030 | index 1031 | inept 1032 | inert 1033 | infer 1034 | ingot 1035 | inlay 1036 | inlet 1037 | inner 1038 | input 1039 | inter 1040 | intro 1041 | ionic 1042 | irate 1043 | irony 1044 | islet 1045 | issue 1046 | itchy 1047 | ivory 1048 | jaunt 1049 | jazzy 1050 | jelly 1051 | jerky 1052 | jetty 1053 | jewel 1054 | jiffy 1055 | joint 1056 | joist 1057 | joker 1058 | jolly 1059 | joust 1060 | judge 1061 | juice 1062 | juicy 1063 | jumbo 1064 | jumpy 1065 | junta 1066 | junto 1067 | juror 1068 | kappa 1069 | karma 1070 | kayak 1071 | kebab 1072 | khaki 1073 | kinky 1074 | kiosk 1075 | kitty 1076 | knack 1077 | knave 1078 | knead 1079 | kneed 1080 | kneel 1081 | knelt 1082 | knife 1083 | knock 1084 | knoll 1085 | known 1086 | koala 1087 | krill 1088 | label 1089 | labor 1090 | laden 1091 | ladle 1092 | lager 1093 | lance 1094 | lanky 1095 | lapel 1096 | lapse 1097 | large 1098 | larva 1099 | lasso 1100 | latch 1101 | later 1102 | lathe 1103 | latte 1104 | laugh 1105 | layer 1106 | leach 1107 | leafy 1108 | leaky 1109 | leant 1110 | leapt 1111 | learn 1112 | lease 1113 | leash 1114 | least 1115 | leave 1116 | ledge 1117 | leech 1118 | leery 1119 | lefty 1120 | legal 1121 | leggy 1122 | lemon 1123 | lemur 1124 | leper 1125 | level 1126 | lever 1127 | libel 1128 | liege 1129 | light 1130 | liken 1131 | lilac 1132 | limbo 1133 | limit 1134 | linen 1135 | liner 1136 | lingo 1137 | lipid 1138 | lithe 1139 | liver 1140 | livid 1141 | llama 1142 | loamy 1143 | loath 1144 | lobby 1145 | local 1146 | locus 1147 | lodge 1148 | lofty 1149 | logic 1150 | login 1151 | loopy 1152 | loose 1153 | lorry 1154 | loser 1155 | louse 1156 | lousy 1157 | lover 1158 | lower 1159 | lowly 1160 | loyal 1161 | lucid 1162 | lucky 1163 | lumen 1164 | lumpy 1165 | lunar 1166 | lunch 1167 | lunge 1168 | lupus 1169 | lurch 1170 | lurid 1171 | lusty 1172 | lying 1173 | lymph 1174 | lyric 1175 | macaw 1176 | macho 1177 | macro 1178 | madam 1179 | madly 1180 | mafia 1181 | magic 1182 | magma 1183 | maize 1184 | major 1185 | maker 1186 | mambo 1187 | mamma 1188 | mammy 1189 | manga 1190 | mange 1191 | mango 1192 | mangy 1193 | mania 1194 | manic 1195 | manly 1196 | manor 1197 | maple 1198 | march 1199 | marry 1200 | marsh 1201 | mason 1202 | masse 1203 | match 1204 | matey 1205 | mauve 1206 | maxim 1207 | maybe 1208 | mayor 1209 | mealy 1210 | meant 1211 | meaty 1212 | mecca 1213 | medal 1214 | media 1215 | medic 1216 | melee 1217 | melon 1218 | mercy 1219 | merge 1220 | merit 1221 | merry 1222 | metal 1223 | meter 1224 | metro 1225 | micro 1226 | midge 1227 | midst 1228 | might 1229 | milky 1230 | mimic 1231 | mince 1232 | miner 1233 | minim 1234 | minor 1235 | minty 1236 | minus 1237 | mirth 1238 | miser 1239 | missy 1240 | mocha 1241 | modal 1242 | model 1243 | modem 1244 | mogul 1245 | moist 1246 | molar 1247 | moldy 1248 | money 1249 | month 1250 | moody 1251 | moose 1252 | moral 1253 | moron 1254 | morph 1255 | mossy 1256 | motel 1257 | motif 1258 | motor 1259 | motto 1260 | moult 1261 | mound 1262 | mount 1263 | mourn 1264 | mouse 1265 | mouth 1266 | mover 1267 | movie 1268 | mower 1269 | mucky 1270 | mucus 1271 | muddy 1272 | mulch 1273 | mummy 1274 | munch 1275 | mural 1276 | murky 1277 | mushy 1278 | music 1279 | musky 1280 | musty 1281 | myrrh 1282 | nadir 1283 | naive 1284 | nanny 1285 | nasal 1286 | nasty 1287 | natal 1288 | naval 1289 | navel 1290 | needy 1291 | neigh 1292 | nerdy 1293 | nerve 1294 | never 1295 | newer 1296 | newly 1297 | nicer 1298 | niche 1299 | niece 1300 | night 1301 | ninja 1302 | ninny 1303 | ninth 1304 | noble 1305 | nobly 1306 | noise 1307 | noisy 1308 | nomad 1309 | noose 1310 | north 1311 | nosey 1312 | notch 1313 | novel 1314 | nudge 1315 | nurse 1316 | nutty 1317 | nylon 1318 | nymph 1319 | oaken 1320 | obese 1321 | occur 1322 | ocean 1323 | octal 1324 | octet 1325 | odder 1326 | oddly 1327 | offal 1328 | offer 1329 | often 1330 | olden 1331 | older 1332 | olive 1333 | ombre 1334 | omega 1335 | onion 1336 | onset 1337 | opera 1338 | opine 1339 | opium 1340 | optic 1341 | orbit 1342 | order 1343 | organ 1344 | other 1345 | otter 1346 | ought 1347 | ounce 1348 | outdo 1349 | outer 1350 | outgo 1351 | ovary 1352 | ovate 1353 | overt 1354 | ovine 1355 | ovoid 1356 | owing 1357 | owner 1358 | oxide 1359 | ozone 1360 | paddy 1361 | pagan 1362 | paint 1363 | paler 1364 | palsy 1365 | panel 1366 | panic 1367 | pansy 1368 | papal 1369 | paper 1370 | parer 1371 | parka 1372 | parry 1373 | parse 1374 | party 1375 | pasta 1376 | paste 1377 | pasty 1378 | patch 1379 | patio 1380 | patsy 1381 | patty 1382 | pause 1383 | payee 1384 | payer 1385 | peace 1386 | peach 1387 | pearl 1388 | pecan 1389 | pedal 1390 | penal 1391 | pence 1392 | penne 1393 | penny 1394 | perch 1395 | peril 1396 | perky 1397 | pesky 1398 | pesto 1399 | petal 1400 | petty 1401 | phase 1402 | phone 1403 | phony 1404 | photo 1405 | piano 1406 | picky 1407 | piece 1408 | piety 1409 | piggy 1410 | pilot 1411 | pinch 1412 | piney 1413 | pinky 1414 | pinto 1415 | piper 1416 | pique 1417 | pitch 1418 | pithy 1419 | pivot 1420 | pixel 1421 | pixie 1422 | pizza 1423 | place 1424 | plaid 1425 | plain 1426 | plait 1427 | plane 1428 | plank 1429 | plant 1430 | plate 1431 | plaza 1432 | plead 1433 | pleat 1434 | plied 1435 | plier 1436 | pluck 1437 | plumb 1438 | plume 1439 | plump 1440 | plunk 1441 | plush 1442 | poesy 1443 | point 1444 | poise 1445 | poker 1446 | polar 1447 | polka 1448 | polyp 1449 | pooch 1450 | poppy 1451 | porch 1452 | poser 1453 | posit 1454 | posse 1455 | pouch 1456 | pound 1457 | pouty 1458 | power 1459 | prank 1460 | prawn 1461 | preen 1462 | press 1463 | price 1464 | prick 1465 | pride 1466 | pried 1467 | prime 1468 | primo 1469 | print 1470 | prior 1471 | prism 1472 | privy 1473 | prize 1474 | probe 1475 | prone 1476 | prong 1477 | proof 1478 | prose 1479 | proud 1480 | prove 1481 | prowl 1482 | proxy 1483 | prude 1484 | prune 1485 | psalm 1486 | pubic 1487 | pudgy 1488 | puffy 1489 | pulpy 1490 | pulse 1491 | punch 1492 | pupil 1493 | puppy 1494 | puree 1495 | purer 1496 | purge 1497 | purse 1498 | pushy 1499 | putty 1500 | pygmy 1501 | quack 1502 | quail 1503 | quake 1504 | qualm 1505 | quark 1506 | quart 1507 | quash 1508 | quasi 1509 | queen 1510 | queer 1511 | quell 1512 | query 1513 | quest 1514 | queue 1515 | quick 1516 | quiet 1517 | quill 1518 | quilt 1519 | quirk 1520 | quite 1521 | quota 1522 | quote 1523 | quoth 1524 | rabbi 1525 | rabid 1526 | racer 1527 | radar 1528 | radii 1529 | radio 1530 | rainy 1531 | raise 1532 | rajah 1533 | rally 1534 | ralph 1535 | ramen 1536 | ranch 1537 | randy 1538 | range 1539 | rapid 1540 | rarer 1541 | raspy 1542 | ratio 1543 | ratty 1544 | raven 1545 | rayon 1546 | razor 1547 | reach 1548 | react 1549 | ready 1550 | realm 1551 | rearm 1552 | rebar 1553 | rebel 1554 | rebus 1555 | rebut 1556 | recap 1557 | recur 1558 | recut 1559 | reedy 1560 | refer 1561 | refit 1562 | regal 1563 | rehab 1564 | reign 1565 | relax 1566 | relay 1567 | relic 1568 | remit 1569 | renal 1570 | renew 1571 | repay 1572 | repel 1573 | reply 1574 | rerun 1575 | reset 1576 | resin 1577 | retch 1578 | retro 1579 | retry 1580 | reuse 1581 | revel 1582 | revue 1583 | rhino 1584 | rhyme 1585 | rider 1586 | ridge 1587 | rifle 1588 | right 1589 | rigid 1590 | rigor 1591 | rinse 1592 | ripen 1593 | riper 1594 | risen 1595 | riser 1596 | risky 1597 | rival 1598 | river 1599 | rivet 1600 | roach 1601 | roast 1602 | robin 1603 | robot 1604 | rocky 1605 | rodeo 1606 | roger 1607 | rogue 1608 | roomy 1609 | roost 1610 | rotor 1611 | rouge 1612 | rough 1613 | round 1614 | rouse 1615 | route 1616 | rover 1617 | rowdy 1618 | rower 1619 | royal 1620 | ruddy 1621 | ruder 1622 | rugby 1623 | ruler 1624 | rumba 1625 | rumor 1626 | rupee 1627 | rural 1628 | rusty 1629 | sadly 1630 | safer 1631 | saint 1632 | salad 1633 | sally 1634 | salon 1635 | salsa 1636 | salty 1637 | salve 1638 | salvo 1639 | sandy 1640 | saner 1641 | sappy 1642 | sassy 1643 | satin 1644 | satyr 1645 | sauce 1646 | saucy 1647 | sauna 1648 | saute 1649 | savor 1650 | savoy 1651 | savvy 1652 | scald 1653 | scale 1654 | scalp 1655 | scaly 1656 | scamp 1657 | scant 1658 | scare 1659 | scarf 1660 | scary 1661 | scene 1662 | scent 1663 | scion 1664 | scoff 1665 | scold 1666 | scone 1667 | scoop 1668 | scope 1669 | score 1670 | scorn 1671 | scour 1672 | scout 1673 | scowl 1674 | scram 1675 | scrap 1676 | scree 1677 | screw 1678 | scrub 1679 | scrum 1680 | scuba 1681 | sedan 1682 | seedy 1683 | segue 1684 | seize 1685 | semen 1686 | sense 1687 | sepia 1688 | serif 1689 | serum 1690 | serve 1691 | setup 1692 | seven 1693 | sever 1694 | sewer 1695 | shack 1696 | shade 1697 | shady 1698 | shaft 1699 | shake 1700 | shaky 1701 | shale 1702 | shall 1703 | shalt 1704 | shame 1705 | shank 1706 | shape 1707 | shard 1708 | share 1709 | shark 1710 | sharp 1711 | shave 1712 | shawl 1713 | shear 1714 | sheen 1715 | sheep 1716 | sheer 1717 | sheet 1718 | sheik 1719 | shelf 1720 | shell 1721 | shied 1722 | shift 1723 | shine 1724 | shiny 1725 | shire 1726 | shirk 1727 | shirt 1728 | shoal 1729 | shock 1730 | shone 1731 | shook 1732 | shoot 1733 | shore 1734 | shorn 1735 | short 1736 | shout 1737 | shove 1738 | shown 1739 | showy 1740 | shrew 1741 | shrub 1742 | shrug 1743 | shuck 1744 | shunt 1745 | shush 1746 | shyly 1747 | siege 1748 | sieve 1749 | sight 1750 | sigma 1751 | silky 1752 | silly 1753 | since 1754 | sinew 1755 | singe 1756 | siren 1757 | sissy 1758 | sixth 1759 | sixty 1760 | skate 1761 | skier 1762 | skiff 1763 | skill 1764 | skimp 1765 | skirt 1766 | skulk 1767 | skull 1768 | skunk 1769 | slack 1770 | slain 1771 | slang 1772 | slant 1773 | slash 1774 | slate 1775 | sleek 1776 | sleep 1777 | sleet 1778 | slept 1779 | slice 1780 | slick 1781 | slide 1782 | slime 1783 | slimy 1784 | sling 1785 | slink 1786 | sloop 1787 | slope 1788 | slosh 1789 | sloth 1790 | slump 1791 | slung 1792 | slunk 1793 | slurp 1794 | slush 1795 | slyly 1796 | smack 1797 | small 1798 | smart 1799 | smash 1800 | smear 1801 | smell 1802 | smelt 1803 | smile 1804 | smirk 1805 | smite 1806 | smith 1807 | smock 1808 | smoke 1809 | smoky 1810 | smote 1811 | snack 1812 | snail 1813 | snake 1814 | snaky 1815 | snare 1816 | snarl 1817 | sneak 1818 | sneer 1819 | snide 1820 | sniff 1821 | snipe 1822 | snoop 1823 | snore 1824 | snort 1825 | snout 1826 | snowy 1827 | snuck 1828 | snuff 1829 | soapy 1830 | sober 1831 | soggy 1832 | solar 1833 | solid 1834 | solve 1835 | sonar 1836 | sonic 1837 | sooth 1838 | sooty 1839 | sorry 1840 | sound 1841 | south 1842 | sower 1843 | space 1844 | spade 1845 | spank 1846 | spare 1847 | spark 1848 | spasm 1849 | spawn 1850 | speak 1851 | spear 1852 | speck 1853 | speed 1854 | spell 1855 | spelt 1856 | spend 1857 | spent 1858 | sperm 1859 | spice 1860 | spicy 1861 | spied 1862 | spiel 1863 | spike 1864 | spiky 1865 | spill 1866 | spilt 1867 | spine 1868 | spiny 1869 | spire 1870 | spite 1871 | splat 1872 | split 1873 | spoil 1874 | spoke 1875 | spoof 1876 | spook 1877 | spool 1878 | spoon 1879 | spore 1880 | sport 1881 | spout 1882 | spray 1883 | spree 1884 | sprig 1885 | spunk 1886 | spurn 1887 | spurt 1888 | squad 1889 | squat 1890 | squib 1891 | stack 1892 | staff 1893 | stage 1894 | staid 1895 | stain 1896 | stair 1897 | stake 1898 | stale 1899 | stalk 1900 | stall 1901 | stamp 1902 | stand 1903 | stank 1904 | stare 1905 | stark 1906 | start 1907 | stash 1908 | state 1909 | stave 1910 | stead 1911 | steak 1912 | steal 1913 | steam 1914 | steed 1915 | steel 1916 | steep 1917 | steer 1918 | stein 1919 | stern 1920 | stick 1921 | stiff 1922 | still 1923 | stilt 1924 | sting 1925 | stink 1926 | stint 1927 | stock 1928 | stoic 1929 | stoke 1930 | stole 1931 | stomp 1932 | stone 1933 | stony 1934 | stood 1935 | stool 1936 | stoop 1937 | store 1938 | stork 1939 | storm 1940 | story 1941 | stout 1942 | stove 1943 | strap 1944 | straw 1945 | stray 1946 | strip 1947 | strut 1948 | stuck 1949 | study 1950 | stuff 1951 | stump 1952 | stung 1953 | stunk 1954 | stunt 1955 | style 1956 | suave 1957 | sugar 1958 | suing 1959 | suite 1960 | sulky 1961 | sully 1962 | sumac 1963 | sunny 1964 | super 1965 | surer 1966 | surge 1967 | surly 1968 | sushi 1969 | swami 1970 | swamp 1971 | swarm 1972 | swash 1973 | swath 1974 | swear 1975 | sweat 1976 | sweep 1977 | sweet 1978 | swell 1979 | swept 1980 | swift 1981 | swill 1982 | swine 1983 | swing 1984 | swirl 1985 | swish 1986 | swoon 1987 | swoop 1988 | sword 1989 | swore 1990 | sworn 1991 | swung 1992 | synod 1993 | syrup 1994 | tabby 1995 | table 1996 | taboo 1997 | tacit 1998 | tacky 1999 | taffy 2000 | taint 2001 | taken 2002 | taker 2003 | tally 2004 | talon 2005 | tamer 2006 | tango 2007 | tangy 2008 | taper 2009 | tapir 2010 | tardy 2011 | tarot 2012 | taste 2013 | tasty 2014 | tatty 2015 | taunt 2016 | tawny 2017 | teach 2018 | teary 2019 | tease 2020 | teddy 2021 | teeth 2022 | tempo 2023 | tenet 2024 | tenor 2025 | tense 2026 | tenth 2027 | tepee 2028 | tepid 2029 | terra 2030 | terse 2031 | testy 2032 | thank 2033 | theft 2034 | their 2035 | theme 2036 | there 2037 | these 2038 | theta 2039 | thick 2040 | thief 2041 | thigh 2042 | thing 2043 | think 2044 | third 2045 | thong 2046 | thorn 2047 | those 2048 | three 2049 | threw 2050 | throb 2051 | throw 2052 | thrum 2053 | thumb 2054 | thump 2055 | thyme 2056 | tiara 2057 | tibia 2058 | tidal 2059 | tiger 2060 | tight 2061 | tilde 2062 | timer 2063 | timid 2064 | tipsy 2065 | titan 2066 | tithe 2067 | title 2068 | toast 2069 | today 2070 | toddy 2071 | token 2072 | tonal 2073 | tonga 2074 | tonic 2075 | tooth 2076 | topaz 2077 | topic 2078 | torch 2079 | torso 2080 | torus 2081 | total 2082 | totem 2083 | touch 2084 | tough 2085 | towel 2086 | tower 2087 | toxic 2088 | toxin 2089 | trace 2090 | track 2091 | tract 2092 | trade 2093 | trail 2094 | train 2095 | trait 2096 | tramp 2097 | trash 2098 | trawl 2099 | tread 2100 | treat 2101 | trend 2102 | triad 2103 | trial 2104 | tribe 2105 | trice 2106 | trick 2107 | tried 2108 | tripe 2109 | trite 2110 | troll 2111 | troop 2112 | trope 2113 | trout 2114 | trove 2115 | truce 2116 | truck 2117 | truer 2118 | truly 2119 | trump 2120 | trunk 2121 | truss 2122 | trust 2123 | truth 2124 | tryst 2125 | tubal 2126 | tuber 2127 | tulip 2128 | tulle 2129 | tumor 2130 | tunic 2131 | turbo 2132 | tutor 2133 | twang 2134 | tweak 2135 | tweed 2136 | tweet 2137 | twice 2138 | twine 2139 | twirl 2140 | twist 2141 | twixt 2142 | tying 2143 | udder 2144 | ulcer 2145 | ultra 2146 | umbra 2147 | uncle 2148 | uncut 2149 | under 2150 | undid 2151 | undue 2152 | unfed 2153 | unfit 2154 | unify 2155 | union 2156 | unite 2157 | unity 2158 | unlit 2159 | unmet 2160 | unset 2161 | untie 2162 | until 2163 | unwed 2164 | unzip 2165 | upper 2166 | upset 2167 | urban 2168 | urine 2169 | usage 2170 | usher 2171 | using 2172 | usual 2173 | usurp 2174 | utile 2175 | utter 2176 | vague 2177 | valet 2178 | valid 2179 | valor 2180 | value 2181 | valve 2182 | vapid 2183 | vapor 2184 | vault 2185 | vaunt 2186 | vegan 2187 | venom 2188 | venue 2189 | verge 2190 | verse 2191 | verso 2192 | verve 2193 | vicar 2194 | video 2195 | vigil 2196 | vigor 2197 | villa 2198 | vinyl 2199 | viola 2200 | viper 2201 | viral 2202 | virus 2203 | visit 2204 | visor 2205 | vista 2206 | vital 2207 | vivid 2208 | vixen 2209 | vocal 2210 | vodka 2211 | vogue 2212 | voice 2213 | voila 2214 | vomit 2215 | voter 2216 | vouch 2217 | vowel 2218 | vying 2219 | wacky 2220 | wafer 2221 | wager 2222 | wagon 2223 | waist 2224 | waive 2225 | waltz 2226 | warty 2227 | waste 2228 | watch 2229 | water 2230 | waver 2231 | waxen 2232 | weary 2233 | weave 2234 | wedge 2235 | weedy 2236 | weigh 2237 | weird 2238 | welch 2239 | welsh 2240 | whack 2241 | whale 2242 | wharf 2243 | wheat 2244 | wheel 2245 | whelp 2246 | where 2247 | which 2248 | whiff 2249 | while 2250 | whine 2251 | whiny 2252 | whirl 2253 | whisk 2254 | white 2255 | whole 2256 | whoop 2257 | whose 2258 | widen 2259 | wider 2260 | widow 2261 | width 2262 | wield 2263 | wight 2264 | willy 2265 | wimpy 2266 | wince 2267 | winch 2268 | windy 2269 | wiser 2270 | wispy 2271 | witch 2272 | witty 2273 | woken 2274 | woman 2275 | women 2276 | woody 2277 | wooer 2278 | wooly 2279 | woozy 2280 | wordy 2281 | world 2282 | worry 2283 | worse 2284 | worst 2285 | worth 2286 | would 2287 | wound 2288 | woven 2289 | wrack 2290 | wrath 2291 | wreak 2292 | wreck 2293 | wrest 2294 | wring 2295 | wrist 2296 | write 2297 | wrong 2298 | wrote 2299 | wrung 2300 | wryly 2301 | yacht 2302 | yearn 2303 | yeast 2304 | yield 2305 | young 2306 | youth 2307 | zebra 2308 | zesty 2309 | zonal -------------------------------------------------------------------------------- /src/wordle/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | pub mod model; 3 | pub mod utils; 4 | -------------------------------------------------------------------------------- /src/wordle/model.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use std::collections::HashMap; 3 | 4 | use crate::wordle; 5 | 6 | pub type KeyboardHints = HashMap; 7 | 8 | #[derive(Debug, Default)] 9 | pub struct Model { 10 | // main wordle word 11 | pub wordle: String, 12 | 13 | // data 14 | pub valid_wordles: Vec, 15 | pub valid_guesses: Vec, 16 | 17 | // user guesses 18 | pub active_guess: String, 19 | pub guesses: Vec>, 20 | 21 | pub running_state: RunningState, 22 | pub keyboard_hints: KeyboardHints, 23 | } 24 | 25 | #[derive(Debug, PartialEq, Eq)] 26 | pub enum GameResult { 27 | CorrectGuess, 28 | WrongGuess, 29 | } 30 | 31 | #[derive(Debug, Default, PartialEq, Eq)] 32 | pub enum RunningState { 33 | #[default] 34 | Waiting, 35 | Calculating, 36 | Over(GameResult), 37 | Done, 38 | } 39 | 40 | #[derive(Clone, Debug, PartialEq)] 41 | pub enum Message { 42 | Listen(char), 43 | Erase, 44 | CalculateStart, 45 | AnimateGuess(usize, Vec), 46 | CalculateEnd(Vec), 47 | Reset, 48 | Quit, 49 | } 50 | 51 | #[derive(Clone, Debug, Default, PartialEq, Eq)] 52 | pub struct LetterStatus { 53 | pub letter: char, 54 | pub status: LetterState, 55 | } 56 | 57 | #[derive(Clone, Debug, Default, PartialEq, Eq)] 58 | pub enum LetterState { 59 | #[default] 60 | Unknown, 61 | NotPresent, 62 | Correct, 63 | Incorrect, 64 | } 65 | 66 | impl Model { 67 | pub fn new() -> Self { 68 | let valid_guesses = wordle::data::valid_guesses(); 69 | let valid_wordles = wordle::data::valid_wordles(); 70 | let wordle = valid_wordles 71 | .choose(&mut rand::thread_rng()) 72 | .unwrap() 73 | .to_string(); 74 | 75 | let default_model = Model::default(); 76 | 77 | Model { 78 | wordle, 79 | valid_guesses, 80 | valid_wordles, 81 | ..default_model 82 | } 83 | } 84 | 85 | pub fn reset(&mut self) { 86 | self.wordle = self 87 | .valid_wordles 88 | .choose(&mut rand::thread_rng()) 89 | .unwrap() 90 | .to_string(); 91 | self.active_guess = "".into(); 92 | self.guesses.clear(); 93 | self.keyboard_hints.clear(); 94 | self.running_state = RunningState::Waiting; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/wordle/utils.rs: -------------------------------------------------------------------------------- 1 | use super::model::{KeyboardHints, LetterState, LetterStatus}; 2 | 3 | // wordle is compared with incoming string and each character is parsed for its correctness. 4 | // There are three cases - correct position, incorrect position, unknown (not present etc) 5 | // CASE 1: Correct position - letter matches exact the position of the wordle 6 | // CASE 2: Incorrect position 7 | // First we have to check if the letter is present anywhere else in the wordle. And an additional 8 | // check has to performed for total number of those letters in the wordle and the guessed word. The 9 | // letter is marked as incorrect if wordle has more or equal number of letters in the guessed word 10 | // CASE 3: unknown - letter is not present in the string at all 11 | pub fn check(wordle: String, guess: String) -> Vec { 12 | let mut output: Vec = Vec::with_capacity(5); 13 | // first things first; let use make sure we have the right length words 14 | if wordle.len() != 5 && guess.len() != 5 { 15 | panic!("5 letter wordle and guess word is needed. Aborting"); 16 | } 17 | 18 | let wordle_letters: Vec = wordle.chars().collect(); 19 | let guess_letters: Vec = guess.chars().collect(); 20 | 21 | for position in 0..5 { 22 | let wordle_letter = wordle_letters[position]; 23 | let guess_letter = guess_letters[position]; 24 | // CASE 1: correct position 25 | if wordle_letter == guess_letter { 26 | output.push(LetterStatus { 27 | letter: guess_letter, 28 | status: LetterState::Correct, 29 | }) 30 | } else { 31 | // CASE 3: guess letter is not present in wordle 32 | if !wordle_letters.contains(&guess_letter) { 33 | output.push(LetterStatus { 34 | letter: guess_letter, 35 | status: LetterState::NotPresent, 36 | }) 37 | } else { 38 | // CASE 2: letter is present but not in the right position 39 | // first let us find character occurences in the wordle 40 | let wordle_indices = get_all_letter_indices(guess_letter, wordle.clone()); 41 | let guess_indices = get_all_letter_indices(guess_letter, guess.clone()); 42 | 43 | let matches = intersection_match(wordle_indices.clone(), guess_indices.clone()); 44 | 45 | let coloring_chances = wordle_indices.len(); 46 | let future_start = guess_indices.partition_point(|&i| i == position); 47 | let future_occurences = guess_indices[future_start..].iter(); 48 | let future_chances = future_occurences.len(); 49 | 50 | // eg: e: ennui [0] <-- where [2,4] 51 | // eg: e: drove [4] <-- evoke [0,4] 52 | // 53 | // should we leave coloring this time? 54 | let match_completed = coloring_chances == matches.len(); 55 | 56 | let should_leave = match_completed || future_chances >= coloring_chances; 57 | if !should_leave { 58 | let is_incorrect = !wordle_indices.contains(&position); 59 | if is_incorrect { 60 | output.push(LetterStatus { 61 | letter: guess_letter, 62 | status: LetterState::Incorrect, 63 | }); 64 | } else { 65 | output.push(LetterStatus { 66 | letter: guess_letter, 67 | status: LetterState::NotPresent, 68 | }); 69 | } 70 | } else { 71 | // we are leaving this letter out as there are matches in the future (word 72 | // occurence) 73 | output.push(LetterStatus { 74 | letter: guess_letter, 75 | status: LetterState::NotPresent, 76 | }); 77 | } 78 | } 79 | } 80 | } 81 | 82 | output 83 | } 84 | 85 | pub fn is_correct_guess(input: Vec) -> bool { 86 | input.iter().all(|x| x.status == LetterState::Correct) 87 | } 88 | 89 | // helper function to update keyboard hints 90 | pub fn update_keyboard_hints<'a>( 91 | hints: &'a mut KeyboardHints, 92 | statuses: Vec, 93 | ) -> &'a mut KeyboardHints { 94 | for status in statuses { 95 | // if the key is not present; just update with the value 96 | if !hints.contains_key(&status.letter) { 97 | hints.insert(status.letter.clone(), status.status.clone()); 98 | } else { 99 | // key is present; based on the status we have to match stuff 100 | match status.status { 101 | LetterState::Incorrect => { 102 | // we will update only if it is not already correct 103 | if let Some(current_status) = hints.get(&status.letter) { 104 | if *current_status != LetterState::Correct { 105 | hints.insert(status.letter.clone(), status.status.clone()); 106 | } 107 | } 108 | } 109 | LetterState::Unknown | LetterState::NotPresent => { 110 | // we will update only if it not in Correct/Incorrect stage 111 | if let Some(current_status) = hints.get(&status.letter) { 112 | if *current_status != LetterState::Correct 113 | && *current_status != LetterState::Incorrect 114 | { 115 | hints.insert(status.letter.clone(), status.status.clone()); 116 | } 117 | } 118 | } 119 | // correct is always correct! 120 | LetterState::Correct => { 121 | hints.insert(status.letter.clone(), LetterState::Correct); 122 | } 123 | } 124 | } 125 | } 126 | 127 | hints 128 | } 129 | 130 | fn get_all_letter_indices(letter: char, word: String) -> Vec { 131 | let mut output: Vec = Vec::new(); 132 | 133 | word.chars() 134 | .enumerate() 135 | .into_iter() 136 | .for_each(|(index, word_letter)| { 137 | if word_letter == letter { 138 | output.push(index); 139 | } 140 | }); 141 | 142 | output 143 | } 144 | 145 | fn intersection_match(wordle: Vec, guess: Vec) -> Vec { 146 | let mut output: Vec = Vec::new(); 147 | 148 | wordle.into_iter().for_each(|i| { 149 | if guess.contains(&i) { 150 | output.push(i.clone()); 151 | } 152 | }); 153 | 154 | output 155 | } 156 | 157 | #[cfg(test)] 158 | mod tests { 159 | use crate::wordle::model::{LetterState, LetterStatus}; 160 | use crate::wordle::utils::*; 161 | use std::collections::HashMap; 162 | 163 | #[test] 164 | fn test_intersection_match() { 165 | let output = intersection_match(vec![0, 2], vec![2]); 166 | assert_eq!(output, vec![2]); 167 | 168 | let output = intersection_match(vec![0, 2], vec![3]); 169 | assert_eq!(output, vec![]); 170 | 171 | let output = intersection_match(vec![1], vec![1, 3]); 172 | assert_eq!(output, vec![1]); 173 | } 174 | 175 | #[test] 176 | fn test_letter_indices() { 177 | let output = get_all_letter_indices('e', "ennui".into()); 178 | assert_eq!(output, vec![0]); 179 | 180 | let output = get_all_letter_indices('e', "where".into()); 181 | assert_eq!(output, vec![2, 4]); 182 | 183 | let output = get_all_letter_indices('e', "drove".into()); 184 | assert_eq!(output, vec![4]); 185 | 186 | let output = get_all_letter_indices('e', "evoke".into()); 187 | assert_eq!(output, vec![0, 4]); 188 | } 189 | 190 | #[test] 191 | fn test_status_random() { 192 | let output = check("ennui".into(), "where".into()); 193 | 194 | let expected: Vec = vec![ 195 | LetterStatus { 196 | letter: 'w', 197 | status: LetterState::NotPresent, 198 | }, 199 | LetterStatus { 200 | letter: 'h', 201 | status: LetterState::NotPresent, 202 | }, 203 | LetterStatus { 204 | letter: 'e', 205 | status: LetterState::NotPresent, 206 | }, 207 | LetterStatus { 208 | letter: 'r', 209 | status: LetterState::NotPresent, 210 | }, 211 | LetterStatus { 212 | letter: 'e', 213 | status: LetterState::Incorrect, 214 | }, 215 | ]; 216 | 217 | assert_eq!(output, expected); 218 | 219 | // test another 220 | let output = check("drove".into(), "evoke".into()); 221 | 222 | let expected: Vec = vec![ 223 | LetterStatus { 224 | letter: 'e', 225 | status: LetterState::NotPresent, 226 | }, 227 | LetterStatus { 228 | letter: 'v', 229 | status: LetterState::Incorrect, 230 | }, 231 | LetterStatus { 232 | letter: 'o', 233 | status: LetterState::Correct, 234 | }, 235 | LetterStatus { 236 | letter: 'k', 237 | status: LetterState::NotPresent, 238 | }, 239 | LetterStatus { 240 | letter: 'e', 241 | status: LetterState::Correct, 242 | }, 243 | ]; 244 | 245 | assert_eq!(output, expected); 246 | 247 | // test another 248 | let output = check("milky".into(), "livid".into()); 249 | 250 | let expected: Vec = vec![ 251 | LetterStatus { 252 | letter: 'l', 253 | status: LetterState::Incorrect, 254 | }, 255 | LetterStatus { 256 | letter: 'i', 257 | status: LetterState::Correct, 258 | }, 259 | LetterStatus { 260 | letter: 'v', 261 | status: LetterState::NotPresent, 262 | }, 263 | LetterStatus { 264 | letter: 'i', 265 | status: LetterState::NotPresent, 266 | }, 267 | LetterStatus { 268 | letter: 'd', 269 | status: LetterState::NotPresent, 270 | }, 271 | ]; 272 | 273 | assert_eq!(output, expected); 274 | } 275 | 276 | #[test] 277 | fn test_letter_status() { 278 | let all_correct_input: Vec = vec![ 279 | LetterStatus { 280 | letter: 'p', 281 | status: LetterState::Correct, 282 | }, 283 | LetterStatus { 284 | letter: 'i', 285 | status: LetterState::Correct, 286 | }, 287 | LetterStatus { 288 | letter: 'o', 289 | status: LetterState::Correct, 290 | }, 291 | LetterStatus { 292 | letter: 'u', 293 | status: LetterState::Correct, 294 | }, 295 | LetterStatus { 296 | letter: 's', 297 | status: LetterState::Correct, 298 | }, 299 | ]; 300 | 301 | assert!(is_correct_guess(all_correct_input)); 302 | 303 | let not_correct_input: Vec = vec![ 304 | LetterStatus { 305 | letter: 'x', 306 | status: LetterState::Unknown, 307 | }, 308 | LetterStatus { 309 | letter: 'i', 310 | status: LetterState::Correct, 311 | }, 312 | LetterStatus { 313 | letter: 'o', 314 | status: LetterState::Correct, 315 | }, 316 | LetterStatus { 317 | letter: 'u', 318 | status: LetterState::Correct, 319 | }, 320 | LetterStatus { 321 | letter: 's', 322 | status: LetterState::Correct, 323 | }, 324 | ]; 325 | 326 | assert_eq!(is_correct_guess(not_correct_input), false); 327 | } 328 | 329 | #[test] 330 | fn test_keyboard_hints() { 331 | // WORDLE - PIOUS 332 | let wordle = String::from("pious"); 333 | let mut hints: KeyboardHints = HashMap::new(); 334 | let statuses: Vec = check(wordle.clone(), "piano".into()); 335 | 336 | update_keyboard_hints(&mut hints, statuses); 337 | 338 | // // all updated values should be correct 339 | assert_eq!(*hints.get(&'p').unwrap(), LetterState::Correct); 340 | assert_eq!(*hints.get(&'i').unwrap(), LetterState::Correct); 341 | assert_eq!(*hints.get(&'a').unwrap(), LetterState::NotPresent); 342 | assert_eq!(*hints.get(&'n').unwrap(), LetterState::NotPresent); 343 | assert_eq!(*hints.get(&'o').unwrap(), LetterState::Incorrect); 344 | 345 | // existing values should not be present 346 | assert_eq!(hints.get(&'x'), None); 347 | 348 | // check new guess word where already correct letter is now in incorrect position; hint 349 | // should show 'correct' because it was correct once 350 | let statuses: Vec = check(wordle.clone(), "smile".into()); 351 | 352 | // update hints again 353 | update_keyboard_hints(&mut hints, statuses); 354 | 355 | // i => already correct, it should still be correct 356 | assert_eq!(*hints.get(&'i').unwrap(), LetterState::Correct); 357 | 358 | // NEW WORDLE 359 | // RESET everything 360 | let wordle = "below"; 361 | hints.clear(); 362 | 363 | // check new wordle 364 | // WORDLE - below; Word - Hello 365 | let statuses: Vec = check(wordle.into(), "hello".into()); 366 | 367 | // update hints again 368 | update_keyboard_hints(&mut hints, statuses); 369 | 370 | // i => already correct, it should still be correct 371 | assert_eq!(*hints.get(&'l').unwrap(), LetterState::Correct); 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /wordl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palerdot/wordl-rs/ebdbd05c52c394feb6bbb90d4665a1284ddc73a5/wordl.gif -------------------------------------------------------------------------------- /wordl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/palerdot/wordl-rs/ebdbd05c52c394feb6bbb90d4665a1284ddc73a5/wordl.png --------------------------------------------------------------------------------