Have got win and failures working
This commit is contained in:
		
							parent
							
								
									d9fa154a01
								
							
						
					
					
						commit
						a8e42da6fd
					
				|  | @ -2,21 +2,32 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"flag" | ||||
| 	"log" | ||||
| 	"math/rand/v2" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type wordList struct { | ||||
| 	Words map[int][]string `json:"words"` | ||||
| } | ||||
| 
 | ||||
| type shufflePattern struct { | ||||
| 	Index map[int][]int `json:"index"` | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	dictFile := flag.String("dict", "", "dictionary of word to prep") | ||||
| 	dictFile := flag.String("dict", "./dict/en_GB.dic", "dictionary of word to prep") | ||||
| 	outDir := flag.String("out", "./site/assets/data", "output directory") | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), uint64(time.Now().UnixNano()))) | ||||
| 
 | ||||
| 	words := wordList{ | ||||
| 		Words: make(map[int][]string), | ||||
| 	} | ||||
|  | @ -29,11 +40,42 @@ func main() { | |||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, word := range words.Words { | ||||
| 	for k, word := range words.Words { | ||||
| 		log.Printf("Found %d words of length %v", len(word), k) | ||||
| 		sort.Strings(word) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := json.NewEncoder(os.Stdout).Encode(words); err != nil { | ||||
| 	var wordData bytes.Buffer | ||||
| 	if err := json.NewEncoder(&wordData).Encode(words); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	if err := os.WriteFile(filepath.Join(*outDir, "words.json"), wordData.Bytes(), 0644); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate a shuffle pattern
 | ||||
| 	shp := shufflePattern{Index: make(map[int][]int)} | ||||
| 	for k := range words.Words { | ||||
| 		pattern := make([]int, len(words.Words[k])) | ||||
| 		for i := range words.Words[k] { | ||||
| 			pattern[i] = i | ||||
| 		} | ||||
| 
 | ||||
| 		for x := 4; x < r.IntN(8)+4; x++ { | ||||
| 			r.Shuffle(len(pattern), func(i, j int) { | ||||
| 				pattern[i], pattern[j] = pattern[j], pattern[i] | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO: shuffle
 | ||||
| 		shp.Index[k] = pattern | ||||
| 	} | ||||
| 
 | ||||
| 	var patternData bytes.Buffer | ||||
| 	if err := json.NewEncoder(&patternData).Encode(shp); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	if err := os.WriteFile(filepath.Join(*outDir, "shuffle_pattern.json"), patternData.Bytes(), 0644); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										1
									
								
								site/assets/data/shuffle_pattern.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								site/assets/data/shuffle_pattern.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -33,6 +33,16 @@ export default class extends Controller { | |||
|       this.playfieldOutlet.tappedKey(key); | ||||
|   } | ||||
| 
 | ||||
|   tapEnter(ev) { | ||||
|     ev.preventDefault(); | ||||
|     this.playfieldOutlet.enterGuess(); | ||||
|   } | ||||
| 
 | ||||
|   tapBackspace(ev) { | ||||
|     ev.preventDefault(); | ||||
|     this.playfieldOutlet.tappedBackspace(); | ||||
|   } | ||||
|      | ||||
|   resetKeyColors(ev) { | ||||
|     for (let keyElement of this.keyTargets) { | ||||
|       keyElement.classList.value = ""; | ||||
|  |  | |||
							
								
								
									
										18
									
								
								site/assets/scripts/controllers/overlay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								site/assets/scripts/controllers/overlay.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| import { Controller } from "https://unpkg.com/@hotwired/stimulus@v3.2.2/dist/stimulus.js" | ||||
| 
 | ||||
| export default class extends Controller { | ||||
|     static targets = ["message"]; | ||||
| 
 | ||||
|     showMessage(msg) { | ||||
|         this.messageTarget.innerText = msg; | ||||
|         this.element.classList.add("show"); | ||||
| 
 | ||||
|         if (this._waitTimer) { | ||||
|             clearTimeout(this._waitTimer); | ||||
|         } | ||||
| 
 | ||||
|         this._waitTimer = setTimeout(() => { | ||||
|             this.element.classList.remove("show"); | ||||
|         }, 3000); | ||||
|     } | ||||
| } | ||||
|  | @ -5,7 +5,8 @@ import { WordSource } from "../models/words.js"; | |||
| 
 | ||||
| 
 | ||||
| export default class extends Controller { | ||||
|   static targets = ["row"]; | ||||
|   static targets = ["row", "playfield", "topMessage", "nextPuzzleButton"]; | ||||
|   static outlets = ["overlay"]; | ||||
|    | ||||
|   async connect() { | ||||
|     this._wordSource = new WordSource(); | ||||
|  | @ -64,20 +65,30 @@ export default class extends Controller { | |||
|      | ||||
|     switch (results.guessResult) { | ||||
|     case GUESS_RESULT.FOUL: | ||||
|       console.log("not a word!"); | ||||
|       this.overlayOutlet.showMessage("Not a valid word."); | ||||
| 
 | ||||
|       rowElem.replaceWith(this._buildPlayfieldRow(this._gameController.wordLength())); | ||||
|       this._activeLetter = 0; | ||||
|       break; | ||||
|     case GUESS_RESULT.MISS: | ||||
|       console.log("try again!"); | ||||
|        | ||||
|       this._colorizeRow(rowElem, results); | ||||
| 
 | ||||
|       this._activeRowIndex += 1; | ||||
|       this._activeLetter = 0; | ||||
|       if (this._activeRowIndex >= this._gameController.guesses()) { | ||||
|         this.topMessageTarget.innerText = this._gameController.currentWord().toUpperCase(); | ||||
|         this.nextPuzzleButtonTarget.classList.remove("hide"); | ||||
|       } else { | ||||
|         this._activeLetter = 0; | ||||
|       } | ||||
| 
 | ||||
|       break; | ||||
|     case GUESS_RESULT.WIN: | ||||
|       console.log("CORRECT!"); | ||||
|       this._colorizeRow(rowElem, results); | ||||
| 
 | ||||
|       this.topMessageTarget.innerText = "Hooray! You did it."; | ||||
|       this.nextPuzzleButtonTarget.classList.remove("hide"); | ||||
| 
 | ||||
|       /* | ||||
|       if (this._gameController.nextWord()) { | ||||
|         this._buildPlayfield(); | ||||
|       } else { | ||||
|  | @ -85,10 +96,20 @@ export default class extends Controller { | |||
|         this._activeRowIndex = -1; | ||||
|         this._colorizeRow(rowElem, results); | ||||
|       } | ||||
|        */ | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async loadNextPuzzle(ev) { | ||||
|     ev.preventDefault(); | ||||
|     if (await this._gameController.nextWord()) { | ||||
|       this._buildPlayfield(); | ||||
|     } else { | ||||
|       this.overlayOutlet.showMessage("No more words available."); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   _buildPlayfield() { | ||||
|     let rows = this._gameController.guesses(); | ||||
|     let wordLength = this._gameController.wordLength(); | ||||
|  | @ -101,7 +122,10 @@ export default class extends Controller { | |||
|       newRows.push(this._buildPlayfieldRow(wordLength)); | ||||
|     } | ||||
|      | ||||
|     this.element.replaceChildren.apply(this.element, newRows); | ||||
|     this.playfieldTarget.replaceChildren.apply(this.playfieldTarget, newRows); | ||||
| 
 | ||||
|     this.topMessageTarget.innerHTML = " " | ||||
|     this.nextPuzzleButtonTarget.classList.add("hide"); | ||||
|      | ||||
|     window.dispatchEvent(new CustomEvent("resetKeyColors")); | ||||
|   } | ||||
|  |  | |||
|  | @ -2,9 +2,11 @@ import { Application, Controller } from "https://unpkg.com/@hotwired/stimulus@v3 | |||
| 
 | ||||
| import PlayfieldController from "./controllers/playfield.js"; | ||||
| import KeyboardController from "./controllers/keyboard.js"; | ||||
| import OverlayController from "./controllers/overlay.js"; | ||||
| 
 | ||||
| 
 | ||||
| window.Stimulus = Application.start(); | ||||
| 
 | ||||
| Stimulus.register("playfield", PlayfieldController); | ||||
| Stimulus.register("keyboard", KeyboardController); | ||||
| Stimulus.register("overlay", OverlayController); | ||||
|  | @ -32,6 +32,11 @@ export class GameController { | |||
|     return this._guesses; | ||||
|   } | ||||
| 
 | ||||
|   currentWord() { | ||||
|     this._checkHasStarted(); | ||||
|     return this._currentWord; | ||||
|   } | ||||
|    | ||||
|   async nextWord() { | ||||
|     this._currentWord = await this._wordSource.getCurrentWord(); | ||||
|     this._guesses = 5; | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ function binSearch(list, word) { | |||
| export class WordSource { | ||||
|     constructor() { | ||||
|         this._wordData = null; | ||||
|         this._pattern = null; | ||||
|         this._currentWord = null; | ||||
|     } | ||||
| 
 | ||||
|  | @ -38,7 +39,9 @@ export class WordSource { | |||
|         } | ||||
| 
 | ||||
|         let words = await this._fetchAllWordsIfNecessary(); | ||||
|         this._currentWord = words.words["4"][7]; | ||||
|         let idx = this._pattern.index["4"][7]; | ||||
| 
 | ||||
|         this._currentWord = words.words["4"][idx]; | ||||
| 
 | ||||
|         return this._currentWord; | ||||
|     } | ||||
|  | @ -57,8 +60,8 @@ export class WordSource { | |||
|             return this._wordData; | ||||
|         } | ||||
| 
 | ||||
|         let res = await fetch("/assets/data/words.json"); | ||||
|         this._wordData = await res.json(); | ||||
|         this._wordData = await (await fetch("/assets/data/words.json")).json(); | ||||
|         this._pattern = await (await fetch("/assets/data/shuffle_pattern.json")).json(); | ||||
|         return this._wordData; | ||||
|     } | ||||
| } | ||||
|  | @ -1,15 +1,90 @@ | |||
| :root { | ||||
|     --color-no-letter-bg: #666; | ||||
|     --color-no-letter-fg: #fff; | ||||
| 
 | ||||
|     --color-has-letter-bg: #dd4; | ||||
|     --color-has-letter-fg: #000; | ||||
| 
 | ||||
|     --color-right-letter-bg: #4b4; | ||||
|     --color-right-letter-fg: #fff; | ||||
| } | ||||
| 
 | ||||
| body { | ||||
|     height: 100vh; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| main { | ||||
|     flex-grow: 1; | ||||
|     flex-shrink: 1; | ||||
| 
 | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| main > div:first-child { | ||||
|     flex-grow: 1; | ||||
| } | ||||
| 
 | ||||
| div.playfield { | ||||
|     text-align: center; | ||||
|     font-size: 1.5rem; | ||||
| } | ||||
| 
 | ||||
| div.playfield div[data-playfield-target="topMessage"] { | ||||
|     font-weight: bold; | ||||
| } | ||||
| 
 | ||||
| div.playfield div[data-playfield-target="playfield"] { | ||||
|     margin-block: 1rem; | ||||
|     font-size: 3rem; | ||||
| } | ||||
| 
 | ||||
| div.keyboard > div { | ||||
|     display: flex; | ||||
|     justify-content: space-around; | ||||
| } | ||||
| 
 | ||||
| div.keyboard button { | ||||
|     width: 9vw; | ||||
|     border-radius: 2px; | ||||
|     height: 4rem; | ||||
|     text-transform: uppercase; | ||||
| 
 | ||||
|     background: #bbb; | ||||
|     color: #000; | ||||
|     border: solid 1px #444; | ||||
| } | ||||
| 
 | ||||
| div.keyboard > div:nth-child(2) { | ||||
|     margin-inline: 2vw; | ||||
| } | ||||
| 
 | ||||
| div.keyboard > div:nth-child(3) { | ||||
|     margin-inline: 4vw; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| button[data-keyboard-target="key"].right-pos { | ||||
|     background: green; | ||||
|     background: var(--color-right-letter-bg); | ||||
|     color: var(--color-right-letter-fg); | ||||
| } | ||||
| 
 | ||||
| button[data-keyboard-target="key"].right-char { | ||||
|     background: yellow; | ||||
|     background: var(--color-has-letter-bg); | ||||
|     color: var(--color-has-letter-fg); | ||||
| } | ||||
| 
 | ||||
| button[data-keyboard-target="key"].miss { | ||||
|     background: grey; | ||||
|     background: var(--color-no-letter-bg); | ||||
|     color: var(--color-no-letter-fg); | ||||
| } | ||||
| 
 | ||||
| div.playfield div.row { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
| } | ||||
| 
 | ||||
| div.playfield div.row span { | ||||
|     display: inline-block; | ||||
|  | @ -17,16 +92,54 @@ div.playfield div.row span { | |||
|      | ||||
|     height: 1.1em; | ||||
|     width: 1.1em; | ||||
|     line-height: 1.1em; | ||||
|     margin: 5px; | ||||
| } | ||||
| 
 | ||||
| div.playfield div.row span.right-pos { | ||||
|     background: green; | ||||
|     background: var(--color-right-letter-bg); | ||||
|     color: var(--color-right-letter-fg); | ||||
| } | ||||
| 
 | ||||
| div.playfield div.row span.right-char { | ||||
|     background: yellow; | ||||
|     background: var(--color-has-letter-bg); | ||||
|     color: var(--color-has-letter-fg); | ||||
| } | ||||
| 
 | ||||
| div.playfield div.row span.miss { | ||||
|     background: grey; | ||||
|     background: var(--color-no-letter-bg); | ||||
|     color: var(--color-no-letter-fg); | ||||
| } | ||||
| 
 | ||||
| div.overlay { | ||||
|     position: fixed; | ||||
|     bottom: 25%; | ||||
|     left: 10%; | ||||
|     right: 10%; | ||||
|     z-index: 10; | ||||
| 
 | ||||
|     display: none; | ||||
|     justify-content: center; | ||||
| } | ||||
| 
 | ||||
| div.overlay.show { | ||||
|     display: flex; | ||||
| } | ||||
| 
 | ||||
| div.overlay-message { | ||||
|     font-size: 2rem; | ||||
|     line-height: 2.2rem; | ||||
| 
 | ||||
|     text-align: center; | ||||
| 
 | ||||
|     color: white; | ||||
|     background-color: rgba(0, 0, 0, 70%); | ||||
|     border-radius: 5px; | ||||
| 
 | ||||
|     padding-block: 12px; | ||||
|     padding-inline: 20px; | ||||
| } | ||||
| 
 | ||||
| .hide { | ||||
|     display: none; | ||||
| } | ||||
|  | @ -3,6 +3,7 @@ | |||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css"> | ||||
|     <link href="/assets/styles/main.css" rel="stylesheet"> | ||||
|     <title>Wordle Clone</title> | ||||
| </head> | ||||
|  | @ -12,11 +13,15 @@ | |||
|   </header> | ||||
| 
 | ||||
|   <main> | ||||
|       <div data-controller="playfield" class="playfield">                 | ||||
|       <div class="playfield" data-controller="playfield" | ||||
|            data-playfield-overlay-outlet=".overlay"> | ||||
| 
 | ||||
|           <div data-playfield-target="topMessage"> </div> | ||||
|           <div data-playfield-target="playfield"></div> | ||||
| 
 | ||||
|           <button data-playfield-target="nextPuzzleButton" class="hide" data-action="playfield#loadNextPuzzle">Next Puzzle</button> | ||||
|       </div> | ||||
|        | ||||
|       <div data-controller="keyboard"  | ||||
|       <div class="keyboard" data-controller="keyboard" | ||||
|            data-keyboard-playfield-outlet=".playfield" | ||||
|            data-action=" | ||||
|               keydown@window->keyboard#onKeyPress | ||||
|  | @ -47,6 +52,7 @@ | |||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="l">l</button> | ||||
|           </div> | ||||
|           <div> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tapBackspace">←</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="z">z</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="x">x</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="c">c</button> | ||||
|  | @ -54,8 +60,7 @@ | |||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="b">b</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="n">n</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tappedKey" data-key="m">m</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tapEnter">enter</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tapBackspace">back</button> | ||||
|               <button data-keyboard-target="key" data-action="keyboard#tapEnter">↵</button> | ||||
|           </div> | ||||
|       </div> | ||||
| 
 | ||||
|  | @ -70,6 +75,10 @@ | |||
|       </template> | ||||
|   </main> | ||||
| 
 | ||||
|   <div class="overlay" data-controller="overlay"> | ||||
|       <div class="overlay-message" data-overlay-target="message">I am the overlay</div> | ||||
|   </div> | ||||
| 
 | ||||
|   <script src="/assets/scripts/main.js" type="module"></script> | ||||
| </body> | ||||
| </html> | ||||
		Loading…
	
		Reference in a new issue