wordle
2022.05.08
Helper for Wordle game in JavaScript
#va#JavaScript
use client /components/post/reExport styled-components react-icons/ri react-icons/md , pos: null, notPos: 1 },
{ char: , pos: 2, notPos: null }
],
charArr: [ ],
noCharsArr: [ ],
vocabArr: [ ],
charsNum: 5,
...args
}
let arr = []
arr = arrOfStrOfCertainLength(vocabArr, charsNum)
arr = arrOfStrWithChars(arr, charArr)
arr = arrOfStrWithoutChars(arr, noCharsArr)
arr = arrOfStrWithCharsAtPos(arr, charObjArr)
arr = arrOfStrWithoutCharsAtPos(arr, charObjArr)
return arr
// helper functions
function arrOfStrOfCertainLength(arr, length) {
return arr.filter(str => str && str.length === length)
}
function arrOfStrWithChars(arr, charsArr) {
return arr.filter(str => {
const areAllCharsInside = charsArr.every(char => str.includes(char))
return areAllCharsInside
})
}
function arrOfStrWithoutChars(arr, charsArr) {
return arr.filter(str => {
const isCharsInside = charsArr.some(char => str.includes(char))
return !isCharsInside
})
}
function isCharAtPosition(str, char, pos) {
return str.split( )[pos] === char
}
function arrOfStrWithCharsAtPos(arr, charObjArr) {
return arr.filter(str => charObjArr.every(o => {
if (isNaN(o.pos)) return true
return isCharAtPosition(str, o.char, o.pos)
}))
}
function arrOfStrWithoutCharsAtPos(arr, charObjArr) {
return arr.filter(str => charObjArr.every(o => {
if (isNaN(o.notPos)) return true
return !isCharAtPosition(str, o.char, o.notPos)
}))
}
}
function Component() {
const [excludeLettersInputState, setExcludeLettersInputState] = React.useState([ ])
const [charsNumInputState, setCharsNumInputState] = React.useState(5)
const [lettersNumState, setLettersNumState] = React.useState(0)
const [possibleWordsState, setPossibleWordsState] = React.useState([])
const [loadingState, setLoadingState] = React.useState(false)
const ref = React.useRef()
// React.useEffect(() => { search() }, [])
async function formArgsFromDom() {
const container = ref.current
const charObjArr = []
container.querySelectorAll( ).forEach(el => { charObjArr.push({ char: el.value }) })
container.querySelectorAll( ).forEach((el, i) => { charObjArr[i].pos = parseInt(el.value) - 1 })
container.querySelectorAll( ).forEach((el, i) => { charObjArr[i].notPos = parseInt(el.value) - 1 })
const charArr = Array.from(container.querySelectorAll( )).map(el => el.value)
const charsNum = parseInt(container.querySelector( ).value)
const noCharsArr = container.querySelector( ).value.split( )
let ruWords = []
const getRuWordsPromise = fetch( )
.then(res => res.json())
.then(data => { ruWords = data })
let enWords = []
const getEnWordsPromise = fetch( )
.then(res => res.json())
.then(data => { enWords = data })
// just to have 1.5s loading... alert
const delayPromise = new Promise(resolve => {
setTimeout(() => {
resolve( )
}, 1500)
})
setLoadingState(true)
return Promise.all([getRuWordsPromise, getEnWordsPromise, delayPromise])
.then((resultArr) => {
const lang = container.querySelector( ).value
const vocabArr = (lang === ) ? enWords : ruWords
return { charObjArr, charsNum, noCharsArr, charArr, vocabArr }
})
.finally(() => { setLoadingState(false) })
}
async function search() {
const argsForNextFunction = await formArgsFromDom()
const possibleWords = listOfPossibleWords(argsForNextFunction)
setPossibleWordsState(possibleWords)
}
return (
<div ref={ref}>
<RadioButtons />
<FlexContainer>
<LetterPosDiv char= />
<LetterPosDiv char= />
{Array(lettersNumState)
.fill( )
.map((val, i) => (
<LetterPosDiv key={i} />
))}
<div style={{ justifyContent: }}>
<Plus onClick={() => setLettersNumState(lettersNumState + 1)} />
<Minus
onClick={() => setLettersNumState(lettersNumState - 1 < 0 ? 0 : lettersNumState - 1)}
/>
</div>
<div className= >
<input
className=
type=
spellCheck=
required
value={excludeLettersInputState}
onChange={e => setExcludeLettersInputState(e.target.value)}
/>
<span className= >Letters to exclude</span>
</div>
<div className= >
<input
className=
type=
value={charsNumInputState}
spellCheck=
required
onChange={e => setCharsNumInputState(e.target.value)}
/>
<span className= >Number of letters</span>
</div>
<button onClick={search}>{loadingState ? }</button>
<div id= >
{possibleWordsState.length === 0
?
:
} found possibleWordsContainer >
{ }
{word}{ }
</span>
))}
</div>
</FlexContainer>
</div>
)
}
function RadioButtons() {
const [langState, setLangState] = React.useState( )
return (
<Div>
<label>
<input type= defaultChecked onChange={e => setLangState(e.target.value)} />
<span>en</span>
</label>
<label>
<input type= onChange={e => setLangState(e.target.value)} />
<span>ru</span>
</label>
</Div>
)
}
const Div = styled.div
function LetterPosDiv(props) {
const [letterInputState, setLetterInputState] = React.useState(props.char)
const [posInputState, setPosInputState] = React.useState(props.pos)
const [notPosInputState, setNotPosInputState] = React.useState(props.notPos)
return (
<div>
<div className= >
<input
className=
type=
maxLength={1}
spellCheck=
autoCapitalize=
required
value={letterInputState}
onChange={e => setLetterInputState(e.target.value)}
/>
<span className= >Letter</span>
</div>
<div className= >
<input
className=
type=
spellCheck=
autoCapitalize=
required
value={posInputState}
onChange={e => setPosInputState(e.target.value)}
/>
<span className= >At</span>
</div>
<div className= >
<input
className=
type=
spellCheck=
autoCapitalize=
required
value={notPosInputState}
onChange={e => setNotPosInputState(e.target.value)}
/>
<span className= >Not at</span>
</div>
</div>
)
}
const FlexContainer = styled.div ] {
letter-spacing: normal;
}
svg {
height: 24px;
cursor: pointer;
&:hover {
transform: scale(1.5);
transition: transform .3s;
}
}
,
date: ,
tags: [ JavaScript https://antonarbus.com/imgs/wordle.png Helper for Wordle game in JavaScript }
<Lnk path= >EN</Lnk> & <Lnk path= >RU</Lnk> games.
</p>
<Component />
<H>Files structure</H>
<p>We have huge files with words arrays and we do not want to include them into our project via <code>import</code>,
because it makes the bundle bigger and will take <i>webpack</i> ages to produce a production build.</p>
<p>We put our files into <code>public</code> folder instead of <code>src</code> and access data via <Code>{ }</Code> function.</p>
<LazyImg src= />
<H>Logic</H>
<Code block jsx>{ , pos: null, notPos: 1 },
{ char: , pos: 2, notPos: null },
],
charArr: [ ],
noCharsArr: [ ],
vocabArr: [ ],
charsNum: 5,
...args,
}
let arr = []
arr = arrOfStrOfCertainLength(vocabArr, charsNum)
arr = arrOfStrWithChars(arr, charArr)
arr = arrOfStrWithoutChars(arr, noCharsArr)
arr = arrOfStrWithCharsAtPos(arr, charObjArr)
arr = arrOfStrWithoutCharsAtPos(arr, charObjArr)
return arr
// helper functions
function arrOfStrOfCertainLength(arr, length) {
return arr.filter(str => str && str.length === length)
}
function arrOfStrWithChars(arr, charsArr) {
return arr.filter(str => {
const areAllCharsInside = charsArr.every(char => str.includes(char))
return areAllCharsInside
})
}
function arrOfStrWithoutChars(arr, charsArr) {
return arr.filter(str => {
const isCharsInside = charsArr.some(char => str.includes(char))
return !isCharsInside
})
}
function isCharAtPosition(str, char, pos) {
return str.split( )[pos] === char
}
function arrOfStrWithCharsAtPos(arr, charObjArr) {
return arr.filter(str => charObjArr.every(o => {
if (isNaN(o.pos)) return true
return isCharAtPosition(str, o.char, o.pos)
}))
}
function arrOfStrWithoutCharsAtPos(arr, charObjArr) {
return arr.filter(str => charObjArr.every(o => {
if (isNaN(o.notPos)) return true
return !isCharAtPosition(str, o.char, o.notPos)
}))
}
}
import styled from
import React from
import { RiAddLine as Plus } from
import { MdRemove as Minus } from
function Component() {
const [excludeLettersInputState, setExcludeLettersInputState] = React.useState([ ])
const [charsNumInputState, setCharsNumInputState] = React.useState(5)
const [lettersNumState, setLettersNumState] = React.useState(0)
const [possibleWordsState, setPossibleWordsState] = React.useState([])
const [loadingState, setLoadingState] = React.useState(false)
const ref = React.useRef()
// React.useEffect(() => { search() }, [])
async function formArgsFromDom() {
const container = ref.current
const charObjArr = []
container.querySelectorAll( ).forEach(el => { charObjArr.push({ char: el.value }) })
container.querySelectorAll( ).forEach((el, i) => { charObjArr[i].pos = parseInt(el.value) - 1 })
container.querySelectorAll( ).forEach((el, i) => { charObjArr[i].notPos = parseInt(el.value) - 1 })
const charArr = Array.from(container.querySelectorAll( )).map(el => el.value)
const charsNum = parseInt(container.querySelector( ).value)
const noCharsArr = container.querySelector( ).value.split( )
let ruWords = []
const getRuWordsPromise = fetch( )
.then(res => res.json())
.then(data => { ruWords = data })
let enWords = []
const getEnWordsPromise = fetch( )
.then(res => res.json())
.then(data => { enWords = data })
// just to have 1.5s loading... alert
const delayPromise = new Promise(resolve => {
setTimeout(() => {
resolve( )
}, 1500)
})
setLoadingState(true)
return Promise.all([getRuWordsPromise, getEnWordsPromise, delayPromise])
.then((resultArr) => {
const lang = container.querySelector( ).value
const vocabArr = (lang === ) ? enWords : ruWords
return { charObjArr, charsNum, noCharsArr, charArr, vocabArr }
})
.finally(() => { setLoadingState(false) })
}
async function search() {
const argsForNextFunction = await formArgsFromDom()
const possibleWords = listOfPossibleWords(argsForNextFunction)
setPossibleWordsState(possibleWords)
}
return (
<div ref={ref}>
<RadioButtons />
<FlexContainer>
<LetterPosDiv char= />
<LetterPosDiv char= />
{Array(lettersNumState).fill( ).map((val, i) => <LetterPosDiv key={i}/>)}
<div style={{ justifyContent: }}>
<Plus onClick={() => setLettersNumState(lettersNumState + 1)} />
<Minus onClick={() => setLettersNumState((lettersNumState - 1 < 0) ? 0 : lettersNumState - 1)} />
</div>
<div className= >
<input className= spellCheck= required value={excludeLettersInputState} onChange={e => setExcludeLettersInputState(e.target.value)}/>
<span className= >Letters to exclude</span>
</div>
<div className= >
<input className= value={charsNumInputState} spellCheck= required onChange={e => setCharsNumInputState(e.target.value)}/>
<span className= >Number of letters</span>
</div>
<button onClick={search}>{loadingState ? }</button>
<div id= >{(possibleWordsState.length === 0) ? \${possibleWordsState.length} word\${possibleWordsState.length === 1 ? }</div>
<div id= >{possibleWordsState.map(word => (<span key={word} className= > {word} </span>))}</div>
</FlexContainer>
</div>
)
}
function RadioButtons() {
const [langState, setLangState] = React.useState( )
return (
<Div>
<label>
<input type= defaultChecked onChange={e => setLangState(e.target.value)} />
<span>en</span>
</label>
<label>
<input type= onChange={e => setLangState(e.target.value)} />
<span>ru</span>
</label>
</Div>
)
}
const Div = styled.div\
function LetterPosDiv(props) {
const [letterInputState, setLetterInputState] = React.useState(props.char)
const [posInputState, setPosInputState] = React.useState(props.pos)
const [notPosInputState, setNotPosInputState] = React.useState(props.notPos)
return (
<div>
<div className= >
<input
className=
type=
maxLength={1}
spellCheck=
autoCapitalize=
required
value={letterInputState}
onChange={e => setLetterInputState(e.target.value)}
/>
<span className= >Letter</span>
</div>
<div className= >
<input
className=
type=
spellCheck=
autoCapitalize=
required
value={posInputState}
onChange={e => setPosInputState(e.target.value)}
/>
<span className= >At</span>
</div>
<div className= >
<input
className=
type=
spellCheck=
autoCapitalize=
required
value={notPosInputState}
onChange={e => setNotPosInputState(e.target.value)}
/>
<span className= >Not at</span>
</div>
</div>
)
}
const FlexContainer = styled.div\ ] {
letter-spacing: normal;
}
svg {
height: 24px;
cursor: pointer;
&:hover {
transform: scale(1.5);
transition: transform .3s;
}
}
\