├── .github
└── workflows
│ └── deploy.yml
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Resources
└── screenshot.png
├── Sources
└── Griddle
│ ├── Data
│ ├── Day.swift
│ ├── GameHistory.swift
│ ├── makeGrid.swift
│ └── wordlist.swift
│ ├── GameManager.swift
│ ├── Views
│ ├── Checkbox.swift
│ ├── GameSummaryView.swift
│ ├── GameView.swift
│ ├── HowToPlay.swift
│ ├── Keyboard.swift
│ ├── ModalView.swift
│ └── Tile.swift
│ ├── checkmark.svg
│ ├── favicon.ico
│ ├── main.swift
│ └── xmark.svg
├── Tests
└── GriddleTests
│ └── GriddleTests.swift
└── index.html
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | jobs:
8 | swiftwasm_deploy:
9 | runs-on: ubuntu-20.04
10 |
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - uses: swiftwasm/swiftwasm-action@v5.6
15 | with:
16 | shell-action: carton bundle --custom-index-page=index.html
17 |
18 | - name: Deploy
19 | uses: peaceiris/actions-gh-pages@v3
20 | with:
21 | github_token: ${{ secrets.GITHUB_TOKEN }}
22 | publish_dir: ./Bundle
23 | cname: griddle.buildpassed.com
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "javascriptkit",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/swiftwasm/JavaScriptKit.git",
7 | "state" : {
8 | "revision" : "2d7bc960eed438dce7355710ece43fa004bbb3ac",
9 | "version" : "0.15.0"
10 | }
11 | },
12 | {
13 | "identity" : "opencombine",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/OpenCombine/OpenCombine.git",
16 | "state" : {
17 | "revision" : "9cf67e363738dbab61b47fb5eaed78d3db31e5ee",
18 | "version" : "0.13.0"
19 | }
20 | },
21 | {
22 | "identity" : "opencombinejs",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/swiftwasm/OpenCombineJS.git",
25 | "state" : {
26 | "revision" : "e574e418ba468ff5c2d4c499eb56f108aeb4d2ba",
27 | "version" : "0.2.0"
28 | }
29 | },
30 | {
31 | "identity" : "swift-argument-parser",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/apple/swift-argument-parser",
34 | "state" : {
35 | "revision" : "df9ee6676cd5b3bf5b330ec7568a5644f547201b",
36 | "version" : "1.1.3"
37 | }
38 | },
39 | {
40 | "identity" : "swift-benchmark",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/google/swift-benchmark",
43 | "state" : {
44 | "revision" : "8163295f6fe82356b0bcf8e1ab991645de17d096",
45 | "version" : "0.1.2"
46 | }
47 | },
48 | {
49 | "identity" : "tokamak",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/TokamakUI/Tokamak",
52 | "state" : {
53 | "branch" : "fiber/view-support",
54 | "revision" : "312a8a7925a527859fb69025f2adc08419110bcd"
55 | }
56 | }
57 | ],
58 | "version" : 2
59 | }
60 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.6
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Griddle",
8 | platforms: [.macOS(.v12), .iOS(.v15)],
9 | products: [
10 | // Products define the executables and libraries a package produces, and make them visible to other packages.
11 | .executable(name: "Griddle", targets: ["Griddle"]),
12 | ],
13 | dependencies: [
14 | // Dependencies declare other packages that this package depends on.
15 | .package(url: "https://github.com/TokamakUI/Tokamak", branch: "fiber/view-support"),
16 | ],
17 | targets: [
18 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
19 | // Targets can depend on other targets in this package, and on products in packages this package depends on.
20 | .executableTarget(
21 | name: "Griddle",
22 | dependencies: [
23 | .product(name: "TokamakShim", package: "Tokamak"),
24 | ],
25 | resources: [
26 | .copy("checkmark.svg"),
27 | .copy("xmark.svg"),
28 | .copy("favicon.ico"),
29 | ],
30 | linkerSettings: [
31 | .unsafeFlags(
32 | ["-Xlinker", "--stack-first", "-Xlinker", "-z", "-Xlinker", "stack-size=16777216"],
33 | .when(platforms: [.wasi])
34 | ),
35 | ]
36 | ),
37 | .testTarget(
38 | name: "GriddleTests",
39 | dependencies: ["Griddle"]),
40 | ]
41 | )
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Griddle](https://griddle.buildpassed.com)
2 |
3 | > Wordle, but in a grid.
4 |
5 |
6 |
7 | This game was built to demonstrate [Tokamak](https://github.com/TokamakUI/Tokamak#fiber-renderers)'s Fiber backend, which can match SwiftUI nearly 1:1 on the web.
8 |
9 | ## Getting Started
10 |
11 | You can play at [griddle.buildpassed.com](https://griddle.buildpassed.com), or you can clone to play locally.
12 |
13 | ### Running with Carton
14 | Install [Carton](https://github.com/swiftwasm/carton):
15 | ```sh
16 | brew install swiftwasm/tap/carton
17 | ```
18 |
19 | Enter this package's root directory and start the site:
20 | ```sh
21 | carton dev
22 | ```
23 |
24 | ## How to Play
25 |
26 | Each row and column forms a word.
27 | Find all six words to win.
28 |
29 | You can make up to 5 guesses per tile.
30 |
31 | Tap on a selector to enter a row/column.
32 | Then you can start typing.
33 |
34 | Letters will change color to indicate various states.
35 |
36 | * Green means the letter is correct. You cannot change a green letter.
37 | * Yellow means the letter goes somewhere else in the grid
38 | * Gray means the letter is not in the word
39 |
40 | Each guess does not need to make a fully valid grid.
41 |
42 | If you make more than 5 guesses in any tile, you lose.
43 |
--------------------------------------------------------------------------------
/Resources/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carson-katri/griddle/209444639e7c7f572ca1315920eaf2e82407ae97/Resources/screenshot.png
--------------------------------------------------------------------------------
/Sources/Griddle/Data/Day.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import JavaScriptKit
3 |
4 | struct Day: Codable, Hashable {
5 | let timestamp: Int
6 |
7 | var grid: [[Character]] {
8 | let grid = makeGrid(seed: timestamp)
9 | return [
10 | grid.0,
11 | grid.1,
12 | grid.2,
13 | ]
14 | }
15 |
16 | /// The game was released 7/4/2022.
17 | ///
18 | /// Used to compute the # of this game when sharing.
19 | static let releaseDate: Self = {
20 | Self(timestamp: 1656892800)
21 | }()
22 |
23 | static let today: Self = {
24 | Self(
25 | timestamp: Int(midnight.valueOf() / 1000)
26 | )
27 | }()
28 |
29 | static let Date = JSObject.global.Date.function!
30 | static let midnight: JSDate = {
31 | var date = JSDate()
32 | return JSDate(millisecondsSinceEpoch: Date.UTC!(date.fullYear, date.month, date.date).number!)
33 | }()
34 | static let next: JSDate = {
35 | var date = JSDate()
36 | date.hours = 0
37 | date.minutes = 0
38 | date.seconds = 0
39 | date.date += 1
40 | return date
41 | }()
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Griddle/Data/GameHistory.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import TokamakShim
3 |
4 | struct GameHistory: Codable, Hashable {
5 | var history: [Day:[[[Character]]]]
6 |
7 | init(history: [Day:[[[Character]]]]) {
8 | self.history = history
9 | }
10 |
11 | static let sharedKey = "history"
12 | static var shared: Self {
13 | get {
14 | guard let data = LocalStorage.standard.read(key: sharedKey)?.data(using: .utf8),
15 | let gameHistory = try? JSONDecoder().decode(GameHistory.self, from: data)
16 | else { return .init(history: [:]) }
17 | return gameHistory
18 | }
19 | set {
20 | guard let data = try? JSONEncoder().encode(newValue),
21 | let json = String(data: data, encoding: .utf8)
22 | else { return }
23 | LocalStorage.standard.store(key: sharedKey, value: json)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/Griddle/Data/makeGrid.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | var generator: SeededRandomNumberGenerator?
4 |
5 | /// Generate a 3x3 grid of valid words.
6 | ///
7 | /// A few conditions must be met for a grid to be valid:
8 | ///
9 | /// * Each row/column must form a valid word. For example,
10 | /// ```
11 | /// S A C
12 | /// A G O
13 | /// T E N
14 | /// ```
15 | /// is a valid grid containing `SAC`, `AGO`, `TEN` as rows, and `SAT`, `AGE`, and `CON` as columns.
16 | ///
17 | /// * No words can repeat. For example,
18 | /// ```
19 | /// S A G
20 | /// A G E
21 | /// T E N
22 | /// ```
23 | /// is not a valid grid (even though each word itself is valid) because `AGE` is the second row and column.
24 | func makeGrid(seed: Int) -> ([Character], [Character], [Character]) {
25 | let rowLength = 3
26 |
27 | if generator == nil {
28 | generator = SeededRandomNumberGenerator(seed: seed)
29 | }
30 |
31 | let shuffledWords = wordlist.shuffled(using: &generator!)
32 |
33 | var possible = [[[Character]]]()
34 |
35 | // First pick the topmost row.
36 | // Example:
37 | // C A T
38 | // _ _ _
39 | // _ _ _
40 | let rowWord = shuffledWords[0]
41 |
42 | // Filter the word list to words that start with any letter in the `rowWord`.
43 | // Example:
44 | // C A T
45 | // A R A
46 | // R T G
47 | // At this point, we don't try to build a valid grid, we just want a list of every word
48 | // that starts with (in this case) C, A, or T.
49 | for col in 0.. S, AWE[0] -> A, TOT[0] -> T.
80 | // SAT is a word, so it passes.
81 | let column = [a[col], b[col], c[col]]
82 | if !wordlist.contains(String(column)) {
83 | return nil
84 | }
85 | // Check if we used a word more than once.
86 | // If so, this is invalid.
87 | if column == a || column == b || column == c || columns.contains(column) {
88 | return nil
89 | }
90 | columns.append(column)
91 | }
92 | // If all checks passed, use this as the grid.
93 | return (a, b, c)
94 | } ?? makeGrid(seed: seed) // If this `rowWord` has no valid grid, try again with a new row word.
95 | }
96 |
97 | /// Naively loops through all combinations of `a`, `b`, and `c`.
98 | private func cartestianProduct(
99 | a: [[Character]],
100 | b: [[Character]],
101 | c: [[Character]],
102 | predicate: ([Character], [Character], [Character]) -> R?
103 | ) -> R? {
104 | for a in a {
105 | for b in b {
106 | for c in c {
107 | if let result = predicate(a, b, c) {
108 | return result
109 | }
110 | }
111 | }
112 | }
113 | return nil
114 | }
115 |
116 | /// A random number generator that accepts a seed.
117 | ///
118 | /// This allows us to generate the same grid for every player each day
119 | /// by using the date as the seed. It also prevents the need to precompute a certain number of grids.
120 | struct SeededRandomNumberGenerator: RandomNumberGenerator {
121 | init(seed: Int) {
122 | srand48(seed)
123 | }
124 |
125 | func next() -> UInt64 {
126 | return withUnsafeBytes(of: drand48()) { bytes in
127 | bytes.load(as: UInt64.self)
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Sources/Griddle/Data/wordlist.swift:
--------------------------------------------------------------------------------
1 | let wordlist = [
2 | "ABS",
3 | "ACE",
4 | "ACT",
5 | "ADD",
6 | "ADO",
7 | "ADS",
8 | "AFT",
9 | "AGE",
10 | "AGO",
11 | "AHA",
12 | "AHI",
13 | "AID",
14 | "AIL",
15 | "AIM",
16 | "AIN",
17 | "AIR",
18 | "AIS",
19 | "ALA",
20 | "ALE",
21 | "ALL",
22 | "ALP",
23 | "ALT",
24 | "AMP",
25 | "AND",
26 | "ANT",
27 | "ANY",
28 | "APE",
29 | "APP",
30 | "APT",
31 | "ARC",
32 | "ARE",
33 | "ARK",
34 | "ARM",
35 | "ART",
36 | "ASH",
37 | "ASK",
38 | "ATE",
39 | "AWE",
40 | "AWL",
41 | "AWN",
42 | "AXE",
43 | "AYE",
44 | "BAD",
45 | "BAG",
46 | "BAM",
47 | "BAN",
48 | "BAR",
49 | "BAT",
50 | "BAY",
51 | "BED",
52 | "BEE",
53 | "BEG",
54 | "BET",
55 | "BEY",
56 | "BIB",
57 | "BID",
58 | "BIG",
59 | "BIN",
60 | "BIO",
61 | "BIT",
62 | "BOA",
63 | "BOB",
64 | "BOD",
65 | "BOG",
66 | "BOO",
67 | "BOP",
68 | "BOT",
69 | "BOW",
70 | "BOX",
71 | "BOY",
72 | "BRA",
73 | "BRO",
74 | "BUB",
75 | "BUD",
76 | "BUG",
77 | "BUM",
78 | "BUN",
79 | "BUR",
80 | "BUS",
81 | "BUT",
82 | "BUY",
83 | "BYE",
84 | "BYS",
85 | "CAB",
86 | "CAD",
87 | "CAM",
88 | "CAN",
89 | "CAP",
90 | "CAR",
91 | "CAT",
92 | "CAW",
93 | "CAY",
94 | "CHI",
95 | "COB",
96 | "COD",
97 | "COG",
98 | "CON",
99 | "COO",
100 | "COP",
101 | "COT",
102 | "COW",
103 | "COX",
104 | "COY",
105 | "CRY",
106 | "CUB",
107 | "CUE",
108 | "CUP",
109 | "CUT",
110 | "DAB",
111 | "DAD",
112 | "DAM",
113 | "DAN",
114 | "DAY",
115 | "DEN",
116 | "DEV",
117 | "DEW",
118 | "DEX",
119 | "DIB",
120 | "DID",
121 | "DIE",
122 | "DIG",
123 | "DIM",
124 | "DIN",
125 | "DIP",
126 | "DOC",
127 | "DOE",
128 | "DOG",
129 | "DON",
130 | "DOS",
131 | "DOT",
132 | "DRY",
133 | "DUB",
134 | "DUD",
135 | "DUE",
136 | "DUG",
137 | "DUH",
138 | "DUO",
139 | "DYE",
140 | "EAR",
141 | "EAT",
142 | "EBB",
143 | "EEK",
144 | "EEL",
145 | "EGG",
146 | "EGO",
147 | "EKE",
148 | "ELF",
149 | "ELK",
150 | "ELM",
151 | "EMU",
152 | "END",
153 | "EON",
154 | "ERA",
155 | "ERR",
156 | "EVE",
157 | "EWE",
158 | "EYE",
159 | "FAB",
160 | "FAD",
161 | "FAN",
162 | "FAR",
163 | "FAT",
164 | "FAX",
165 | "FED",
166 | "FEE",
167 | "FEW",
168 | "FEZ",
169 | "FIB",
170 | "FIG",
171 | "FIN",
172 | "FIR",
173 | "FIT",
174 | "FIX",
175 | "FLU",
176 | "FLY",
177 | "FOB",
178 | "FOE",
179 | "FOG",
180 | "FOR",
181 | "FOX",
182 | "FRO",
183 | "FRY",
184 | "FUN",
185 | "FUR",
186 | "GAB",
187 | "GAD",
188 | "GAG",
189 | "GAL",
190 | "GAM",
191 | "GAP",
192 | "GAS",
193 | "GAY",
194 | "GEE",
195 | "GEL",
196 | "GEM",
197 | "GEN",
198 | "GET",
199 | "GIB",
200 | "GIG",
201 | "GIN",
202 | "GNU",
203 | "GOB",
204 | "GOD",
205 | "GOO",
206 | "GOT",
207 | "GUM",
208 | "GUN",
209 | "GUT",
210 | "GUY",
211 | "GYM",
212 | "GYP",
213 | "HAD",
214 | "HAG",
215 | "HAM",
216 | "HAP",
217 | "HAS",
218 | "HAT",
219 | "HAW",
220 | "HAY",
221 | "HEM",
222 | "HEN",
223 | "HER",
224 | "HEW",
225 | "HEX",
226 | "HEY",
227 | "HID",
228 | "HIM",
229 | "HIP",
230 | "HIS",
231 | "HIT",
232 | "HOB",
233 | "HOE",
234 | "HOG",
235 | "HON",
236 | "HOP",
237 | "HOT",
238 | "HOW",
239 | "HUB",
240 | "HUE",
241 | "HUG",
242 | "HUM",
243 | "HUN",
244 | "HUT",
245 | "ICE",
246 | "ICK",
247 | "ICY",
248 | "IFS",
249 | "IGG",
250 | "ILK",
251 | "ILL",
252 | "IMP",
253 | "INK",
254 | "INN",
255 | "INS",
256 | "ION",
257 | "IRE",
258 | "IRK",
259 | "ITS",
260 | "IVY",
261 | "JAB",
262 | "JAG",
263 | "JAM",
264 | "JAR",
265 | "JAW",
266 | "JAY",
267 | "JET",
268 | "JIB",
269 | "JIG",
270 | "JOB",
271 | "JOE",
272 | "JOG",
273 | "JOT",
274 | "JOY",
275 | "JUG",
276 | "JUT",
277 | "KEG",
278 | "KEY",
279 | "KID",
280 | "KIN",
281 | "KIP",
282 | "KIT",
283 | "KOI",
284 | "LAB",
285 | "LAD",
286 | "LAG",
287 | "LAM",
288 | "LAP",
289 | "LAT",
290 | "LAV",
291 | "LAW",
292 | "LAX",
293 | "LAY",
294 | "LED",
295 | "LEE",
296 | "LEG",
297 | "LEI",
298 | "LET",
299 | "LEU",
300 | "LEX",
301 | "LID",
302 | "LIE",
303 | "LIP",
304 | "LIT",
305 | "LOB",
306 | "LOG",
307 | "LOO",
308 | "LOP",
309 | "LOT",
310 | "LOW",
311 | "LOX",
312 | "LUG",
313 | "LUX",
314 | "LYE",
315 | "MAC",
316 | "MAD",
317 | "MAG",
318 | "MAN",
319 | "MAP",
320 | "MAT",
321 | "MAW",
322 | "MAX",
323 | "MAY",
324 | "MED",
325 | "MEN",
326 | "MET",
327 | "MEW",
328 | "MIC",
329 | "MID",
330 | "MIX",
331 | "MOB",
332 | "MOL",
333 | "MOM",
334 | "MOO",
335 | "MOP",
336 | "MOW",
337 | "MUD",
338 | "MUG",
339 | "MUM",
340 | "NAB",
341 | "NAG",
342 | "NAN",
343 | "NAP",
344 | "NAW",
345 | "NAY",
346 | "NET",
347 | "NEW",
348 | "NIL",
349 | "NIP",
350 | "NIT",
351 | "NIX",
352 | "NOD",
353 | "NOG",
354 | "NOR",
355 | "NOT",
356 | "NOW",
357 | "NUB",
358 | "NUN",
359 | "NUT",
360 | "OAF",
361 | "OAK",
362 | "OAR",
363 | "OAT",
364 | "ODD",
365 | "ODE",
366 | "OFF",
367 | "OFT",
368 | "OHM",
369 | "OIL",
370 | "OLD",
371 | "OLE",
372 | "ONE",
373 | "OPS",
374 | "OPT",
375 | "ORB",
376 | "ORC",
377 | "ORE",
378 | "OUR",
379 | "OUT",
380 | "OWE",
381 | "OWL",
382 | "OWN",
383 | "OXY",
384 | "PAD",
385 | "PAL",
386 | "PAN",
387 | "PAR",
388 | "PAT",
389 | "PAW",
390 | "PAY",
391 | "PEA",
392 | "PEC",
393 | "PEE",
394 | "PEG",
395 | "PEN",
396 | "PEP",
397 | "PER",
398 | "PET",
399 | "PEW",
400 | "PIC",
401 | "PIE",
402 | "PIG",
403 | "PIN",
404 | "PIP",
405 | "PIT",
406 | "PLY",
407 | "POD",
408 | "POP",
409 | "POT",
410 | "POW",
411 | "POX",
412 | "PRO",
413 | "PRY",
414 | "PUB",
415 | "PUG",
416 | "PUN",
417 | "PUP",
418 | "PUR",
419 | "PUS",
420 | "PUT",
421 | "RAD",
422 | "RAG",
423 | "RAM",
424 | "RAN",
425 | "RAP",
426 | "RAT",
427 | "RAW",
428 | "RAY",
429 | "RED",
430 | "REF",
431 | "REP",
432 | "REV",
433 | "RIB",
434 | "RID",
435 | "RIG",
436 | "RIM",
437 | "RIP",
438 | "ROB",
439 | "ROD",
440 | "ROT",
441 | "ROW",
442 | "RUB",
443 | "RUG",
444 | "RUM",
445 | "RUN",
446 | "RUT",
447 | "RYE",
448 | "SAC",
449 | "SAD",
450 | "SAG",
451 | "SAP",
452 | "SAT",
453 | "SAW",
454 | "SAX",
455 | "SAY",
456 | "SEA",
457 | "SEC",
458 | "SEE",
459 | "SET",
460 | "SEW",
461 | "SHE",
462 | "SHY",
463 | "SIM",
464 | "SIN",
465 | "SIP",
466 | "SIR",
467 | "SIS",
468 | "SIT",
469 | "SIX",
470 | "SKI",
471 | "SKY",
472 | "SLY",
473 | "SOB",
474 | "SOD",
475 | "SON",
476 | "SOP",
477 | "SOW",
478 | "SOY",
479 | "SPA",
480 | "SPY",
481 | "STY",
482 | "SUB",
483 | "SUE",
484 | "SUM",
485 | "SUN",
486 | "TAB",
487 | "TAD",
488 | "TAG",
489 | "TAN",
490 | "TAP",
491 | "TAR",
492 | "TAT",
493 | "TAX",
494 | "TEA",
495 | "TEE",
496 | "TEN",
497 | "THE",
498 | "THY",
499 | "TIC",
500 | "TIE",
501 | "TIN",
502 | "TIP",
503 | "TOE",
504 | "TON",
505 | "TOO",
506 | "TOP",
507 | "TOT",
508 | "TOW",
509 | "TOY",
510 | "TRY",
511 | "TUB",
512 | "TUG",
513 | "TUX",
514 | "TWO",
515 | "URN",
516 | "USE",
517 | "VAC",
518 | "VAN",
519 | "VAT",
520 | "VET",
521 | "VEX",
522 | "VIA",
523 | "VIM",
524 | "VOW",
525 | "VOX",
526 | "WAD",
527 | "WAG",
528 | "WAR",
529 | "WAS",
530 | "WAX",
531 | "WAY",
532 | "WEB",
533 | "WED",
534 | "WEE",
535 | "WET",
536 | "WHO",
537 | "WHY",
538 | "WIG",
539 | "WIN",
540 | "WIT",
541 | "WIZ",
542 | "WOE",
543 | "WON",
544 | "WOW",
545 | "WRY",
546 | "YAK",
547 | "YAM",
548 | "YAP",
549 | "YAW",
550 | "YAY",
551 | "YES",
552 | "YET",
553 | "YIP",
554 | "YOU",
555 | "YUM",
556 | "ZAG",
557 | "ZAP",
558 | "ZIG",
559 | "ZIP",
560 | "ZIT",
561 | "ZOO",
562 | ]
563 |
564 | /*
565 | let wordlist = [
566 | "aahs",
567 | "abbe",
568 | "abbr",
569 | "abed",
570 | "abet",
571 | "able",
572 | "ably",
573 | "abut",
574 | "acct",
575 | "aced",
576 | "aces",
577 | "ache",
578 | "achy",
579 | "acid",
580 | "aclu",
581 | "acme",
582 | "acne",
583 | "acre",
584 | "acts",
585 | "adam",
586 | "adds",
587 | "adit",
588 | "ados",
589 | "advt",
590 | "aeon",
591 | "aery",
592 | "afar",
593 | "afro",
594 | "agar",
595 | "aged",
596 | "ages",
597 | "agha",
598 | "agin",
599 | "agog",
600 | "ague",
601 | "ahem",
602 | "ahoy",
603 | "aide",
604 | "aids",
605 | "ails",
606 | "aims",
607 | "airs",
608 | "airy",
609 | "ajar",
610 | "akin",
611 | "alai",
612 | "alan",
613 | "alar",
614 | "alas",
615 | "alba",
616 | "albs",
617 | "alee",
618 | "ales",
619 | "alfa",
620 | "alga",
621 | "alii",
622 | "alit",
623 | "alls",
624 | "ally",
625 | "alma",
626 | "alms",
627 | "aloe",
628 | "alps",
629 | "also",
630 | "alto",
631 | "alum",
632 | "amah",
633 | "amen",
634 | "amex",
635 | "amid",
636 | "amis",
637 | "ammo",
638 | "amok",
639 | "amps",
640 | "amyl",
641 | "anal",
642 | "ands",
643 | "anew",
644 | "anis",
645 | "ankh",
646 | "anna",
647 | "anne",
648 | "anno",
649 | "anon",
650 | "ansi",
651 | "ante",
652 | "anti",
653 | "ants",
654 | "anus",
655 | "aped",
656 | "aper",
657 | "apes",
658 | "apex",
659 | "apse",
660 | "aqua",
661 | "arab",
662 | "arch",
663 | "arco",
664 | "arcs",
665 | "area",
666 | "ares",
667 | "aria",
668 | "arid",
669 | "arks",
670 | "arms",
671 | "army",
672 | "arse",
673 | "arts",
674 | "arty",
675 | "arum",
676 | "asap",
677 | "asea",
678 | "ashy",
679 | "asia",
680 | "asks",
681 | "asps",
682 | "assn",
683 | "asst",
684 | "atma",
685 | "atom",
686 | "atop",
687 | "attn",
688 | "atty",
689 | "auks",
690 | "auld",
691 | "aunt",
692 | "aura",
693 | "auto",
694 | "avdp",
695 | "aver",
696 | "aves",
697 | "avid",
698 | "avis",
699 | "avow",
700 | "away",
701 | "awed",
702 | "awes",
703 | "awls",
704 | "awns",
705 | "awol",
706 | "awry",
707 | "axed",
708 | "axel",
709 | "axes",
710 | "axil",
711 | "axis",
712 | "axle",
713 | "axon",
714 | "ayah",
715 | "ayes",
716 | "baal",
717 | "baas",
718 | "baba",
719 | "babe",
720 | "babu",
721 | "baby",
722 | "bach",
723 | "back",
724 | "bade",
725 | "bads",
726 | "bags",
727 | "baht",
728 | "bail",
729 | "bait",
730 | "bake",
731 | "bald",
732 | "bale",
733 | "bali",
734 | "balk",
735 | "ball",
736 | "balm",
737 | "band",
738 | "bane",
739 | "bang",
740 | "bank",
741 | "bans",
742 | "barb",
743 | "bard",
744 | "bare",
745 | "barf",
746 | "bark",
747 | "barn",
748 | "bars",
749 | "base",
750 | "bash",
751 | "bask",
752 | "bass",
753 | "bast",
754 | "bate",
755 | "bath",
756 | "bats",
757 | "baud",
758 | "bawd",
759 | "bawl",
760 | "bays",
761 | "bdrm",
762 | "bead",
763 | "beak",
764 | "beam",
765 | "bean",
766 | "bear",
767 | "beat",
768 | "beau",
769 | "beck",
770 | "beds",
771 | "beef",
772 | "been",
773 | "beep",
774 | "beer",
775 | "bees",
776 | "beet",
777 | "begs",
778 | "bell",
779 | "belt",
780 | "bema",
781 | "bend",
782 | "bene",
783 | "bens",
784 | "bent",
785 | "berg",
786 | "berm",
787 | "bess",
788 | "best",
789 | "beta",
790 | "bete",
791 | "bets",
792 | "bevy",
793 | "beys",
794 | "bias",
795 | "bibs",
796 | "bide",
797 | "bids",
798 | "bier",
799 | "biff",
800 | "bike",
801 | "bile",
802 | "bilk",
803 | "bill",
804 | "bind",
805 | "bins",
806 | "biol",
807 | "bios",
808 | "bird",
809 | "bite",
810 | "bits",
811 | "blab",
812 | "blah",
813 | "blat",
814 | "bldg",
815 | "bled",
816 | "blew",
817 | "blip",
818 | "blob",
819 | "bloc",
820 | "blot",
821 | "blow",
822 | "blue",
823 | "blur",
824 | "blvd",
825 | "boar",
826 | "boas",
827 | "boat",
828 | "bobs",
829 | "boca",
830 | "bock",
831 | "bode",
832 | "bods",
833 | "body",
834 | "boer",
835 | "boff",
836 | "bogs",
837 | "bogy",
838 | "boil",
839 | "bola",
840 | "bold",
841 | "bole",
842 | "boll",
843 | "bolo",
844 | "bolt",
845 | "bomb",
846 | "bona",
847 | "bond",
848 | "bone",
849 | "bong",
850 | "bono",
851 | "bons",
852 | "bony",
853 | "boob",
854 | "book",
855 | "boom",
856 | "boon",
857 | "boor",
858 | "boos",
859 | "boot",
860 | "bops",
861 | "bore",
862 | "born",
863 | "bort",
864 | "bosh",
865 | "boss",
866 | "both",
867 | "bout",
868 | "bowl",
869 | "bows",
870 | "boxy",
871 | "boyo",
872 | "boys",
873 | "bozo",
874 | "brad",
875 | "brae",
876 | "brag",
877 | "bran",
878 | "bras",
879 | "brat",
880 | "braw",
881 | "bray",
882 | "bred",
883 | "brew",
884 | "brie",
885 | "brig",
886 | "brim",
887 | "brin",
888 | "brio",
889 | "brit",
890 | "bros",
891 | "brow",
892 | "brut",
893 | "bubo",
894 | "bubs",
895 | "buck",
896 | "buds",
897 | "buff",
898 | "bugs",
899 | "bulb",
900 | "bulk",
901 | "bull",
902 | "bump",
903 | "bums",
904 | "bund",
905 | "bung",
906 | "bunk",
907 | "bunn",
908 | "buns",
909 | "bunt",
910 | "buoy",
911 | "burg",
912 | "burl",
913 | "burn",
914 | "burp",
915 | "burr",
916 | "burs",
917 | "bury",
918 | "bush",
919 | "buss",
920 | "bust",
921 | "busy",
922 | "buts",
923 | "butt",
924 | "buys",
925 | "buzz",
926 | "byes",
927 | "byre",
928 | "byte",
929 | "cabs",
930 | "cads",
931 | "cafe",
932 | "cage",
933 | "cagy",
934 | "cake",
935 | "caky",
936 | "calc",
937 | "calf",
938 | "calk",
939 | "call",
940 | "calm",
941 | "calx",
942 | "came",
943 | "camp",
944 | "cams",
945 | "cane",
946 | "cans",
947 | "cant",
948 | "cape",
949 | "caps",
950 | "card",
951 | "care",
952 | "carl",
953 | "carp",
954 | "cars",
955 | "cart",
956 | "casa",
957 | "case",
958 | "cash",
959 | "cask",
960 | "cast",
961 | "cats",
962 | "caul",
963 | "cave",
964 | "cavy",
965 | "caws",
966 | "cays",
967 | "ceca",
968 | "cede",
969 | "cees",
970 | "ceil",
971 | "cell",
972 | "celt",
973 | "cent",
974 | "cert",
975 | "cess",
976 | "chad",
977 | "cham",
978 | "chap",
979 | "char",
980 | "chat",
981 | "chaw",
982 | "chef",
983 | "chem",
984 | "chew",
985 | "chez",
986 | "chia",
987 | "chic",
988 | "chid",
989 | "chin",
990 | "chip",
991 | "chit",
992 | "chop",
993 | "chou",
994 | "chow",
995 | "chub",
996 | "chug",
997 | "chum",
998 | "ciao",
999 | "cine",
1000 | "circ",
1001 | "cite",
1002 | "city",
1003 | "clad",
1004 | "clam",
1005 | "clan",
1006 | "clap",
1007 | "claw",
1008 | "clay",
1009 | "clef",
1010 | "clew",
1011 | "clip",
1012 | "clod",
1013 | "clog",
1014 | "clop",
1015 | "clot",
1016 | "cloy",
1017 | "club",
1018 | "clue",
1019 | "cmdg",
1020 | "coal",
1021 | "coat",
1022 | "coax",
1023 | "cobs",
1024 | "cock",
1025 | "coco",
1026 | "coda",
1027 | "code",
1028 | "cods",
1029 | "coed",
1030 | "cogs",
1031 | "coho",
1032 | "coif",
1033 | "coil",
1034 | "coin",
1035 | "coir",
1036 | "coke",
1037 | "cola",
1038 | "cold",
1039 | "cole",
1040 | "coll",
1041 | "colt",
1042 | "coma",
1043 | "comb",
1044 | "come",
1045 | "comp",
1046 | "cone",
1047 | "conf",
1048 | "conj",
1049 | "conk",
1050 | "conn",
1051 | "cons",
1052 | "cont",
1053 | "cony",
1054 | "cook",
1055 | "cool",
1056 | "coon",
1057 | "coop",
1058 | "coos",
1059 | "coot",
1060 | "cope",
1061 | "cops",
1062 | "copy",
1063 | "cord",
1064 | "core",
1065 | "cork",
1066 | "corm",
1067 | "corn",
1068 | "corp",
1069 | "cosh",
1070 | "cost",
1071 | "cosy",
1072 | "cote",
1073 | "cots",
1074 | "coup",
1075 | "cove",
1076 | "cowl",
1077 | "cows",
1078 | "cozy",
1079 | "crab",
1080 | "crag",
1081 | "cram",
1082 | "crap",
1083 | "craw",
1084 | "cree",
1085 | "crew",
1086 | "crib",
1087 | "crop",
1088 | "crow",
1089 | "crud",
1090 | "crux",
1091 | "ctrl",
1092 | "cuba",
1093 | "cube",
1094 | "cubs",
1095 | "cuds",
1096 | "cued",
1097 | "cues",
1098 | "cuff",
1099 | "cuke",
1100 | "cull",
1101 | "cult",
1102 | "cunt",
1103 | "cups",
1104 | "curb",
1105 | "curd",
1106 | "cure",
1107 | "curl",
1108 | "curs",
1109 | "curt",
1110 | "cusp",
1111 | "cuss",
1112 | "cute",
1113 | "cuts",
1114 | "cyan",
1115 | "cyme",
1116 | "cyst",
1117 | "czar",
1118 | "dabs",
1119 | "dace",
1120 | "dada",
1121 | "dado",
1122 | "dads",
1123 | "daft",
1124 | "dago",
1125 | "dais",
1126 | "dale",
1127 | "dame",
1128 | "damn",
1129 | "damp",
1130 | "dams",
1131 | "dana",
1132 | "dane",
1133 | "dang",
1134 | "dank",
1135 | "dare",
1136 | "dark",
1137 | "darn",
1138 | "dart",
1139 | "dash",
1140 | "data",
1141 | "date",
1142 | "daub",
1143 | "dave",
1144 | "dawn",
1145 | "days",
1146 | "daze",
1147 | "dbms",
1148 | "dead",
1149 | "deaf",
1150 | "deal",
1151 | "dean",
1152 | "dear",
1153 | "debs",
1154 | "debt",
1155 | "deck",
1156 | "deco",
1157 | "deed",
1158 | "deem",
1159 | "deep",
1160 | "deer",
1161 | "dees",
1162 | "deft",
1163 | "defy",
1164 | "deja",
1165 | "dele",
1166 | "deli",
1167 | "dell",
1168 | "demo",
1169 | "dens",
1170 | "dent",
1171 | "deny",
1172 | "dept",
1173 | "derm",
1174 | "desk",
1175 | "deus",
1176 | "deux",
1177 | "deva",
1178 | "dews",
1179 | "dewy",
1180 | "dhow",
1181 | "diag",
1182 | "dial",
1183 | "diam",
1184 | "dias",
1185 | "dibs",
1186 | "dice",
1187 | "dick",
1188 | "dict",
1189 | "dido",
1190 | "didy",
1191 | "died",
1192 | "diem",
1193 | "dies",
1194 | "diet",
1195 | "digs",
1196 | "dike",
1197 | "dill",
1198 | "dime",
1199 | "dims",
1200 | "dine",
1201 | "ding",
1202 | "dins",
1203 | "dint",
1204 | "dips",
1205 | "dipt",
1206 | "dire",
1207 | "dirk",
1208 | "dirt",
1209 | "disc",
1210 | "dish",
1211 | "disk",
1212 | "diva",
1213 | "dive",
1214 | "djin",
1215 | "dock",
1216 | "docs",
1217 | "dodo",
1218 | "doer",
1219 | "does",
1220 | "doff",
1221 | "doge",
1222 | "dogs",
1223 | "dogy",
1224 | "dojo",
1225 | "dole",
1226 | "doll",
1227 | "dolt",
1228 | "dome",
1229 | "doms",
1230 | "dona",
1231 | "done",
1232 | "dong",
1233 | "dons",
1234 | "doom",
1235 | "door",
1236 | "dope",
1237 | "dopy",
1238 | "dorm",
1239 | "dorp",
1240 | "dors",
1241 | "dory",
1242 | "dose",
1243 | "doss",
1244 | "dost",
1245 | "dote",
1246 | "doth",
1247 | "dots",
1248 | "doty",
1249 | "dour",
1250 | "dove",
1251 | "down",
1252 | "dows",
1253 | "doxy",
1254 | "doze",
1255 | "dozy",
1256 | "drab",
1257 | "drag",
1258 | "dram",
1259 | "drat",
1260 | "draw",
1261 | "dray",
1262 | "dreg",
1263 | "drek",
1264 | "drew",
1265 | "drib",
1266 | "drip",
1267 | "drop",
1268 | "drub",
1269 | "drug",
1270 | "drum",
1271 | "drys",
1272 | "duad",
1273 | "dual",
1274 | "dubs",
1275 | "duce",
1276 | "duck",
1277 | "duct",
1278 | "dude",
1279 | "duds",
1280 | "duel",
1281 | "dues",
1282 | "duet",
1283 | "duff",
1284 | "dugs",
1285 | "duke",
1286 | "dull",
1287 | "duly",
1288 | "dumb",
1289 | "dump",
1290 | "dune",
1291 | "dung",
1292 | "dunk",
1293 | "duns",
1294 | "duos",
1295 | "dupe",
1296 | "durn",
1297 | "dusk",
1298 | "dust",
1299 | "duty",
1300 | "dyad",
1301 | "dyed",
1302 | "dyer",
1303 | "dyes",
1304 | "dyke",
1305 | "dyne",
1306 | "each",
1307 | "earl",
1308 | "earn",
1309 | "ears",
1310 | "ease",
1311 | "east",
1312 | "easy",
1313 | "eats",
1314 | "eaux",
1315 | "eave",
1316 | "ebbs",
1317 | "ebon",
1318 | "eccl",
1319 | "echo",
1320 | "ecol",
1321 | "econ",
1322 | "ecru",
1323 | "ecus",
1324 | "edam",
1325 | "edda",
1326 | "eddy",
1327 | "eden",
1328 | "edge",
1329 | "edgy",
1330 | "edit",
1331 | "eels",
1332 | "eely",
1333 | "eery",
1334 | "effs",
1335 | "efts",
1336 | "egad",
1337 | "eggs",
1338 | "egis",
1339 | "egos",
1340 | "eire",
1341 | "eked",
1342 | "ekes",
1343 | "elan",
1344 | "elds",
1345 | "elhi",
1346 | "elks",
1347 | "ells",
1348 | "elms",
1349 | "elmy",
1350 | "else",
1351 | "emir",
1352 | "emit",
1353 | "emmy",
1354 | "emus",
1355 | "encl",
1356 | "ends",
1357 | "engr",
1358 | "enow",
1359 | "envy",
1360 | "eons",
1361 | "epee",
1362 | "epic",
1363 | "eras",
1364 | "erat",
1365 | "ergo",
1366 | "ergs",
1367 | "erie",
1368 | "erin",
1369 | "erne",
1370 | "erns",
1371 | "eros",
1372 | "errs",
1373 | "erst",
1374 | "eses",
1375 | "espy",
1376 | "esse",
1377 | "etch",
1378 | "etna",
1379 | "etym",
1380 | "even",
1381 | "ever",
1382 | "eves",
1383 | "evil",
1384 | "ewer",
1385 | "ewes",
1386 | "exam",
1387 | "exec",
1388 | "exes",
1389 | "exit",
1390 | "expo",
1391 | "eyed",
1392 | "eyer",
1393 | "eyes",
1394 | "face",
1395 | "fact",
1396 | "fade",
1397 | "fads",
1398 | "fags",
1399 | "fail",
1400 | "fain",
1401 | "fair",
1402 | "fait",
1403 | "fake",
1404 | "fall",
1405 | "fame",
1406 | "fang",
1407 | "fans",
1408 | "fare",
1409 | "farm",
1410 | "faro",
1411 | "fart",
1412 | "fast",
1413 | "fate",
1414 | "fats",
1415 | "faun",
1416 | "faut",
1417 | "faux",
1418 | "fawn",
1419 | "fays",
1420 | "faze",
1421 | "fear",
1422 | "feat",
1423 | "feds",
1424 | "feed",
1425 | "feel",
1426 | "fees",
1427 | "feet",
1428 | "fell",
1429 | "felt",
1430 | "fend",
1431 | "fens",
1432 | "fern",
1433 | "fess",
1434 | "feta",
1435 | "fete",
1436 | "feud",
1437 | "fiat",
1438 | "fibs",
1439 | "fica",
1440 | "fide",
1441 | "fido",
1442 | "fids",
1443 | "fief",
1444 | "fife",
1445 | "figs",
1446 | "fiji",
1447 | "file",
1448 | "fill",
1449 | "film",
1450 | "find",
1451 | "fine",
1452 | "fink",
1453 | "finn",
1454 | "fins",
1455 | "fire",
1456 | "firm",
1457 | "firs",
1458 | "fish",
1459 | "fist",
1460 | "fits",
1461 | "five",
1462 | "fixe",
1463 | "fizz",
1464 | "flab",
1465 | "flag",
1466 | "flak",
1467 | "flan",
1468 | "flap",
1469 | "flat",
1470 | "flaw",
1471 | "flax",
1472 | "flay",
1473 | "flea",
1474 | "fled",
1475 | "flee",
1476 | "flew",
1477 | "flex",
1478 | "flip",
1479 | "flit",
1480 | "floe",
1481 | "flog",
1482 | "flop",
1483 | "flow",
1484 | "flub",
1485 | "flue",
1486 | "flus",
1487 | "flux",
1488 | "foal",
1489 | "foam",
1490 | "fobs",
1491 | "foci",
1492 | "foes",
1493 | "fogs",
1494 | "fogy",
1495 | "foil",
1496 | "fold",
1497 | "folk",
1498 | "fond",
1499 | "font",
1500 | "food",
1501 | "fool",
1502 | "foot",
1503 | "fops",
1504 | "fora",
1505 | "ford",
1506 | "fore",
1507 | "fork",
1508 | "form",
1509 | "fort",
1510 | "foul",
1511 | "four",
1512 | "fowl",
1513 | "foxy",
1514 | "frag",
1515 | "frat",
1516 | "frau",
1517 | "fray",
1518 | "fred",
1519 | "free",
1520 | "fret",
1521 | "frig",
1522 | "friz",
1523 | "frog",
1524 | "from",
1525 | "frow",
1526 | "frug",
1527 | "fuck",
1528 | "fuds",
1529 | "fuel",
1530 | "fugs",
1531 | "fuji",
1532 | "full",
1533 | "fume",
1534 | "fumy",
1535 | "fund",
1536 | "funk",
1537 | "furl",
1538 | "furs",
1539 | "fury",
1540 | "fuse",
1541 | "fuss",
1542 | "fuze",
1543 | "fuzz",
1544 | "gabs",
1545 | "gads",
1546 | "gaff",
1547 | "gaga",
1548 | "gage",
1549 | "gags",
1550 | "gain",
1551 | "gait",
1552 | "gala",
1553 | "gale",
1554 | "gall",
1555 | "gals",
1556 | "game",
1557 | "gams",
1558 | "gamy",
1559 | "gang",
1560 | "gaol",
1561 | "gape",
1562 | "gaps",
1563 | "gapy",
1564 | "garb",
1565 | "gars",
1566 | "gary",
1567 | "gash",
1568 | "gasp",
1569 | "gate",
1570 | "gats",
1571 | "gaud",
1572 | "gave",
1573 | "gawk",
1574 | "gays",
1575 | "gaze",
1576 | "gear",
1577 | "geed",
1578 | "geek",
1579 | "gees",
1580 | "geld",
1581 | "gels",
1582 | "gelt",
1583 | "gems",
1584 | "gene",
1585 | "gens",
1586 | "gent",
1587 | "geog",
1588 | "geol",
1589 | "geom",
1590 | "germ",
1591 | "gets",
1592 | "geum",
1593 | "ghat",
1594 | "ghee",
1595 | "gibe",
1596 | "gibs",
1597 | "gift",
1598 | "gigs",
1599 | "gila",
1600 | "gild",
1601 | "gill",
1602 | "gilt",
1603 | "gimp",
1604 | "gins",
1605 | "gips",
1606 | "gird",
1607 | "girl",
1608 | "girt",
1609 | "gist",
1610 | "give",
1611 | "glad",
1612 | "glee",
1613 | "glen",
1614 | "glib",
1615 | "glim",
1616 | "glob",
1617 | "glom",
1618 | "glop",
1619 | "glow",
1620 | "glue",
1621 | "glum",
1622 | "glut",
1623 | "gnat",
1624 | "gnaw",
1625 | "gnus",
1626 | "goad",
1627 | "goal",
1628 | "goat",
1629 | "gobs",
1630 | "goby",
1631 | "gods",
1632 | "goer",
1633 | "goes",
1634 | "gogo",
1635 | "gold",
1636 | "golf",
1637 | "gone",
1638 | "gong",
1639 | "good",
1640 | "goof",
1641 | "gook",
1642 | "goon",
1643 | "goop",
1644 | "goos",
1645 | "gore",
1646 | "gory",
1647 | "gosh",
1648 | "goth",
1649 | "gout",
1650 | "govt",
1651 | "gown",
1652 | "goys",
1653 | "grab",
1654 | "grad",
1655 | "gram",
1656 | "gras",
1657 | "gray",
1658 | "grew",
1659 | "grey",
1660 | "grid",
1661 | "grim",
1662 | "grin",
1663 | "grip",
1664 | "grit",
1665 | "grog",
1666 | "grot",
1667 | "grow",
1668 | "grub",
1669 | "guam",
1670 | "guar",
1671 | "guck",
1672 | "guff",
1673 | "gulf",
1674 | "gull",
1675 | "gulp",
1676 | "gums",
1677 | "gung",
1678 | "gunk",
1679 | "guns",
1680 | "guru",
1681 | "gush",
1682 | "gust",
1683 | "guts",
1684 | "guys",
1685 | "gyms",
1686 | "gyps",
1687 | "gyre",
1688 | "gyro",
1689 | "gyve",
1690 | "hack",
1691 | "hadj",
1692 | "haft",
1693 | "hags",
1694 | "hahs",
1695 | "hail",
1696 | "hair",
1697 | "haji",
1698 | "hajj",
1699 | "hake",
1700 | "hale",
1701 | "half",
1702 | "hall",
1703 | "halo",
1704 | "halt",
1705 | "hams",
1706 | "hand",
1707 | "hang",
1708 | "hank",
1709 | "haps",
1710 | "hard",
1711 | "hare",
1712 | "hark",
1713 | "harm",
1714 | "harp",
1715 | "hart",
1716 | "hash",
1717 | "hasp",
1718 | "hast",
1719 | "hate",
1720 | "hath",
1721 | "hats",
1722 | "haul",
1723 | "have",
1724 | "hawk",
1725 | "haws",
1726 | "hays",
1727 | "haze",
1728 | "hazy",
1729 | "head",
1730 | "heal",
1731 | "heap",
1732 | "hear",
1733 | "heat",
1734 | "heck",
1735 | "heed",
1736 | "heel",
1737 | "heft",
1738 | "heil",
1739 | "heir",
1740 | "held",
1741 | "hell",
1742 | "helm",
1743 | "help",
1744 | "heme",
1745 | "hemp",
1746 | "hems",
1747 | "hens",
1748 | "herb",
1749 | "herd",
1750 | "here",
1751 | "hero",
1752 | "herr",
1753 | "hers",
1754 | "hest",
1755 | "hewn",
1756 | "hews",
1757 | "hick",
1758 | "hide",
1759 | "hied",
1760 | "hies",
1761 | "high",
1762 | "hike",
1763 | "hill",
1764 | "hilt",
1765 | "hind",
1766 | "hint",
1767 | "hips",
1768 | "hire",
1769 | "hisn",
1770 | "hiss",
1771 | "hist",
1772 | "hits",
1773 | "hive",
1774 | "hoar",
1775 | "hoax",
1776 | "hobo",
1777 | "hobs",
1778 | "hock",
1779 | "hods",
1780 | "hoed",
1781 | "hoer",
1782 | "hoes",
1783 | "hogs",
1784 | "hoke",
1785 | "hold",
1786 | "hole",
1787 | "holt",
1788 | "holy",
1789 | "home",
1790 | "homo",
1791 | "homy",
1792 | "hone",
1793 | "honk",
1794 | "hood",
1795 | "hoof",
1796 | "hook",
1797 | "hoop",
1798 | "hoot",
1799 | "hope",
1800 | "hopi",
1801 | "hops",
1802 | "hora",
1803 | "horn",
1804 | "hors",
1805 | "hose",
1806 | "hosp",
1807 | "host",
1808 | "hots",
1809 | "hour",
1810 | "hove",
1811 | "howe",
1812 | "howl",
1813 | "hows",
1814 | "hubs",
1815 | "huck",
1816 | "hued",
1817 | "hues",
1818 | "huff",
1819 | "huge",
1820 | "hugs",
1821 | "hula",
1822 | "hulk",
1823 | "hull",
1824 | "hump",
1825 | "hums",
1826 | "hung",
1827 | "hunk",
1828 | "huns",
1829 | "hunt",
1830 | "hurl",
1831 | "hurt",
1832 | "hush",
1833 | "husk",
1834 | "huts",
1835 | "hyde",
1836 | "hymn",
1837 | "hype",
1838 | "hypo",
1839 | "iamb",
1840 | "ibex",
1841 | "ibid",
1842 | "ibis",
1843 | "icbm",
1844 | "iced",
1845 | "ices",
1846 | "icky",
1847 | "icon",
1848 | "idea",
1849 | "idee",
1850 | "idem",
1851 | "ideo",
1852 | "ides",
1853 | "idle",
1854 | "idly",
1855 | "idol",
1856 | "idyl",
1857 | "ieee",
1858 | "iffy",
1859 | "ikon",
1860 | "ilia",
1861 | "ilks",
1862 | "ills",
1863 | "illy",
1864 | "imam",
1865 | "imps",
1866 | "inca",
1867 | "inch",
1868 | "info",
1869 | "inks",
1870 | "inky",
1871 | "inly",
1872 | "inns",
1873 | "inst",
1874 | "intl",
1875 | "into",
1876 | "intr",
1877 | "ions",
1878 | "iota",
1879 | "iowa",
1880 | "ipso",
1881 | "iran",
1882 | "iraq",
1883 | "ired",
1884 | "ires",
1885 | "iris",
1886 | "irks",
1887 | "iron",
1888 | "isis",
1889 | "isle",
1890 | "isms",
1891 | "ital",
1892 | "itch",
1893 | "item",
1894 | "iuds",
1895 | "ixia",
1896 | "izar",
1897 | "jabs",
1898 | "jack",
1899 | "jade",
1900 | "jags",
1901 | "jail",
1902 | "jake",
1903 | "jamb",
1904 | "jams",
1905 | "jane",
1906 | "jape",
1907 | "jars",
1908 | "jato",
1909 | "java",
1910 | "jaws",
1911 | "jays",
1912 | "jazz",
1913 | "jean",
1914 | "jeep",
1915 | "jeer",
1916 | "jeez",
1917 | "jefe",
1918 | "jell",
1919 | "jerk",
1920 | "jess",
1921 | "jest",
1922 | "jets",
1923 | "jeux",
1924 | "jews",
1925 | "jibe",
1926 | "jibs",
1927 | "jiff",
1928 | "jigs",
1929 | "jill",
1930 | "jilt",
1931 | "jinn",
1932 | "jins",
1933 | "jinx",
1934 | "jive",
1935 | "jobs",
1936 | "jock",
1937 | "joes",
1938 | "joey",
1939 | "jogs",
1940 | "john",
1941 | "joie",
1942 | "join",
1943 | "joke",
1944 | "jolt",
1945 | "jose",
1946 | "josh",
1947 | "joss",
1948 | "jota",
1949 | "jots",
1950 | "jour",
1951 | "jowl",
1952 | "joys",
1953 | "juan",
1954 | "judo",
1955 | "judy",
1956 | "jugs",
1957 | "juju",
1958 | "juke",
1959 | "july",
1960 | "jump",
1961 | "june",
1962 | "junk",
1963 | "juno",
1964 | "jupe",
1965 | "jure",
1966 | "jury",
1967 | "just",
1968 | "jute",
1969 | "juts",
1970 | "kaka",
1971 | "kale",
1972 | "kame",
1973 | "kart",
1974 | "kayo",
1975 | "kays",
1976 | "keel",
1977 | "keen",
1978 | "keep",
1979 | "kegs",
1980 | "kelp",
1981 | "keno",
1982 | "kens",
1983 | "kent",
1984 | "kepi",
1985 | "kept",
1986 | "kerb",
1987 | "kerf",
1988 | "kern",
1989 | "keys",
1990 | "khan",
1991 | "kick",
1992 | "kids",
1993 | "kiev",
1994 | "kike",
1995 | "kill",
1996 | "kiln",
1997 | "kilo",
1998 | "kilt",
1999 | "kind",
2000 | "kine",
2001 | "king",
2002 | "kink",
2003 | "kins",
2004 | "kips",
2005 | "kirk",
2006 | "kiss",
2007 | "kist",
2008 | "kite",
2009 | "kith",
2010 | "kits",
2011 | "kiwi",
2012 | "knee",
2013 | "knew",
2014 | "knit",
2015 | "knob",
2016 | "knot",
2017 | "know",
2018 | "knox",
2019 | "koan",
2020 | "kohl",
2021 | "kola",
2022 | "kong",
2023 | "kook",
2024 | "koto",
2025 | "kris",
2026 | "kudo",
2027 | "kudu",
2028 | "kung",
2029 | "kwhr",
2030 | "kyat",
2031 | "labs",
2032 | "lace",
2033 | "lack",
2034 | "lacy",
2035 | "lade",
2036 | "lads",
2037 | "lady",
2038 | "lags",
2039 | "laid",
2040 | "lain",
2041 | "lair",
2042 | "lait",
2043 | "lake",
2044 | "laky",
2045 | "lama",
2046 | "lamb",
2047 | "lame",
2048 | "lamp",
2049 | "lams",
2050 | "land",
2051 | "lane",
2052 | "lank",
2053 | "laos",
2054 | "lapp",
2055 | "laps",
2056 | "lard",
2057 | "lark",
2058 | "lash",
2059 | "lass",
2060 | "last",
2061 | "late",
2062 | "lath",
2063 | "laud",
2064 | "lava",
2065 | "lave",
2066 | "lawn",
2067 | "laws",
2068 | "lays",
2069 | "laze",
2070 | "lazy",
2071 | "lead",
2072 | "leaf",
2073 | "leak",
2074 | "leal",
2075 | "lean",
2076 | "leap",
2077 | "lear",
2078 | "leas",
2079 | "lech",
2080 | "lect",
2081 | "leek",
2082 | "leer",
2083 | "lees",
2084 | "left",
2085 | "legs",
2086 | "leis",
2087 | "leks",
2088 | "lend",
2089 | "lens",
2090 | "lent",
2091 | "leon",
2092 | "leos",
2093 | "lese",
2094 | "less",
2095 | "lest",
2096 | "lets",
2097 | "leva",
2098 | "levi",
2099 | "levo",
2100 | "levy",
2101 | "lewd",
2102 | "leys",
2103 | "liar",
2104 | "libs",
2105 | "lice",
2106 | "lick",
2107 | "lido",
2108 | "lids",
2109 | "lied",
2110 | "lief",
2111 | "lien",
2112 | "lier",
2113 | "lies",
2114 | "lieu",
2115 | "life",
2116 | "lift",
2117 | "like",
2118 | "lilt",
2119 | "lily",
2120 | "lima",
2121 | "limb",
2122 | "lime",
2123 | "limn",
2124 | "limo",
2125 | "limp",
2126 | "limy",
2127 | "line",
2128 | "ling",
2129 | "link",
2130 | "lino",
2131 | "lins",
2132 | "lint",
2133 | "liny",
2134 | "lion",
2135 | "lips",
2136 | "lira",
2137 | "lire",
2138 | "lisp",
2139 | "list",
2140 | "lite",
2141 | "lith",
2142 | "lits",
2143 | "live",
2144 | "load",
2145 | "loaf",
2146 | "loam",
2147 | "loan",
2148 | "lobe",
2149 | "lobo",
2150 | "lobs",
2151 | "loch",
2152 | "loci",
2153 | "lock",
2154 | "loco",
2155 | "lode",
2156 | "loft",
2157 | "loge",
2158 | "logo",
2159 | "logs",
2160 | "logy",
2161 | "loin",
2162 | "loll",
2163 | "lone",
2164 | "long",
2165 | "look",
2166 | "loom",
2167 | "loon",
2168 | "loop",
2169 | "loos",
2170 | "loot",
2171 | "lope",
2172 | "lops",
2173 | "lord",
2174 | "lore",
2175 | "lorn",
2176 | "lory",
2177 | "lose",
2178 | "loss",
2179 | "lost",
2180 | "loth",
2181 | "lots",
2182 | "loud",
2183 | "loup",
2184 | "lour",
2185 | "lout",
2186 | "love",
2187 | "lows",
2188 | "luau",
2189 | "lube",
2190 | "luck",
2191 | "lucy",
2192 | "luff",
2193 | "luge",
2194 | "lugs",
2195 | "luke",
2196 | "lull",
2197 | "lulu",
2198 | "lump",
2199 | "luna",
2200 | "lune",
2201 | "lung",
2202 | "lunk",
2203 | "luny",
2204 | "lure",
2205 | "lurk",
2206 | "lush",
2207 | "lust",
2208 | "lute",
2209 | "luxe",
2210 | "lyes",
2211 | "lynx",
2212 | "lyre",
2213 | "mace",
2214 | "mach",
2215 | "mack",
2216 | "macs",
2217 | "made",
2218 | "mads",
2219 | "mage",
2220 | "magi",
2221 | "mags",
2222 | "maid",
2223 | "mail",
2224 | "maim",
2225 | "main",
2226 | "make",
2227 | "mala",
2228 | "male",
2229 | "mali",
2230 | "mall",
2231 | "malt",
2232 | "mama",
2233 | "mane",
2234 | "mans",
2235 | "manx",
2236 | "many",
2237 | "maps",
2238 | "marc",
2239 | "mare",
2240 | "mark",
2241 | "marl",
2242 | "mars",
2243 | "mart",
2244 | "marx",
2245 | "mary",
2246 | "mash",
2247 | "mask",
2248 | "mass",
2249 | "mast",
2250 | "mate",
2251 | "math",
2252 | "mats",
2253 | "matt",
2254 | "maul",
2255 | "maut",
2256 | "maws",
2257 | "maxi",
2258 | "maya",
2259 | "mayo",
2260 | "mays",
2261 | "maze",
2262 | "mazy",
2263 | "mead",
2264 | "meal",
2265 | "mean",
2266 | "meas",
2267 | "meat",
2268 | "mech",
2269 | "meed",
2270 | "meek",
2271 | "meet",
2272 | "mein",
2273 | "meld",
2274 | "melt",
2275 | "memo",
2276 | "mend",
2277 | "mens",
2278 | "menu",
2279 | "meow",
2280 | "mere",
2281 | "mesa",
2282 | "mesh",
2283 | "mess",
2284 | "meta",
2285 | "mete",
2286 | "mewl",
2287 | "mews",
2288 | "mibs",
2289 | "mica",
2290 | "mice",
2291 | "mick",
2292 | "midi",
2293 | "mids",
2294 | "mien",
2295 | "miff",
2296 | "migs",
2297 | "mike",
2298 | "mild",
2299 | "mile",
2300 | "milk",
2301 | "mill",
2302 | "mils",
2303 | "milt",
2304 | "mime",
2305 | "mind",
2306 | "mine",
2307 | "ming",
2308 | "mini",
2309 | "mink",
2310 | "mins",
2311 | "mint",
2312 | "minx",
2313 | "mire",
2314 | "mirk",
2315 | "mirv",
2316 | "miry",
2317 | "misc",
2318 | "mise",
2319 | "miso",
2320 | "miss",
2321 | "mist",
2322 | "mite",
2323 | "mitt",
2324 | "mixt",
2325 | "mktg",
2326 | "moan",
2327 | "moas",
2328 | "moat",
2329 | "mobs",
2330 | "mock",
2331 | "mode",
2332 | "modi",
2333 | "modo",
2334 | "mods",
2335 | "moil",
2336 | "mold",
2337 | "mole",
2338 | "moll",
2339 | "molt",
2340 | "moly",
2341 | "moms",
2342 | "monk",
2343 | "mono",
2344 | "mons",
2345 | "mony",
2346 | "mood",
2347 | "moon",
2348 | "moor",
2349 | "moos",
2350 | "moot",
2351 | "mope",
2352 | "mops",
2353 | "mopy",
2354 | "more",
2355 | "morn",
2356 | "mort",
2357 | "moss",
2358 | "most",
2359 | "mote",
2360 | "moth",
2361 | "mots",
2362 | "moue",
2363 | "move",
2364 | "mown",
2365 | "mows",
2366 | "moxa",
2367 | "msec",
2368 | "much",
2369 | "muck",
2370 | "muds",
2371 | "muff",
2372 | "mugs",
2373 | "mule",
2374 | "mull",
2375 | "mumm",
2376 | "mump",
2377 | "mums",
2378 | "muon",
2379 | "murk",
2380 | "muse",
2381 | "mush",
2382 | "musk",
2383 | "muss",
2384 | "must",
2385 | "mute",
2386 | "mutt",
2387 | "myna",
2388 | "myth",
2389 | "nabs",
2390 | "nags",
2391 | "naif",
2392 | "nail",
2393 | "name",
2394 | "nape",
2395 | "naps",
2396 | "narc",
2397 | "nard",
2398 | "nark",
2399 | "nary",
2400 | "nasa",
2401 | "natl",
2402 | "nato",
2403 | "naut",
2404 | "nave",
2405 | "navy",
2406 | "nays",
2407 | "nazi",
2408 | "neap",
2409 | "near",
2410 | "neat",
2411 | "nebs",
2412 | "neck",
2413 | "need",
2414 | "neon",
2415 | "nerd",
2416 | "ness",
2417 | "nest",
2418 | "nets",
2419 | "nevi",
2420 | "news",
2421 | "newt",
2422 | "next",
2423 | "nibs",
2424 | "nice",
2425 | "nick",
2426 | "nigh",
2427 | "nile",
2428 | "nill",
2429 | "nils",
2430 | "nims",
2431 | "nine",
2432 | "nips",
2433 | "nisi",
2434 | "nits",
2435 | "nixy",
2436 | "noah",
2437 | "nobs",
2438 | "nock",
2439 | "node",
2440 | "nods",
2441 | "noel",
2442 | "noes",
2443 | "nogs",
2444 | "noir",
2445 | "nolo",
2446 | "nome",
2447 | "noms",
2448 | "none",
2449 | "nook",
2450 | "noon",
2451 | "nope",
2452 | "norm",
2453 | "nose",
2454 | "nosh",
2455 | "nosy",
2456 | "nota",
2457 | "note",
2458 | "nots",
2459 | "noun",
2460 | "nous",
2461 | "nova",
2462 | "novo",
2463 | "nows",
2464 | "nubs",
2465 | "nude",
2466 | "nuke",
2467 | "null",
2468 | "numb",
2469 | "nuns",
2470 | "nuts",
2471 | "oafs",
2472 | "oaks",
2473 | "oars",
2474 | "oath",
2475 | "oats",
2476 | "obey",
2477 | "obis",
2478 | "obit",
2479 | "oboe",
2480 | "obol",
2481 | "odds",
2482 | "odes",
2483 | "odic",
2484 | "odin",
2485 | "odor",
2486 | "odyl",
2487 | "ofay",
2488 | "offs",
2489 | "ogee",
2490 | "ogle",
2491 | "ogre",
2492 | "ohed",
2493 | "ohio",
2494 | "ohms",
2495 | "oils",
2496 | "oily",
2497 | "oink",
2498 | "okay",
2499 | "okie",
2500 | "okra",
2501 | "olds",
2502 | "oleo",
2503 | "oles",
2504 | "olio",
2505 | "olla",
2506 | "omen",
2507 | "omit",
2508 | "once",
2509 | "ones",
2510 | "only",
2511 | "onto",
2512 | "onus",
2513 | "onyx",
2514 | "oohs",
2515 | "oops",
2516 | "ooze",
2517 | "oozy",
2518 | "opal",
2519 | "opec",
2520 | "open",
2521 | "opes",
2522 | "opts",
2523 | "opus",
2524 | "oral",
2525 | "orbs",
2526 | "orca",
2527 | "orch",
2528 | "orcs",
2529 | "ordo",
2530 | "ores",
2531 | "orgy",
2532 | "orig",
2533 | "orth",
2534 | "orts",
2535 | "oryx",
2536 | "oslo",
2537 | "otic",
2538 | "otto",
2539 | "ouch",
2540 | "ours",
2541 | "oust",
2542 | "outs",
2543 | "ouzo",
2544 | "oval",
2545 | "oven",
2546 | "over",
2547 | "ovid",
2548 | "ovum",
2549 | "owed",
2550 | "owes",
2551 | "owls",
2552 | "owns",
2553 | "oxen",
2554 | "oxes",
2555 | "oyer",
2556 | "oyes",
2557 | "oyez",
2558 | "pace",
2559 | "pack",
2560 | "pacs",
2561 | "pact",
2562 | "pads",
2563 | "page",
2564 | "paid",
2565 | "pail",
2566 | "pain",
2567 | "pair",
2568 | "pale",
2569 | "pall",
2570 | "palm",
2571 | "pals",
2572 | "pane",
2573 | "pang",
2574 | "pans",
2575 | "pant",
2576 | "papa",
2577 | "paps",
2578 | "para",
2579 | "pard",
2580 | "pare",
2581 | "park",
2582 | "pars",
2583 | "part",
2584 | "paso",
2585 | "pass",
2586 | "past",
2587 | "pate",
2588 | "path",
2589 | "pats",
2590 | "paul",
2591 | "pave",
2592 | "pawl",
2593 | "pawn",
2594 | "paws",
2595 | "pays",
2596 | "peak",
2597 | "peal",
2598 | "pean",
2599 | "pear",
2600 | "peas",
2601 | "peat",
2602 | "peck",
2603 | "peds",
2604 | "peed",
2605 | "peek",
2606 | "peel",
2607 | "peen",
2608 | "peep",
2609 | "peer",
2610 | "pees",
2611 | "pegs",
2612 | "peke",
2613 | "pelf",
2614 | "pelt",
2615 | "pend",
2616 | "pens",
2617 | "pent",
2618 | "peon",
2619 | "peps",
2620 | "pere",
2621 | "perk",
2622 | "perm",
2623 | "pert",
2624 | "peru",
2625 | "peso",
2626 | "pest",
2627 | "pets",
2628 | "pews",
2629 | "phew",
2630 | "phiz",
2631 | "phys",
2632 | "pica",
2633 | "pick",
2634 | "pics",
2635 | "pied",
2636 | "pier",
2637 | "pies",
2638 | "pigs",
2639 | "pike",
2640 | "pile",
2641 | "pill",
2642 | "pima",
2643 | "pimp",
2644 | "pine",
2645 | "ping",
2646 | "pink",
2647 | "pins",
2648 | "pint",
2649 | "piny",
2650 | "pion",
2651 | "pipe",
2652 | "pips",
2653 | "pipy",
2654 | "pisa",
2655 | "pish",
2656 | "piss",
2657 | "pita",
2658 | "pith",
2659 | "pits",
2660 | "pity",
2661 | "pius",
2662 | "pixy",
2663 | "pkwy",
2664 | "plan",
2665 | "plat",
2666 | "play",
2667 | "plea",
2668 | "pled",
2669 | "plod",
2670 | "plop",
2671 | "plot",
2672 | "plow",
2673 | "ploy",
2674 | "plug",
2675 | "plum",
2676 | "plus",
2677 | "pock",
2678 | "poco",
2679 | "pods",
2680 | "poem",
2681 | "poet",
2682 | "poke",
2683 | "poky",
2684 | "pole",
2685 | "polk",
2686 | "poll",
2687 | "polo",
2688 | "pols",
2689 | "poly",
2690 | "pome",
2691 | "pomp",
2692 | "pond",
2693 | "pone",
2694 | "pong",
2695 | "pons",
2696 | "pony",
2697 | "pooh",
2698 | "pool",
2699 | "poop",
2700 | "poor",
2701 | "pope",
2702 | "pops",
2703 | "pore",
2704 | "pork",
2705 | "porn",
2706 | "port",
2707 | "pose",
2708 | "posh",
2709 | "post",
2710 | "posy",
2711 | "pots",
2712 | "pouf",
2713 | "pour",
2714 | "pout",
2715 | "pows",
2716 | "pram",
2717 | "prat",
2718 | "pray",
2719 | "prep",
2720 | "pres",
2721 | "prey",
2722 | "prig",
2723 | "prim",
2724 | "prix",
2725 | "proc",
2726 | "prod",
2727 | "prof",
2728 | "prom",
2729 | "pron",
2730 | "prop",
2731 | "pros",
2732 | "prow",
2733 | "psst",
2734 | "pubs",
2735 | "puce",
2736 | "puck",
2737 | "puds",
2738 | "puff",
2739 | "pugs",
2740 | "puke",
2741 | "pule",
2742 | "pull",
2743 | "pulp",
2744 | "puma",
2745 | "pump",
2746 | "punk",
2747 | "puns",
2748 | "punt",
2749 | "puny",
2750 | "pupa",
2751 | "pups",
2752 | "pure",
2753 | "purl",
2754 | "purr",
2755 | "push",
2756 | "puss",
2757 | "puts",
2758 | "putt",
2759 | "pyre",
2760 | "qaid",
2761 | "qoph",
2762 | "quad",
2763 | "quae",
2764 | "quag",
2765 | "quai",
2766 | "qual",
2767 | "quam",
2768 | "quat",
2769 | "quay",
2770 | "quem",
2771 | "ques",
2772 | "quey",
2773 | "quia",
2774 | "quid",
2775 | "quip",
2776 | "quit",
2777 | "quiz",
2778 | "quod",
2779 | "quos",
2780 | "race",
2781 | "rack",
2782 | "racy",
2783 | "rads",
2784 | "raft",
2785 | "raga",
2786 | "rage",
2787 | "rags",
2788 | "raid",
2789 | "rail",
2790 | "rain",
2791 | "raja",
2792 | "rake",
2793 | "ramp",
2794 | "rams",
2795 | "rand",
2796 | "rang",
2797 | "rani",
2798 | "rank",
2799 | "rant",
2800 | "rape",
2801 | "raps",
2802 | "rapt",
2803 | "rara",
2804 | "rare",
2805 | "rase",
2806 | "rash",
2807 | "rasp",
2808 | "rata",
2809 | "rate",
2810 | "rats",
2811 | "rave",
2812 | "raws",
2813 | "rays",
2814 | "raze",
2815 | "razz",
2816 | "rcpt",
2817 | "read",
2818 | "real",
2819 | "ream",
2820 | "reap",
2821 | "rear",
2822 | "rebs",
2823 | "recd",
2824 | "recs",
2825 | "redo",
2826 | "reds",
2827 | "reed",
2828 | "reef",
2829 | "reek",
2830 | "reel",
2831 | "refs",
2832 | "reft",
2833 | "rein",
2834 | "rely",
2835 | "rems",
2836 | "rend",
2837 | "reno",
2838 | "rent",
2839 | "reps",
2840 | "resp",
2841 | "rest",
2842 | "retd",
2843 | "revs",
2844 | "rhea",
2845 | "rial",
2846 | "ribs",
2847 | "rice",
2848 | "rich",
2849 | "rick",
2850 | "ride",
2851 | "rids",
2852 | "riel",
2853 | "rife",
2854 | "riff",
2855 | "rift",
2856 | "rigs",
2857 | "rile",
2858 | "rill",
2859 | "rime",
2860 | "rims",
2861 | "rimy",
2862 | "rind",
2863 | "ring",
2864 | "rink",
2865 | "riot",
2866 | "ripe",
2867 | "rips",
2868 | "rise",
2869 | "risk",
2870 | "rite",
2871 | "ritz",
2872 | "rive",
2873 | "road",
2874 | "roam",
2875 | "roan",
2876 | "roar",
2877 | "robe",
2878 | "robs",
2879 | "rock",
2880 | "rocs",
2881 | "rode",
2882 | "rods",
2883 | "roes",
2884 | "roil",
2885 | "role",
2886 | "roll",
2887 | "rome",
2888 | "romp",
2889 | "roms",
2890 | "rood",
2891 | "roof",
2892 | "rook",
2893 | "room",
2894 | "root",
2895 | "rope",
2896 | "ropy",
2897 | "rosa",
2898 | "rose",
2899 | "rosy",
2900 | "rote",
2901 | "roto",
2902 | "rots",
2903 | "roue",
2904 | "rout",
2905 | "roux",
2906 | "rove",
2907 | "rows",
2908 | "rube",
2909 | "rubs",
2910 | "ruby",
2911 | "ruck",
2912 | "rude",
2913 | "rued",
2914 | "ruer",
2915 | "rues",
2916 | "ruff",
2917 | "rugs",
2918 | "ruin",
2919 | "rule",
2920 | "rump",
2921 | "rums",
2922 | "rune",
2923 | "rung",
2924 | "runs",
2925 | "runt",
2926 | "ruse",
2927 | "rush",
2928 | "rusk",
2929 | "rust",
2930 | "ruth",
2931 | "ruts",
2932 | "ryas",
2933 | "ryes",
2934 | "sack",
2935 | "sacs",
2936 | "safe",
2937 | "saga",
2938 | "sage",
2939 | "sago",
2940 | "sags",
2941 | "sagy",
2942 | "said",
2943 | "sail",
2944 | "sake",
2945 | "sale",
2946 | "salt",
2947 | "same",
2948 | "sand",
2949 | "sane",
2950 | "sang",
2951 | "sank",
2952 | "sans",
2953 | "saps",
2954 | "sari",
2955 | "sash",
2956 | "sass",
2957 | "sate",
2958 | "save",
2959 | "sawn",
2960 | "saws",
2961 | "says",
2962 | "scab",
2963 | "scad",
2964 | "scag",
2965 | "scam",
2966 | "scan",
2967 | "scar",
2968 | "scat",
2969 | "scil",
2970 | "scop",
2971 | "scot",
2972 | "scow",
2973 | "scud",
2974 | "scum",
2975 | "scut",
2976 | "seal",
2977 | "seam",
2978 | "sear",
2979 | "seas",
2980 | "seat",
2981 | "secs",
2982 | "sect",
2983 | "seed",
2984 | "seek",
2985 | "seem",
2986 | "seen",
2987 | "seep",
2988 | "seer",
2989 | "sees",
2990 | "self",
2991 | "sell",
2992 | "semi",
2993 | "send",
2994 | "sent",
2995 | "sept",
2996 | "sera",
2997 | "serb",
2998 | "sere",
2999 | "serf",
3000 | "sets",
3001 | "sewn",
3002 | "sews",
3003 | "sexy",
3004 | "shad",
3005 | "shag",
3006 | "shah",
3007 | "sham",
3008 | "shat",
3009 | "shay",
3010 | "shed",
3011 | "shes",
3012 | "shew",
3013 | "shim",
3014 | "shin",
3015 | "ship",
3016 | "shit",
3017 | "shiv",
3018 | "shmo",
3019 | "shod",
3020 | "shoe",
3021 | "shoo",
3022 | "shop",
3023 | "shot",
3024 | "show",
3025 | "shul",
3026 | "shun",
3027 | "shut",
3028 | "siam",
3029 | "sibs",
3030 | "sick",
3031 | "sics",
3032 | "side",
3033 | "sift",
3034 | "sigh",
3035 | "sign",
3036 | "sikh",
3037 | "silk",
3038 | "sill",
3039 | "silo",
3040 | "silt",
3041 | "simp",
3042 | "sine",
3043 | "sing",
3044 | "sinh",
3045 | "sink",
3046 | "sins",
3047 | "sips",
3048 | "sire",
3049 | "sirs",
3050 | "site",
3051 | "sits",
3052 | "situ",
3053 | "sitz",
3054 | "size",
3055 | "sizy",
3056 | "skag",
3057 | "skew",
3058 | "skid",
3059 | "skim",
3060 | "skin",
3061 | "skip",
3062 | "skis",
3063 | "skit",
3064 | "skys",
3065 | "slab",
3066 | "slag",
3067 | "slam",
3068 | "slap",
3069 | "slat",
3070 | "slav",
3071 | "slaw",
3072 | "slay",
3073 | "sled",
3074 | "slew",
3075 | "slid",
3076 | "slim",
3077 | "slip",
3078 | "slit",
3079 | "slob",
3080 | "sloe",
3081 | "slog",
3082 | "slop",
3083 | "slot",
3084 | "slow",
3085 | "slue",
3086 | "slug",
3087 | "slum",
3088 | "slur",
3089 | "slut",
3090 | "smit",
3091 | "smog",
3092 | "smug",
3093 | "smut",
3094 | "snag",
3095 | "snap",
3096 | "snip",
3097 | "snit",
3098 | "snob",
3099 | "snot",
3100 | "snow",
3101 | "snub",
3102 | "snug",
3103 | "soak",
3104 | "soap",
3105 | "soar",
3106 | "sobs",
3107 | "sock",
3108 | "soda",
3109 | "sods",
3110 | "sofa",
3111 | "soft",
3112 | "soil",
3113 | "sold",
3114 | "sole",
3115 | "soli",
3116 | "solo",
3117 | "soma",
3118 | "some",
3119 | "song",
3120 | "sons",
3121 | "soon",
3122 | "soot",
3123 | "soph",
3124 | "sops",
3125 | "sore",
3126 | "sort",
3127 | "sots",
3128 | "soul",
3129 | "soup",
3130 | "sour",
3131 | "sown",
3132 | "sows",
3133 | "soya",
3134 | "soys",
3135 | "span",
3136 | "spar",
3137 | "spas",
3138 | "spat",
3139 | "spay",
3140 | "spec",
3141 | "sped",
3142 | "spew",
3143 | "spic",
3144 | "spin",
3145 | "spit",
3146 | "spot",
3147 | "spry",
3148 | "spud",
3149 | "spun",
3150 | "spur",
3151 | "stab",
3152 | "stag",
3153 | "star",
3154 | "stat",
3155 | "stay",
3156 | "stem",
3157 | "step",
3158 | "stet",
3159 | "stew",
3160 | "stir",
3161 | "stoa",
3162 | "stop",
3163 | "stow",
3164 | "stub",
3165 | "stud",
3166 | "stun",
3167 | "stye",
3168 | "styx",
3169 | "subs",
3170 | "such",
3171 | "suck",
3172 | "suds",
3173 | "sued",
3174 | "suer",
3175 | "sues",
3176 | "suet",
3177 | "suey",
3178 | "suez",
3179 | "suit",
3180 | "sulk",
3181 | "sumo",
3182 | "sump",
3183 | "sums",
3184 | "sung",
3185 | "sunk",
3186 | "suns",
3187 | "supe",
3188 | "sups",
3189 | "supt",
3190 | "sure",
3191 | "surf",
3192 | "swab",
3193 | "swag",
3194 | "swam",
3195 | "swan",
3196 | "swap",
3197 | "swat",
3198 | "sway",
3199 | "swig",
3200 | "swim",
3201 | "swob",
3202 | "swop",
3203 | "swum",
3204 | "sync",
3205 | "syne",
3206 | "tabs",
3207 | "tabu",
3208 | "tach",
3209 | "tack",
3210 | "taco",
3211 | "tact",
3212 | "tads",
3213 | "tags",
3214 | "tail",
3215 | "take",
3216 | "talc",
3217 | "tale",
3218 | "talk",
3219 | "tall",
3220 | "tame",
3221 | "tamp",
3222 | "tams",
3223 | "tang",
3224 | "tank",
3225 | "tans",
3226 | "taos",
3227 | "tape",
3228 | "taps",
3229 | "tare",
3230 | "tarn",
3231 | "taro",
3232 | "tarp",
3233 | "tars",
3234 | "tart",
3235 | "task",
3236 | "tass",
3237 | "tate",
3238 | "tats",
3239 | "taut",
3240 | "taws",
3241 | "taxi",
3242 | "tbsp",
3243 | "teak",
3244 | "teal",
3245 | "team",
3246 | "tear",
3247 | "teas",
3248 | "teat",
3249 | "tech",
3250 | "teds",
3251 | "teed",
3252 | "teem",
3253 | "teen",
3254 | "tees",
3255 | "tell",
3256 | "temp",
3257 | "tend",
3258 | "tens",
3259 | "tent",
3260 | "term",
3261 | "tern",
3262 | "terr",
3263 | "test",
3264 | "text",
3265 | "thai",
3266 | "than",
3267 | "that",
3268 | "thaw",
3269 | "thee",
3270 | "them",
3271 | "then",
3272 | "thew",
3273 | "they",
3274 | "thin",
3275 | "this",
3276 | "thor",
3277 | "thou",
3278 | "thro",
3279 | "thru",
3280 | "thud",
3281 | "thug",
3282 | "thus",
3283 | "tick",
3284 | "tics",
3285 | "tide",
3286 | "tidy",
3287 | "tied",
3288 | "tier",
3289 | "ties",
3290 | "tiff",
3291 | "tike",
3292 | "tile",
3293 | "till",
3294 | "tilt",
3295 | "time",
3296 | "tine",
3297 | "ting",
3298 | "tins",
3299 | "tint",
3300 | "tiny",
3301 | "tipi",
3302 | "tips",
3303 | "tire",
3304 | "tiro",
3305 | "tits",
3306 | "tnpk",
3307 | "toad",
3308 | "toed",
3309 | "toes",
3310 | "toff",
3311 | "tofu",
3312 | "toga",
3313 | "togo",
3314 | "togs",
3315 | "toil",
3316 | "toke",
3317 | "told",
3318 | "tole",
3319 | "toll",
3320 | "tomb",
3321 | "tome",
3322 | "toms",
3323 | "tone",
3324 | "tong",
3325 | "tons",
3326 | "tony",
3327 | "took",
3328 | "tool",
3329 | "toot",
3330 | "tope",
3331 | "tops",
3332 | "tora",
3333 | "torc",
3334 | "tore",
3335 | "torn",
3336 | "toro",
3337 | "tors",
3338 | "tort",
3339 | "tory",
3340 | "tosh",
3341 | "toss",
3342 | "tost",
3343 | "tote",
3344 | "toto",
3345 | "tots",
3346 | "tour",
3347 | "tout",
3348 | "town",
3349 | "tows",
3350 | "toys",
3351 | "tram",
3352 | "trap",
3353 | "tray",
3354 | "tree",
3355 | "tref",
3356 | "trek",
3357 | "trey",
3358 | "trig",
3359 | "trim",
3360 | "trio",
3361 | "trip",
3362 | "trod",
3363 | "trop",
3364 | "trot",
3365 | "trow",
3366 | "troy",
3367 | "true",
3368 | "tsar",
3369 | "tuba",
3370 | "tube",
3371 | "tubs",
3372 | "tuck",
3373 | "tufa",
3374 | "tuff",
3375 | "tuft",
3376 | "tugs",
3377 | "tuna",
3378 | "tune",
3379 | "tuns",
3380 | "tups",
3381 | "turd",
3382 | "turf",
3383 | "turk",
3384 | "turn",
3385 | "tush",
3386 | "tusk",
3387 | "tuts",
3388 | "tutu",
3389 | "twas",
3390 | "twat",
3391 | "twig",
3392 | "twin",
3393 | "twit",
3394 | "twos",
3395 | "tyke",
3396 | "type",
3397 | "typo",
3398 | "tyre",
3399 | "tyro",
3400 | "tzar",
3401 | "ufos",
3402 | "ughs",
3403 | "ugli",
3404 | "ugly",
3405 | "ukes",
3406 | "ulna",
3407 | "ulva",
3408 | "umps",
3409 | "unco",
3410 | "undo",
3411 | "undy",
3412 | "unit",
3413 | "univ",
3414 | "unix",
3415 | "unto",
3416 | "unum",
3417 | "upon",
3418 | "ural",
3419 | "urbs",
3420 | "urds",
3421 | "urea",
3422 | "urge",
3423 | "uric",
3424 | "urns",
3425 | "ursa",
3426 | "used",
3427 | "usee",
3428 | "user",
3429 | "uses",
3430 | "ussr",
3431 | "utah",
3432 | "vade",
3433 | "vail",
3434 | "vain",
3435 | "vale",
3436 | "vamp",
3437 | "vane",
3438 | "vans",
3439 | "vary",
3440 | "vase",
3441 | "vast",
3442 | "vats",
3443 | "veal",
3444 | "veda",
3445 | "veep",
3446 | "veer",
3447 | "vees",
3448 | "veil",
3449 | "vein",
3450 | "vela",
3451 | "veld",
3452 | "vend",
3453 | "vent",
3454 | "verb",
3455 | "vers",
3456 | "vert",
3457 | "very",
3458 | "vest",
3459 | "veto",
3460 | "vets",
3461 | "vial",
3462 | "vias",
3463 | "vice",
3464 | "vide",
3465 | "vied",
3466 | "vier",
3467 | "vies",
3468 | "view",
3469 | "vile",
3470 | "vims",
3471 | "vine",
3472 | "vino",
3473 | "vins",
3474 | "viny",
3475 | "viol",
3476 | "vips",
3477 | "visa",
3478 | "vise",
3479 | "vita",
3480 | "viva",
3481 | "vive",
3482 | "vivo",
3483 | "voce",
3484 | "void",
3485 | "vole",
3486 | "volt",
3487 | "vote",
3488 | "vows",
3489 | "vrow",
3490 | "vugg",
3491 | "vugh",
3492 | "vugs",
3493 | "wack",
3494 | "wacs",
3495 | "wade",
3496 | "wadi",
3497 | "wads",
3498 | "waft",
3499 | "wage",
3500 | "wags",
3501 | "waif",
3502 | "wail",
3503 | "wain",
3504 | "wait",
3505 | "wake",
3506 | "wale",
3507 | "walk",
3508 | "wall",
3509 | "walt",
3510 | "wand",
3511 | "wane",
3512 | "wang",
3513 | "want",
3514 | "ward",
3515 | "ware",
3516 | "wark",
3517 | "warm",
3518 | "warn",
3519 | "warp",
3520 | "wars",
3521 | "wart",
3522 | "wary",
3523 | "wash",
3524 | "wasp",
3525 | "wast",
3526 | "wats",
3527 | "watt",
3528 | "waul",
3529 | "wave",
3530 | "wavy",
3531 | "waxy",
3532 | "ways",
3533 | "weak",
3534 | "weal",
3535 | "wean",
3536 | "wear",
3537 | "webs",
3538 | "weds",
3539 | "weed",
3540 | "week",
3541 | "ween",
3542 | "weep",
3543 | "weft",
3544 | "weir",
3545 | "weld",
3546 | "well",
3547 | "welt",
3548 | "wend",
3549 | "wens",
3550 | "went",
3551 | "wept",
3552 | "were",
3553 | "wert",
3554 | "west",
3555 | "wets",
3556 | "wham",
3557 | "whap",
3558 | "what",
3559 | "whee",
3560 | "when",
3561 | "whet",
3562 | "whew",
3563 | "whey",
3564 | "whig",
3565 | "whim",
3566 | "whip",
3567 | "whir",
3568 | "whit",
3569 | "whiz",
3570 | "whoa",
3571 | "whom",
3572 | "whys",
3573 | "wick",
3574 | "wide",
3575 | "wife",
3576 | "wigs",
3577 | "wild",
3578 | "wile",
3579 | "will",
3580 | "wilt",
3581 | "wily",
3582 | "wind",
3583 | "wine",
3584 | "wing",
3585 | "wink",
3586 | "wino",
3587 | "wins",
3588 | "winy",
3589 | "wipe",
3590 | "wire",
3591 | "wiry",
3592 | "wise",
3593 | "wish",
3594 | "wisp",
3595 | "with",
3596 | "wits",
3597 | "wive",
3598 | "wkly",
3599 | "woad",
3600 | "woes",
3601 | "woke",
3602 | "woks",
3603 | "wold",
3604 | "wolf",
3605 | "womb",
3606 | "wont",
3607 | "wood",
3608 | "woof",
3609 | "wool",
3610 | "woos",
3611 | "wops",
3612 | "word",
3613 | "wore",
3614 | "work",
3615 | "worm",
3616 | "worn",
3617 | "wort",
3618 | "wots",
3619 | "wove",
3620 | "wows",
3621 | "wrap",
3622 | "wren",
3623 | "writ",
3624 | "wyes",
3625 | "xiii",
3626 | "xmas",
3627 | "xvii",
3628 | "xxii",
3629 | "xxiv",
3630 | "yack",
3631 | "yaks",
3632 | "yale",
3633 | "yams",
3634 | "yang",
3635 | "yank",
3636 | "yaps",
3637 | "yard",
3638 | "yare",
3639 | "yarn",
3640 | "yawl",
3641 | "yawn",
3642 | "yawp",
3643 | "yaws",
3644 | "yeah",
3645 | "year",
3646 | "yeas",
3647 | "yegg",
3648 | "yell",
3649 | "yelp",
3650 | "yens",
3651 | "yeti",
3652 | "yews",
3653 | "yids",
3654 | "yins",
3655 | "yipe",
3656 | "yips",
3657 | "ymca",
3658 | "yoga",
3659 | "yogi",
3660 | "yoke",
3661 | "yolk",
3662 | "yond",
3663 | "yoni",
3664 | "yore",
3665 | "york",
3666 | "your",
3667 | "yowl",
3668 | "yows",
3669 | "yuan",
3670 | "yuks",
3671 | "yule",
3672 | "yurt",
3673 | "ywca",
3674 | "zags",
3675 | "zany",
3676 | "zaps",
3677 | "zeal",
3678 | "zebu",
3679 | "zeds",
3680 | "zees",
3681 | "zero",
3682 | "zest",
3683 | "zeta",
3684 | "zeus",
3685 | "zigs",
3686 | "zinc",
3687 | "zing",
3688 | "zion",
3689 | "zips",
3690 | "zone",
3691 | "zoom",
3692 | "zoos",
3693 | "zori",
3694 | "zulu",
3695 | "zuni"
3696 | ]
3697 | */
3698 |
--------------------------------------------------------------------------------
/Sources/Griddle/GameManager.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 | import Foundation
3 | import JavaScriptKit
4 |
5 | final class GameManager: ObservableObject {
6 | static let maxGuessesPerLetter = 5
7 |
8 | let grid: [[Character]]
9 | let occurrences: [Character:Int]
10 |
11 | var showSummaryModalTimer: JSTimer?
12 | @Published var phase = Phase.playing {
13 | didSet {
14 | if phase != .playing {
15 | switch phase {
16 | case .win:
17 | if guesses.contains(where: { $0.contains(where: { $0.count > 4 }) }) {
18 | self.showMessage("Phew!")
19 | } else {
20 | self.showMessage("Nice job!")
21 | }
22 | case .loss:
23 | self.showMessage("Oh no!")
24 | default:
25 | break
26 | }
27 | self.showSummaryModalTimer = JSTimer(millisecondsDelay: 2000) { [weak self] in
28 | self?.showSummaryModal = true
29 | }
30 | }
31 | }
32 | }
33 | @Published var showSummaryModal = false
34 | @Published var activeInput = [Character]()
35 | @Published var guesses = [[[Character]]]()
36 | @Published var selection: Selection? = nil
37 |
38 | @Published var messages = [String]()
39 |
40 | enum Phase: Hashable {
41 | case playing
42 | case win
43 | case loss
44 | }
45 |
46 | struct Selection: Hashable {
47 | let direction: Axis
48 | var row: Int
49 | var column: Int
50 | }
51 |
52 | init() {
53 | let grid = Day.today.grid
54 | self.grid = grid
55 | var occurrences = [Character:Int]()
56 | var guesses = [[[Character]]]()
57 | for row in grid {
58 | guesses.append(Array(repeating: [], count: row.count))
59 | for column in row {
60 | occurrences[column, default: 0] += 1
61 | }
62 | }
63 | self.guesses = GameHistory.shared.history[.today] ?? guesses
64 | self.occurrences = occurrences
65 | activeInput = grid.map { _ in " " }
66 |
67 | checkWin()
68 |
69 | let alphabet = "abcdefghijklmnopqrstuvwxyz"
70 | _ = JSObject.global.document.addEventListener("keydown", JSClosure { [weak self] event in
71 | guard let event = event.first?.object,
72 | let key = event.key.string
73 | else { return .undefined }
74 | if key == "Enter" {
75 | self?.guess()
76 | } else if key == "Backspace" {
77 | self?.delete()
78 | } else if let character = key.first,
79 | alphabet.contains(character) {
80 | self?.enter(character)
81 | }
82 | return .undefined
83 | })
84 | }
85 |
86 | var timers = [ObjectIdentifier:JSTimer]()
87 | func showMessage(_ message: String) {
88 | messages.append(message)
89 | var id: ObjectIdentifier?
90 | let timer = JSTimer(millisecondsDelay: 2000) { [weak self] in
91 | guard let self = self else { return }
92 | self.messages.removeLast()
93 | guard let id = id else { return }
94 | self.timers.removeValue(forKey: id)
95 | }
96 | id = ObjectIdentifier(timer)
97 | timers[id!] = timer
98 | }
99 |
100 | func select(column: Int) {
101 | var selection = Selection(
102 | direction: .vertical,
103 | row: 0,
104 | column: column
105 | )
106 | activeInput = guesses.enumerated().map { (row, guessRow) in
107 | guessRow[column].last == grid[row][column] ? grid[row][column] : " "
108 | }
109 | while guesses.indices.contains(selection.row) && guesses[selection.row][column].last == grid[selection.row][column] {
110 | selection.row += 1
111 | }
112 |
113 | if self.selection == selection {
114 | self.selection = nil
115 | } else {
116 | self.selection = selection
117 | }
118 | }
119 |
120 | func select(row: Int) {
121 | var selection = Selection(
122 | direction: .horizontal,
123 | row: row,
124 | column: 0
125 | )
126 |
127 | activeInput = guesses[row].enumerated().map { (column, guesses) in
128 | guesses.last == grid[row][column] ? grid[row][column] : " "
129 | }
130 | while guesses.indices.contains(row) && guesses[row].indices.contains(selection.column) && guesses[row][selection.column].last == grid[row][selection.column] {
131 | selection.column += 1
132 | }
133 |
134 | if self.selection == selection {
135 | self.selection = nil
136 | } else {
137 | self.selection = selection
138 | }
139 | }
140 |
141 | func enter(_ character: Character) {
142 | let character = Character(String(character).uppercased())
143 |
144 | guard var selection = selection,
145 | guesses.indices.contains(selection.row),
146 | guesses[selection.row].indices.contains(selection.column)
147 | else { return }
148 | switch selection.direction {
149 | case .horizontal:
150 | activeInput[selection.column] = character
151 | selection.column += 1
152 | while guesses.indices.contains(selection.row) && guesses[selection.row].indices.contains(selection.column) && guesses[selection.row][selection.column].last == grid[selection.row][selection.column] {
153 | selection.column += 1
154 | }
155 | case .vertical:
156 | activeInput[selection.row] = character
157 | selection.row += 1
158 | while guesses.indices.contains(selection.row) && guesses[selection.row].indices.contains(selection.column) && guesses[selection.row][selection.column].last == grid[selection.row][selection.column] {
159 | selection.row += 1
160 | }
161 | }
162 |
163 | if self.selection == selection {
164 | self.selection = nil
165 | } else {
166 | self.selection = selection
167 | }
168 | }
169 |
170 | func delete() {
171 | guard var selection = selection
172 | else { return }
173 | let targetRow = selection.direction == .vertical ? selection.row - 1 : selection.row
174 | let targetColumn = selection.direction == .horizontal ? selection.column - 1 : selection.column
175 | guard guesses.indices.contains(targetRow),
176 | guesses[targetRow].indices.contains(targetColumn)
177 | else { return }
178 | switch selection.direction {
179 | case .horizontal:
180 | if guesses[targetRow][targetColumn].last != grid[targetRow][targetColumn] {
181 | activeInput[targetColumn] = " "
182 | }
183 | selection.column -= 1
184 | while guesses.indices.contains(targetRow) && guesses[targetRow].indices.contains(selection.column) && guesses[targetRow][selection.column].last == grid[targetRow][selection.column] {
185 | selection.column -= 1
186 | }
187 | activeInput[selection.column] = " "
188 | case .vertical:
189 | if guesses[targetRow][targetColumn].last != grid[targetRow][targetColumn] {
190 | activeInput[targetRow] = " "
191 | }
192 | selection.row -= 1
193 | while guesses.indices.contains(selection.row) && guesses[selection.row].indices.contains(targetColumn) && guesses[selection.row][targetColumn].last == grid[selection.row][targetColumn] {
194 | selection.row -= 1
195 | }
196 | activeInput[selection.row] = " "
197 | }
198 | self.selection = selection
199 | }
200 |
201 | /// Commit a guess.
202 | func guess() {
203 | guard wordlist.contains(String(activeInput)) else {
204 | showMessage("Not in word list")
205 | return
206 | }
207 | guard let selection = selection else { return }
208 | switch selection.direction {
209 | case .horizontal:
210 | guard guesses.indices.contains(selection.row)
211 | else { return }
212 | for column in 0..= Self.maxGuessesPerLetter {
243 | self.phase = .loss
244 | return
245 | }
246 | didWin = false
247 | }
248 | }
249 | }
250 | if didWin {
251 | self.phase = .win
252 | }
253 | }
254 |
255 | func lastGuess(row: Int, column: Int, useInput: Bool = true) -> Character {
256 | guard let selection = selection else { return guesses[row][column].last ?? " " }
257 |
258 | let lastGuess = guesses[row][column].last ?? " "
259 |
260 | switch selection.direction {
261 | case .horizontal:
262 | if useInput && selection.row == row && lastGuess != grid[row][column] {
263 | return activeInput[column]
264 | } else {
265 | return lastGuess
266 | }
267 | case .vertical:
268 | if useInput && selection.column == column && lastGuess != grid[row][column] {
269 | return activeInput[row]
270 | } else {
271 | return lastGuess
272 | }
273 | }
274 | }
275 |
276 | /// The color for a grid tile at `row`/`column`.
277 | func color(row: Int, column: Int) -> Color {
278 | let guess = lastGuess(row: row, column: column, useInput: false)
279 |
280 | // No guess has been made, so fill with clear.
281 | if guess == " " {
282 | return .clear
283 | }
284 |
285 | let real = grid[row][column]
286 |
287 | // The guess was correct, so fill with green.
288 | if guess == real {
289 | return .green
290 | }
291 |
292 | switch selection?.direction {
293 | case .horizontal:
294 | guard selection?.row != row else { return .clear }
295 | case .vertical:
296 | guard selection?.column != column else { return .clear }
297 | default:
298 | break
299 | }
300 |
301 | var guessOccurrences = 0
302 | for rowIndex in 0.. row || (rowIndex == row && columnIndex >= column) {
310 | continue
311 | }
312 | if lastGuess == guess {
313 | guessOccurrences += 1
314 | }
315 | }
316 | }
317 | return guessOccurrences < occurrences[guess, default: 0] ? .yellow : .secondary
318 | }
319 |
320 | /// The color for a share tile, indicating the number of guesses taken for each.
321 | func resultsColor(row: Int, column: Int) -> Color {
322 | let guesses = guesses[row][column]
323 | if guesses.last != grid[row][column] {
324 | return .red
325 | } else if guesses.count <= 2 {
326 | return .green
327 | } else if guesses.count <= 3 {
328 | return .yellow
329 | } else {
330 | return .secondary
331 | }
332 | }
333 |
334 | /// The color for a key given the current selection.
335 | func color(for letter: Character) -> Color? {
336 | let letter = Character(String(letter).uppercased())
337 |
338 | // If we have a selection,
339 | // disable the key when we previously guessed this letter here but it was wrong.
340 | if let selection = selection,
341 | grid.indices.contains(selection.row),
342 | grid[selection.row].indices.contains(selection.column),
343 | guesses[selection.row][selection.column].contains(letter) && grid[selection.row][selection.column] != letter {
344 | return .disabled
345 | }
346 |
347 | var isContained = false
348 | var wasGuessed = false
349 | // Check each coordinate
350 | for row in 0..: View {
6 | @Binding var isOn: Bool
7 | @ViewBuilder let label: Label
8 |
9 | var body: some View {
10 | Button(action: { isOn.toggle() }) {
11 | HStack(spacing: 8) {
12 | Image(isOn ? "checkmark.svg" : "xmark.svg")
13 | .resizable()
14 | .frame(width: 15, height: 15)
15 | .frame(width: 20, height: 20)
16 | .background(isOn ? .green : .secondary, in: RoundedRectangle(cornerRadius: 5))
17 | label
18 | .foregroundStyle(.secondary)
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Griddle/Views/GameSummaryView.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 | import JavaScriptKit
3 | import Foundation
4 |
5 | /// Modal content for a breakdown of the final board.
6 | /// The colors indicate how many guesses it took the player to solve a given tile.
7 | struct GameSummaryView: View {
8 | @ObservedObject var manager: GameManager
9 | @AppStorage("includeURL") private var includeURL = true
10 |
11 | @StateObject private var timer = Timer()
12 | final class Timer: ObservableObject {
13 | private var timer: JSTimer!
14 | @Published var now = JSDate()
15 |
16 | init() {
17 | self.timer = JSTimer(millisecondsDelay: 1000, isRepeating: true) { [weak self] in
18 | self?.now = JSDate()
19 | }
20 | }
21 | }
22 |
23 | var body: some View {
24 | VStack(alignment: .leading) {
25 | if case .win = manager.phase {
26 | Text("Nice work!")
27 | .font(.largeTitle)
28 | .bold()
29 | } else {
30 | Text("Oh no!")
31 | .font(.largeTitle)
32 | .bold()
33 | }
34 | Text("Today's Grid")
35 | .foregroundStyle(.secondary)
36 | HStack(alignment: .top) {
37 | Grid(horizontalSpacing: 2, verticalSpacing: 2).callAsFunction {
38 | ForEach(Array(manager.grid.enumerated()), id: \.offset) { row in
39 | GridRow {
40 | ForEach(Array(row.element.enumerated()), id: \.offset) { letter in
41 | let guesses = manager.guesses[row.offset][letter.offset].count
42 | Text(String(letter.element))
43 | .font(.title3)
44 | .bold()
45 | .foregroundColor(.white)
46 | .frame(width: 40, height: 40)
47 | .background(manager.resultsColor(row: row.offset, column: letter.offset))
48 | }
49 | }
50 | }
51 | }
52 | Legend()
53 | }
54 | .padding(.vertical)
55 | HStack {
56 | VStack(alignment: .leading) {
57 | Text("Next Griddle")
58 | .font(.caption)
59 | .foregroundColor(.secondary)
60 | Text(formattedCountdown)
61 | .font(.title)
62 | .bold()
63 | }
64 | .padding(.leading)
65 | .frame(maxWidth: .infinity, alignment: .leading)
66 | // FIXME: Use `Divider` when fiber supports it
67 | Rectangle()
68 | .fill(.quaternary)
69 | .frame(width: 1, height: 50)
70 | VStack(alignment: .leading) {
71 | Keyboard.Key(
72 | label: Text("share your grid")
73 | .foregroundColor(.white),
74 | padding: 8,
75 | action: { manager.share(includeURL: includeURL) },
76 | fill: .green
77 | )
78 | .frame(width: 125)
79 | Checkbox(isOn: $includeURL) {
80 | Text(includeURL ? "with site link" : "without site link")
81 | .font(.caption)
82 | }
83 | }
84 | }
85 | }
86 | .frame(maxWidth: 300)
87 | }
88 |
89 | var formattedCountdown: String {
90 | let remaining = (Day.next.valueOf() - timer.now.valueOf()) / 1000
91 | let h = remaining / 3600
92 | let m = remaining.truncatingRemainder(dividingBy: 3600) / 60
93 | let s = remaining.truncatingRemainder(dividingBy: 3600).truncatingRemainder(dividingBy: 60)
94 | return (h < 10 ? "0\(Int(h))" : "\(Int(h))")
95 | + ":" + (m < 10 ? "0\(Int(m))" : "\(Int(m))")
96 | + ":" + (s < 10 ? "0\(Int(s))" : "\(Int(s))")
97 | }
98 |
99 | struct Legend: View {
100 | var body: some View {
101 | VStack(alignment: .leading) {
102 | color(.green, label: "2 or less guesses")
103 | color(.yellow, label: "3 or less guesses")
104 | color(.primary.opacity(0.5), label: "more than 3 guesses")
105 | }
106 | }
107 |
108 | func color(_ color: Color, label: String) -> some View {
109 | HStack {
110 | Rectangle()
111 | .fill(color)
112 | .frame(width: 15, height: 15)
113 | Text(label)
114 | .font(.caption2)
115 | .foregroundColor(.secondary)
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Sources/Griddle/Views/GameView.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import TokamakShim
3 | import JavaScriptKit
4 |
5 | /// The main game view, consisting of a grid and keyboard for entering letters.
6 | struct GameView: View {
7 | @StateObject private var manager: GameManager
8 |
9 | @Binding var seenTutorial: Bool
10 | @State private var showTutorial = false
11 |
12 | init(seenTutorial: Binding) {
13 | self._manager = .init(wrappedValue: GameManager())
14 | self._seenTutorial = seenTutorial
15 | self._showTutorial = .init(wrappedValue: !seenTutorial.wrappedValue)
16 | }
17 |
18 | var body: some View {
19 | VStack {
20 | VStack {
21 | Text("Griddle")
22 | .font(.largeTitle.weight(.heavy))
23 | // FIXME: `Link` is not yet supported.
24 | HTML("a", ["href": "https://github.com/carson-katri/griddle"]) {
25 | Text("view on GitHub")
26 | .underline()
27 | .font(.caption2)
28 | .foregroundColor(.secondary)
29 | }
30 | }
31 | .padding(.vertical)
32 | .frame(maxWidth: .infinity)
33 | .overlay(alignment: .trailing) {
34 | Keyboard.Key(label: Text("?"), padding: 5, action: { showTutorial = true }, fill: .quaternary)
35 | .frame(width: 28)
36 | .padding(.trailing)
37 | }
38 |
39 | VStack {
40 | Spacer()
41 | .frame(maxHeight: .infinity)
42 |
43 | Grid(horizontalSpacing: 4, verticalSpacing: 4).callAsFunction {
44 | GridRow {
45 | Color.clear
46 | .gridCellUnsizedAxes([.horizontal, .vertical])
47 | ForEach(0.. ()
154 |
155 | var body: some View {
156 | Button(action: action) {
157 | ellipsis
158 | .frame(maxWidth: axis == .vertical ? .infinity : nil, maxHeight: axis == .horizontal ? .infinity : nil)
159 | .background {
160 | Group {
161 | Rectangle()
162 | .fill(isActive ? Color.primary.opacity(0.2) : .clear)
163 | Rectangle()
164 | .stroke(.quaternary, lineWidth: 2)
165 | }
166 | }
167 | }
168 | .gridCellUnsizedAxes([.horizontal, .vertical])
169 | }
170 |
171 | @ViewBuilder
172 | var ellipsis: some View {
173 | let ellipsisLayout = axis == .vertical ? AnyLayout(HStack()) : AnyLayout(VStack())
174 | ellipsisLayout.callAsFunction {
175 | ForEach(0..<3) { _ in
176 | Circle()
177 | .fill(.secondary)
178 | .frame(width: 5, height: 5)
179 | }
180 | }
181 | .padding(axis == .vertical ? .vertical : .horizontal)
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/Sources/Griddle/Views/HowToPlay.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 | import Foundation
3 |
4 | /// Modal content with explainer text for the game.
5 | struct HowToPlay: View {
6 | let onClose: () -> ()
7 |
8 | var body: some View {
9 | VStack(alignment: .leading) {
10 | Text("How to Play")
11 | .font(.title)
12 | .bold()
13 | Group {
14 | Text("""
15 | Each row/column forms a word.
16 | Find all six words to win.
17 |
18 | You can make up to 5 guesses per tile.
19 |
20 | """)
21 | Text("Tap on a selector to enter a row/column.")
22 | Text("Guesses are made on a single row/column at a time.")
23 | AxisButton(axis: .vertical, isActive: false, action: {})
24 | .frame(width: 70)
25 | Text("Once it's active, you can start typing.")
26 | Text("Tap enter to confirm the row/column you selected.")
27 | Text("The colors of the tiles will indicate how close you are.")
28 | .padding(.bottom)
29 | }
30 | .font(.caption)
31 |
32 |
33 | Text("Examples")
34 | .font(.headline)
35 | .padding(.bottom, 4)
36 | VStack(alignment: .leading, spacing: 16) {
37 | ExampleRow(
38 | word: [("H", .green), ("E", .clear), ("Y", .clear)],
39 | caption: """
40 | Green means 'H' is correct.
41 | You cannot change a green letter.
42 | """
43 | )
44 | ExampleRow(
45 | word: [("B", .clear), ("A", .yellow), ("T", .clear)],
46 | caption: "Yellow means 'A' goes somewhere else in the grid."
47 | )
48 | ExampleRow(
49 | word: [("F", .clear), ("U", .clear), ("N", Color.secondary)],
50 | caption: "Gray means 'N' is not in the word."
51 | )
52 | }
53 | .padding(.bottom)
54 |
55 | Text("""
56 | Each guess does not need to make a fully valid grid.
57 | If you make more than 5 guesses in a tile, you lose.
58 | """)
59 | .font(.caption)
60 |
61 | Keyboard.Key(
62 | label: Text("start playing")
63 | .foregroundColor(.white),
64 | padding: 8,
65 | action: onClose,
66 | fill: .green
67 | )
68 | }
69 | .frame(maxWidth: 300, alignment: .leading)
70 | }
71 |
72 | /// An example of a single row of tiles.
73 | struct ExampleRow: View {
74 | let word: [(letter: Character, color: Color)]
75 | let caption: String
76 |
77 | var body: some View {
78 | VStack(alignment: .leading, spacing: 0) {
79 | HStack(spacing: 4) {
80 | ForEach(word, id: \.letter) { letter in
81 | Tile(letter.letter, fill: letter.color)
82 | .frame(width: 35, height: 35)
83 | }
84 | }
85 | .font(.title3.bold())
86 | Text(caption)
87 | .font(.caption2)
88 | .foregroundColor(.secondary)
89 | .padding(.top, 2)
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Sources/Griddle/Views/Keyboard.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 | import Foundation
3 |
4 | /// An interactive keyboard, with an enter button on the left and delete button on the right.
5 | ///
6 | /// The grid contains 20 columns so the second row can be offset by half a key (it is one key shorter than the first row).
7 | /// Each key spans 2 columns, and the action buttons span 3.
8 | struct Keyboard: View {
9 | static let rows = [
10 | "qwertyuiop",
11 | "asdfghjkl",
12 | "zxcvbnm"
13 | ]
14 |
15 | @ObservedObject var manager: GameManager
16 |
17 | let onTapLetter: (Character) -> ()
18 | let onEnter: () -> ()
19 | let onDelete: () -> ()
20 |
21 | var body: some View {
22 | Grid(horizontalSpacing: 2, verticalSpacing: 2).callAsFunction {
23 | ForEach(Self.rows, id: \.self) { row in
24 | GridRow {
25 | if row.starts(with: "a") {
26 | // The second row of letters is one key shorter than the first,
27 | // so offset it one grid column with an unsized clear color.
28 | Color.clear
29 | .gridCellUnsizedAxes(.vertical)
30 | } else if row.starts(with: "z") {
31 | // The last row contains an enter button on the left.
32 | Key(label: Text("enter"), action: onEnter, fill: .quaternary)
33 | .gridCellColumns(3)
34 | .gridCellUnsizedAxes(.horizontal)
35 | }
36 | ForEach(Array(row), id: \.self) { letter in
37 | // Resolved color for this key, or the default quaternary hierarchical style.
38 | // FIXME: When `AnyShapeStyle` is available in Tokamak, use `HierarchicalShapeStyle.quaternary` directly instead of recreating it from `Color.primary`.
39 | let color = manager.color(for: letter) ?? Color.primary.opacity(0.2)
40 | Key(
41 | label: Text(String(letter))
42 | .foregroundColor(
43 | // Use a white foreground color on a colorful fill.
44 | color == .disabled || color == Color.primary.opacity(0.2)
45 | ? nil
46 | : .white
47 | ),
48 | action: { onTapLetter(letter) },
49 | fill: color
50 | )
51 | .gridCellColumns(2)
52 | }
53 | if row.starts(with: "z") {
54 | // The last row contains a delete button on the right.
55 | Key(label: Text("delete"), action: onDelete, fill: .quaternary)
56 | .gridCellColumns(3)
57 | .gridCellUnsizedAxes(.horizontal)
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
64 | struct Key: View {
65 | let label: Text
66 | var padding: CGFloat = 15
67 | let action: () -> ()
68 | let fill: Fill
69 |
70 | var body: some View {
71 | Button(action: action) {
72 | label
73 | .font(.system(size: 14))
74 | .fontWeight(.medium)
75 | .frame(maxWidth: .infinity)
76 | .padding(.vertical, padding)
77 | .background(fill, in: RoundedRectangle(cornerRadius: 5))
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Sources/Griddle/Views/ModalView.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 | import Foundation
3 |
4 | /// A custom modal UI. This could perhaps use the `dialog` tag when available in Tokamak.
5 | struct ModalView: View {
6 | @ViewBuilder let content: Content
7 |
8 | var body: some View {
9 | Color.black.opacity(0.25)
10 | .overlay {
11 | content
12 | .padding()
13 | .background(Color(white: 1), in: RoundedRectangle(cornerRadius: 5))
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Griddle/Views/Tile.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 |
3 | /// A single letter in the grid with a given fill color or stoke.
4 | ///
5 | /// Different fill colors have specific meanings:
6 | /// * Green - the tile is correct.
7 | /// * Yellow - the tile is somewhere in the grid.
8 | /// * Gray - the tile is not in the grid.
9 | ///
10 | /// When active, the stroke prominence is increased.
11 | struct Tile: View {
12 | let letter: Character
13 | let fill: Color
14 | let isActive: Bool
15 |
16 | init(_ letter: Character, fill: Color = .clear, isActive: Bool = false) {
17 | self.letter = letter
18 | self.fill = fill
19 | self.isActive = isActive
20 | }
21 |
22 | var body: some View {
23 | Text(String(letter))
24 | .foregroundColor(fill == .clear ? nil : Color.white)
25 | .frame(maxWidth: .infinity, maxHeight: .infinity)
26 | .background(fill)
27 | .overlay {
28 | Rectangle()
29 | .stroke(
30 | isActive ? .secondary : .tertiary,
31 | lineWidth: fill != .clear ? 0 :
32 | isActive ? 4 : 2
33 | )
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Griddle/checkmark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Sources/Griddle/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carson-katri/griddle/209444639e7c7f572ca1315920eaf2e82407ae97/Sources/Griddle/favicon.ico
--------------------------------------------------------------------------------
/Sources/Griddle/main.swift:
--------------------------------------------------------------------------------
1 | import TokamakShim
2 | import JavaScriptKit
3 | import JavaScriptEventLoop
4 | import Foundation
5 |
6 | JSObject.global.document.object!.body.innerHTML = .string("")
7 |
8 | struct GriddleApp: App {
9 | #if os(WASI)
10 | static let _configuration = _AppConfiguration(reconciler: .fiber(useDynamicLayout: true))
11 | #endif
12 |
13 | @AppStorage("seenTutorial") var seenTutorial = false
14 |
15 | var body: some Scene {
16 | WindowGroup("Griddle") {
17 | GameView(seenTutorial: $seenTutorial)
18 | }
19 | }
20 | }
21 |
22 | extension Character: Codable {
23 | public init(from decoder: Decoder) throws {
24 | let container = try decoder.singleValueContainer()
25 | self = try Character(container.decode(String.self))
26 | }
27 |
28 | public func encode(to encoder: Encoder) throws {
29 | var container = encoder.singleValueContainer()
30 | try container.encode(String(self))
31 | }
32 | }
33 |
34 | var style = JSObject.global.document.createElement("style")
35 | style.innerHTML = .string("button { border: none; background: none; padding: none; margin: none; touch-action: manipulation; }")
36 | _ = JSObject.global.document.head.appendChild(style)
37 |
38 | var title = JSObject.global.document.createElement("title")
39 | title.innerHTML = .string("Griddle")
40 | _ = JSObject.global.document.head.appendChild(title)
41 |
42 | var favicon = JSObject.global.document.createElement("link")
43 | favicon.rel = .string("icon")
44 | favicon.type = .string("image/x-icon")
45 | favicon.href = .string("favicon.ico")
46 | _ = JSObject.global.document.head.appendChild(favicon)
47 |
48 | JavaScriptEventLoop.installGlobalExecutor()
49 |
50 | GriddleApp.main()
51 |
--------------------------------------------------------------------------------
/Sources/Griddle/xmark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Tests/GriddleTests/GriddleTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import Griddle
3 |
4 | final class GriddleTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | XCTAssertEqual(Griddle().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
69 |
70 |
71 |
72 |
73 |
83 |
110 |
111 |
112 |
113 |
123 |
124 |
125 |
126 |
127 |
128 |
138 |
165 |
166 |
167 |
168 |
178 |
179 |
180 |
181 |
182 |
183 |
193 |
220 |
221 |
222 |
223 |
233 |
234 |
235 |
236 |
237 |
238 |
248 |
275 |
276 |
277 |
278 |
288 |
289 |
290 |
291 |
292 |
293 |
303 |
330 |
331 |
332 |
333 |
343 |
344 |
345 |
346 |
347 |
348 |
358 |
385 |
386 |
387 |
388 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
--------------------------------------------------------------------------------