├── .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 |
7 |
8 |
9 | 
10 | 
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 |
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
--------------------------------------------------------------------------------