Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2c11ce9efa | ||
|
e6df0633bf |
182721
web/pages/wordle/dictionary.json
Normal file
182721
web/pages/wordle/dictionary.json
Normal file
File diff suppressed because it is too large
Load Diff
115
web/pages/wordle/index.tsx
Normal file
115
web/pages/wordle/index.tsx
Normal 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
37949
web/pages/wordle/targets.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user