Compare commits

...

2 Commits
main ... wordle

Author SHA1 Message Date
Austin Chen
2c11ce9efa Parse guesses instead of hardcoding them 2022-02-13 22:48:56 -08:00
Austin Chen
e6df0633bf Implement a Wordle Hard Mode solver 2022-02-13 22:48:56 -08:00
3 changed files with 220785 additions and 0 deletions

File diff suppressed because it is too large Load Diff

115
web/pages/wordle/index.tsx Normal file
View File

@ -0,0 +1,115 @@
import { useState } from 'react'
import { Page } from '../../components/page'
import { Title } from '../../components/title'
// From https://github.com/lynn/hello-wordl
// `dictionary` has all accepted words; `targets` are common-ish words
import * as dictionary from './dictionary.json'
import * as targets from './targets.json'
export default function Wordle() {
const WORD_LENGTH = 5
// Find all words of length WORD_LENGTH
// const dict = dictionary.filter((word) => word.length === WORD_LENGTH)
const words = targets.filter((word) => word.length === WORD_LENGTH)
const [input, setInput] = useState('')
const pastGuesses: PastGuess[] = parsePastGuesses(input)
const placeholder = 'e.g.\nstage BBYBB\nlucre YYGYY'
const valids = validHardMode(words, pastGuesses)
return (
<Page>
<div className="text-gray-500">
<Title text="Wordle: Hard Mode possibilities" />
<p className="text-black">
Given a list of past Wordle guesses, show all possible valid next
guesses for Hard Mode
</p>
Actually for{' '}
<a href="https://hellowordl.net/">https://hellowordl.net/</a>
<p>Past guesses:</p>
<textarea
className="h-32 w-full p-2"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder={placeholder}
/>
<br />
<br />
<p className="text-black">
{Array(10)
.fill(null)
.map((_) => chooseRandom(valids))
.join(', ')}
</p>
<p>Total valid words: {valids.length}</p>
<p>All valid words:</p>
<pre>{JSON.stringify(valids, null, 2)}</pre>
</div>
</Page>
)
}
function chooseRandom(list: any[]) {
return list[Math.floor(Math.random() * list.length)]
}
type PastGuess = {
guess: string
// Result is a string like 'GYBBG'
// G = Green (match), Y = Yellow (somewhere), B = Black (nowhere)
result: string
}
function validHardMode(wordlist: string[], pastGuesses: PastGuess[]) {
let candidates = wordlist.slice()
const blacks = new Set()
for (const { guess, result } of pastGuesses) {
candidates = candidates.filter((word) => {
// e.g. guess = 'helot', result = 'GGGYB', word = 'hello => true
// e.g. guess = 'helot', result = 'GGGYB', word = 'helot => false
const guessYellows = []
const wordNonGreens: string[] = []
for (let i = 0; i < result.length; i++) {
switch (result[i]) {
case 'G':
if (word[i] !== guess[i]) return false
break
case 'Y':
if (word[i] === guess[i]) return false
guessYellows.push(guess[i])
wordNonGreens.push(word[i])
break
case 'B':
if (word[i] === guess[i]) return false
wordNonGreens.push(word[i])
blacks.add(guess[i])
break
}
if (blacks.has(word[i])) return false
}
// Return true if every letter in guessYellows is in wordNonGreens
return guessYellows.every((letter) => wordNonGreens.includes(letter))
})
}
return candidates
}
function parsePastGuesses(pastGuesses: string) {
return pastGuesses
.split('\n')
.map((pg) => {
const [guess, result] = pg.split(' ')
return { guess, result }
})
.filter(({ guess, result }) => guess && result)
}
// Bad target words: witan, sedum
// TODO: Maybe just use canonical Wordle dictionaries
// DEBUG: { guess: 'lucre', result: 'YYGYY' }
// Should produce: ulcer

37949
web/pages/wordle/targets.json Normal file

File diff suppressed because it is too large Load Diff