List of possible words for Wordle EN & RU games. Files structure We have huge files with words arrays and we do not want to include them into our project via import , because it makes the bundle bigger and will take webpack ages to produce a production build. We put our files into public folder instead of src and access data via fetch('url') function. Logic
function listOfPossibleWords(args) {
const { charObjArr, vocabArr, charsNum, noCharsArr, charArr } = {
// just some default values to show the structure
charObjArr: [
{ char: 'a', pos: null, notPos: 1 },
{ char: 'b', pos: 2, notPos: null },
],
charArr: ['a', 'b'],
noCharsArr: ['c', 'd', 'e'],
vocabArr: ['hi', 'bye'],
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)
}))
}
}
UI
import styled from 'styled-components'
import React from 'react'
import { RiAddLine as Plus } from 'react-icons/ri'
import { MdRemove as Minus } from 'react-icons/md'
function Component() {
const [excludeLettersInputState, setExcludeLettersInputState] = React.useState(['cde'])
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('.char').forEach(el => { charObjArr.push({ char: el.value }) })
container.querySelectorAll('.pos').forEach((el, i) => { charObjArr[i].pos = parseInt(el.value) - 1 })
container.querySelectorAll('.notPos').forEach((el, i) => { charObjArr[i].notPos = parseInt(el.value) - 1 })
const charArr = Array.from(container.querySelectorAll('.char')).map(el => el.value)
const charsNum = parseInt(container.querySelector('.charsNum').value)
const noCharsArr = container.querySelector('.noCharsArr').value.split('')
let ruWords = []
const getRuWordsPromise = fetch('/filesToFetch/ruWords.json')
.then(res => res.json())
.then(data => { ruWords = data })
let enWords = []
const getEnWordsPromise = fetch('/filesToFetch/enWords.json')
.then(res => res.json())
.then(data => { enWords = data })
// just to have 1.5s loading... alert
const delayPromise = new Promise(resolve => {
setTimeout(() => {
resolve('done')
}, 1500)
})
setLoadingState(true)
return Promise.all([getRuWordsPromise, getEnWordsPromise, delayPromise])
.then((resultArr) => {
const lang = container.querySelector('input:checked').value
const vocabArr = (lang === 'en') ? 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='a' pos='' notPos='1'/>
<LetterPosDiv char='b' pos='2' notPos='' />
{Array(lettersNumState).fill('').map((val, i) => <LetterPosDiv key={i}/>)}
<div style={{ justifyContent: 'center' }}>
<Plus onClick={() => setLettersNumState(lettersNumState + 1)} />
<Minus onClick={() => setLettersNumState((lettersNumState - 1 < 0) ? 0 : lettersNumState - 1)} />
</div>
<div className="inputWrapper">
<input className='noCharsArr' type="text" spellCheck="false" required value={excludeLettersInputState} onChange={e => setExcludeLettersInputState(e.target.value)}/>
<span className="placeholder">Letters to exclude</span>
</div>
<div className="inputWrapper">
<input className="charsNum" type="number" value={charsNumInputState} spellCheck="false" required onChange={e => setCharsNumInputState(e.target.value)}/>
<span className="placeholder">Number of letters</span>
</div>
<button onClick={search}>{loadingState ? 'Loading...' : 'Show'}</button>
<div id="possibleWordsTag">{(possibleWordsState.length === 0) ? 'Possible words' : `${possibleWordsState.length} word${possibleWordsState.length === 1 ? '' : 's'} found`}</div>
<div id="possibleWordsContainer">{possibleWordsState.map(word => (<span key={word} className='foundWord'> {word} </span>))}</div>
</FlexContainer>
</div>
)
}
function RadioButtons() {
const [langState, setLangState] = React.useState('en')
return (
<Div>
<label>
<input type="radio" name="lang" value='en' defaultChecked onChange={e => setLangState(e.target.value)} />
<span>en</span>
</label>
<label>
<input type="radio" name="lang" value='ru' onChange={e => setLangState(e.target.value)} />
<span>ru</span>
</label>
</Div>
)
}
const Div = styled.div`
label {
display: inline-block;
margin-right: 10px;
cursor: pointer;
input {
margin: 2px;
}
span {
position: relative;
bottom: 1.8px;
}
}
`
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="inputWrapper">
<input
className="char"
type="text"
maxLength={1}
spellCheck="false"
autoCapitalize='off'
required
value={letterInputState}
onChange={e => setLetterInputState(e.target.value)}
/>
<span className="placeholder">Letter</span>
</div>
<div className="inputWrapper">
<input
className="pos"
type="number"
spellCheck="false"
autoCapitalize='off'
required
value={posInputState}
onChange={e => setPosInputState(e.target.value)}
/>
<span className="placeholder">At</span>
</div>
<div className="inputWrapper">
<input
className="notPos"
type="number"
spellCheck="false"
autoCapitalize='off'
required
value={notPosInputState}
onChange={e => setNotPosInputState(e.target.value)}
/>
<span className="placeholder">Not at</span>
</div>
</div>
)
}
const FlexContainer = styled.div`
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: stretch;
align-items: center;
row-gap: 10px;
& > * {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
width: 350px;
max-width: 100%;
column-gap: 10px;
}
.inputWrapper {
position: relative;
height: 60px;
}
input {
outline: none;
border: 1px solid #c4c4c4;
border-radius: 6px;
appearance: none;
box-shadow: inset #00000033 0px 0px 3px 0px;
padding: 20px 10px 10px 10px;
width: 100%;
height: 100%;
font-size: 20px;
letter-spacing: 5px;
&:focus ~ .placeholder,
&:not(:focus):valid ~ .placeholder {
top: 2px;
left: 10px;
font-size: 13px;
color: grey;
}
}
.placeholder {
position: absolute;
pointer-events: none;
top: 25px;
left: 10px;
transition: 0.3s ease all;
color: grey;
}
button {
display: inline-block;
outline: 0;
cursor: pointer;
text-align: center;
border: 1px solid #babfc3;
padding: 11px 24px;
min-height: 44px;
min-width: 44px;
width: 100px;
color: #202223;
background: #ffffff;
border-radius: 4px;
font-weight: 500;
font-size: 14px;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 0px 0px;
&:hover {
background: #f6f6f7;
outline: 1px solid transparent;
}
}
#possibleWordsTag {
position: relative;
top: 10px;
justify-content: center;
align-items: center;
}
#possibleWordsContainer {
display: block;
border: 1px solid #d5d5d5;
border-radius: 4px;
width: 100%;
height: 99px;
padding: 5px;
word-wrap: break-word;
overflow: auto;
}
.foundWord {
margin: 0px 0.3em;
padding: 0.1em 0.6em;
border-radius: 3px;
border: 1px solid rgb(204, 204, 204);
color: rgb(51, 51, 51);
line-height: 1.4;
font-family: Arial,Helvetica,sans-serif;
display: inline-block;
box-shadow: 0px 1px 0px rgba(0,0,0,0.2), inset 0px 0px 0px 2px #ffffff;
background-color: rgb(247, 247, 247);
text-shadow: 0 1px 0 #fff;
font-size: 12px;
}
input[type="number"] {
letter-spacing: normal;
}
svg {
height: 24px;
cursor: pointer;
&:hover {
transform: scale(1.5);
transition: transform .3s;
}
}
`