├── .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 | A screenshot of the game 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 |
15 |
16 |
17 |
18 |
26 | 27 |
28 |
29 |
43 |
G
53 |
54 |
55 |
56 |
57 |
58 |
66 | 67 |
68 |
69 |
70 |
71 |
72 |
73 |
81 | 82 |
83 |
84 |
98 |
R
108 |
109 |
110 |
111 |
112 |
113 |
121 | 122 |
123 |
124 |
125 |
126 |
127 |
128 |
136 | 137 |
138 |
139 |
153 |
I
163 |
164 |
165 |
166 |
167 |
168 |
176 | 177 |
178 |
179 |
180 |
181 |
182 |
183 |
191 | 192 |
193 |
194 |
208 |
D
218 |
219 |
220 |
221 |
222 |
223 |
231 | 232 |
233 |
234 |
235 |
236 |
237 |
238 |
246 | 247 |
248 |
249 |
263 |
D
273 |
274 |
275 |
276 |
277 |
278 |
286 | 287 |
288 |
289 |
290 |
291 |
292 |
293 |
301 | 302 |
303 |
304 |
318 |
L
328 |
329 |
330 |
331 |
332 |
333 |
341 | 342 |
343 |
344 |
345 |
346 |
347 |
348 |
356 | 357 |
358 |
359 |
373 |
E
383 |
384 |
385 |
386 |
387 |
388 |
396 | 397 |
398 |
399 |
400 |
401 |
402 | 403 | 404 | 405 | --------------------------------------------------------------------------------