Moved to an easier dictionary and added invalid word guesses back
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				/ publish (push) Successful in 1m19s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	/ publish (push) Successful in 1m19s
				
			Also added a spinner
This commit is contained in:
		
							parent
							
								
									36b079681c
								
							
						
					
					
						commit
						566f55ed12
					
				|  | @ -23,13 +23,11 @@ const maxWordLength = 7 | |||
| 
 | ||||
| var validWordRegex = regexp.MustCompile(`^[a-z]+$`) | ||||
| 
 | ||||
| type wordList struct { | ||||
| 	Words map[int][]string `json:"words"` | ||||
| } | ||||
| 
 | ||||
| type shufflePattern struct { | ||||
| 	ID    string        `json:"id"` | ||||
| 	Index map[int][]int `json:"index"` | ||||
| type dataStruct struct { | ||||
| 	VersionID      string           `json:"versionId"` | ||||
| 	GuessWords     map[int][]string `json:"guessWords"` | ||||
| 	OtherWords     map[int][]string `json:"otherWords"` | ||||
| 	ShufflePattern map[int][]int    `json:"shufflePattern"` | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
|  | @ -39,49 +37,52 @@ func main() { | |||
| 
 | ||||
| 	r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), uint64(time.Now().UnixNano()))) | ||||
| 
 | ||||
| 	words := wordList{ | ||||
| 		Words: make(map[int][]string), | ||||
| 	data := dataStruct{ | ||||
| 		VersionID:      gonanoid.MustID(12), | ||||
| 		GuessWords:     make(map[int][]string), | ||||
| 		OtherWords:     make(map[int][]string), | ||||
| 		ShufflePattern: make(map[int][]int), | ||||
| 	} | ||||
| 
 | ||||
| 	wordSet := make(map[string]bool) | ||||
| 	if err := scanSuitableWords(*dictFile, func(word string) { | ||||
| 	guessWords := make(map[string]bool) | ||||
| 	otherWords := make(map[string]bool) | ||||
| 
 | ||||
| 	if err := scanSuitableWords(*dictFile, func(easy bool, word string) { | ||||
| 		w := strings.TrimSpace(word) | ||||
| 		if !validWordRegex.MatchString(w) { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if len(w) >= 4 && len(w) <= maxWordLength { | ||||
| 			wordSet[word] = true | ||||
| 			if easy { | ||||
| 				guessWords[word] = true | ||||
| 			} else { | ||||
| 				otherWords[word] = true | ||||
| 			} | ||||
| 		} | ||||
| 	}); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	for w := range wordSet { | ||||
| 		words.Words[len(w)] = append(words.Words[len(w)], w) | ||||
| 	for w := range guessWords { | ||||
| 		data.GuessWords[len(w)] = append(data.GuessWords[len(w)], w) | ||||
| 	} | ||||
| 	for w := range otherWords { | ||||
| 		data.OtherWords[len(w)] = append(data.OtherWords[len(w)], w) | ||||
| 	} | ||||
| 
 | ||||
| 	for k, word := range words.Words { | ||||
| 		log.Printf("Found %d words of length %v", len(word), k) | ||||
| 	for k, word := range data.GuessWords { | ||||
| 		log.Printf("Found %d guess words of length %v", len(word), k) | ||||
| 		sort.Strings(word) | ||||
| 	} | ||||
| 	for k, word := range data.OtherWords { | ||||
| 		log.Printf("Found %d other words of length %v", len(word), k) | ||||
| 		sort.Strings(word) | ||||
| 	} | ||||
| 
 | ||||
| 	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{ | ||||
| 		ID:    gonanoid.MustID(12), | ||||
| 		Index: make(map[int][]int), | ||||
| 	} | ||||
| 	for k := range words.Words { | ||||
| 		pattern := make([]int, len(words.Words[k])) | ||||
| 		for i := range words.Words[k] { | ||||
| 	for k := range data.GuessWords { | ||||
| 		pattern := make([]int, len(data.GuessWords[k])) | ||||
| 		for i := range data.GuessWords[k] { | ||||
| 			pattern[i] = i | ||||
| 		} | ||||
| 
 | ||||
|  | @ -92,19 +93,19 @@ func main() { | |||
| 		} | ||||
| 
 | ||||
| 		// TODO: shuffle
 | ||||
| 		shp.Index[k] = pattern | ||||
| 		data.ShufflePattern[k] = pattern | ||||
| 	} | ||||
| 
 | ||||
| 	var patternData bytes.Buffer | ||||
| 	if err := json.NewEncoder(&patternData).Encode(shp); err != nil { | ||||
| 	var wordData bytes.Buffer | ||||
| 	if err := json.NewEncoder(&wordData).Encode(data); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	if err := os.WriteFile(filepath.Join(*outDir, "shuffle_pattern.json"), patternData.Bytes(), 0644); err != nil { | ||||
| 	if err := os.WriteFile(filepath.Join(*outDir, "data.json"), wordData.Bytes(), 0644); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func scanSuitableWords(dictDir string, withWord func(word string)) error { | ||||
| func scanSuitableWords(dictDir string, withWord func(easy bool, word string)) error { | ||||
| 	if err := scanSuitableWordsFromWordListHTML(filepath.Join(dictDir, "oxford-word-list.htm"), withWord); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | @ -120,7 +121,7 @@ func scanSuitableWords(dictDir string, withWord func(word string)) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func scanSuitableWordsFromWordListHTML(dictFile string, withWord func(word string)) error { | ||||
| func scanSuitableWordsFromWordListHTML(dictFile string, withWord func(easy bool, word string)) error { | ||||
| 	f, err := os.Open(dictFile) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|  | @ -133,12 +134,12 @@ func scanSuitableWordsFromWordListHTML(dictFile string, withWord func(word strin | |||
| 	} | ||||
| 
 | ||||
| 	dom.Find("table.t tbody tr td.t:nth-child(2)").Each(func(i int, s *goquery.Selection) { | ||||
| 		withWord(s.Text()) | ||||
| 		withWord(true, s.Text()) | ||||
| 	}) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func scanSuitableWordsFromOxford3000(dictFile string, withWord func(word string)) error { | ||||
| func scanSuitableWordsFromOxford3000(dictFile string, withWord func(easy bool, word string)) error { | ||||
| 	f, err := os.Open(dictFile) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|  | @ -147,20 +148,20 @@ func scanSuitableWordsFromOxford3000(dictFile string, withWord func(word string) | |||
| 
 | ||||
| 	scanner := bufio.NewScanner(f) | ||||
| 	for scanner.Scan() { | ||||
| 		withWord(scanner.Text()) | ||||
| 		withWord(true, scanner.Text()) | ||||
| 	} | ||||
| 
 | ||||
| 	return scanner.Err() | ||||
| } | ||||
| 
 | ||||
| func scanSuitableWordsFromEnGB(dictFile, affFile string, withWord func(word string)) error { | ||||
| func scanSuitableWordsFromEnGB(dictFile, affFile string, withWord func(easy bool, word string)) error { | ||||
| 	words, err := script.Exec(fmt.Sprintf("unmunch '%v' '%v'", dictFile, affFile)).String() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, word := range strings.Split(words, "\n") { | ||||
| 		withWord(word) | ||||
| 		withWord(false, word) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
							
								
								
									
										1
									
								
								site/assets/data/data.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								site/assets/data/data.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -5,7 +5,7 @@ import { WordSource } from "../models/words.js"; | |||
| 
 | ||||
| 
 | ||||
| export default class extends Controller { | ||||
|   static targets = ["row", "playfield", "topMessage", "nextPuzzleButtons"]; | ||||
|   static targets = ["row", "playfield", "topMessage", "nextPuzzleButtons", "loader"]; | ||||
|   static outlets = ["overlay"]; | ||||
| 
 | ||||
|   async connect() { | ||||
|  | @ -15,6 +15,8 @@ export default class extends Controller { | |||
|     await this._gameController.start(); | ||||
| 
 | ||||
|     this._buildPlayfield(); | ||||
| 
 | ||||
|     this.loaderTarget.classList.add("hide"); | ||||
|   } | ||||
| 
 | ||||
|   tappedKey(key) { | ||||
|  | @ -109,11 +111,16 @@ export default class extends Controller { | |||
| 
 | ||||
|   async loadNextPuzzle(ev) { | ||||
|     ev.preventDefault(); | ||||
| 
 | ||||
|     this.loaderTarget.classList.remove("hide"); | ||||
| 
 | ||||
|     if (await this._gameController.nextWord()) { | ||||
|       this._buildPlayfield(); | ||||
|     } else { | ||||
|       this.overlayOutlet.showMessage("No more words available."); | ||||
|     } | ||||
| 
 | ||||
|     this.loaderTarget.classList.add("hide"); | ||||
|   } | ||||
| 
 | ||||
|  _showWin() { | ||||
|  |  | |||
|  | @ -149,6 +149,18 @@ export class GameController { | |||
|     let misses = {}; | ||||
|     let hits = {}; | ||||
| 
 | ||||
|     if (!this._wordSource.isWord(guess)) { | ||||
|       hits = {}; | ||||
|       for (let i = 0; i < guess.length; i++) { | ||||
|         hits[guess[i]] = MARKERS.ATTEMPTED; | ||||
|       } | ||||
| 
 | ||||
|       return { | ||||
|         hits: hits, | ||||
|         guessResult: GUESS_RESULT.FOUL, | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
|     for (let i = 0; i < guess.length; i++) { | ||||
|       if (guess[i] == this._currentWord[i]) { | ||||
|         markers[i] = MARKERS.RIGHT_POS; | ||||
|  |  | |||
|  | @ -19,38 +19,38 @@ function binSearch(list, word) { | |||
| 
 | ||||
| export class WordSource { | ||||
|     constructor() { | ||||
|         this._dataVersion = null; | ||||
|         this._wordData = null; | ||||
|         this._otherWords = null; | ||||
|         this._pattern = null; | ||||
|         // this._currentWord = null;
 | ||||
|         // this._progressionState=
 | ||||
|     } | ||||
| 
 | ||||
|     isWord(word) { | ||||
|         let list = this._wordData.words[word.length.toString()]; | ||||
|         if (!list) { | ||||
|         if (!this._wordData || !this._otherWords) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return binSearch(list, word); | ||||
|         return binSearch(this._wordData[word.length.toString()], word) || | ||||
|             binSearch(this._otherWords[word.length.toString()], word); | ||||
|     } | ||||
| 
 | ||||
|     async needToResetProgression(prog) { | ||||
|         await this._fetchAllWordsIfNecessary(); | ||||
|         return !prog || !prog.shuffleId || this._pattern.id !== prog.shuffleId; | ||||
|         return !prog || !prog.shuffleId || this._dataVersion !== prog.shuffleId; | ||||
|     } | ||||
| 
 | ||||
|     async getPattenShuffleID() { | ||||
|         return this._pattern.id; | ||||
|         return this._dataVersion; | ||||
|     } | ||||
|      | ||||
|     async getCurrentWord(prog) { | ||||
|         let words = await this._fetchAllWordsIfNecessary(); | ||||
|         await this._fetchAllWordsIfNecessary(); | ||||
| 
 | ||||
|         let wordLengthKey = prog.wordLength + ""; | ||||
|         let wordIndex = prog.wordIndex[wordLengthKey]; | ||||
|         let idx = this._pattern.index[wordLengthKey][wordIndex]; | ||||
|         let idx = this._pattern[wordLengthKey][wordIndex]; | ||||
| 
 | ||||
|         return words.words[wordLengthKey][idx]; | ||||
|         return this._wordData[wordLengthKey][idx]; | ||||
|     } | ||||
|      | ||||
|     async _fetchAllWordsIfNecessary() { | ||||
|  | @ -58,8 +58,11 @@ export class WordSource { | |||
|             return this._wordData; | ||||
|         } | ||||
| 
 | ||||
|         this._wordData = await (await fetch("/assets/data/words.json")).json(); | ||||
|         this._pattern = await (await fetch("/assets/data/shuffle_pattern.json")).json(); | ||||
|         return this._wordData; | ||||
|         let data = await (await fetch("/assets/data/data.json")).json(); | ||||
| 
 | ||||
|         this._dataVersion = data["versionId"]; | ||||
|         this._wordData = data["guessWords"]; | ||||
|         this._otherWords = data["otherWords"]; | ||||
|         this._pattern = data["shufflePattern"]; | ||||
|     } | ||||
| } | ||||
|  | @ -217,4 +217,26 @@ div.overlay-message { | |||
| 
 | ||||
| .hide { | ||||
|     display: none !important; | ||||
| } | ||||
| 
 | ||||
| .loader { | ||||
|     margin-top: 6rem; | ||||
| 
 | ||||
|     width: 48px; | ||||
|     height: 48px; | ||||
|     border: 5px solid #FFF; | ||||
|     border-bottom-color: var(--color-no-letter-bg); | ||||
|     border-radius: 50%; | ||||
|     display: inline-block; | ||||
|     box-sizing: border-box; | ||||
|     animation: rotation 1s linear infinite; | ||||
| } | ||||
| 
 | ||||
| @keyframes rotation { | ||||
|     0% { | ||||
|         transform: rotate(0deg); | ||||
|     } | ||||
|     100% { | ||||
|         transform: rotate(360deg); | ||||
|     } | ||||
| } | ||||
|  | @ -21,6 +21,8 @@ | |||
|           <div data-playfield-target="topMessage"> </div> | ||||
|           <div data-playfield-target="playfield"></div> | ||||
| 
 | ||||
|           <span data-playfield-target="loader" class="loader"></span> | ||||
| 
 | ||||
|           <div data-playfield-target="nextPuzzleButtons" class="hide"> | ||||
|             <button class="secondary" data-action="playfield#loadDef">Define</button> | ||||
|             <button data-action="playfield#loadNextPuzzle">Next Puzzle</button> | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue