在 JavaScript/React 文字游戏中跟踪多个文本输入值以确认它们全部正确的最佳方法
我正在为我的投资组合制作一个密码文字游戏(破译一句名言)。我已经完成了最后一个主要障碍:确认谜题已解决。
我没有声誉点来发布真正有助于澄清的图片,但想想命运之轮,其中每个白框都是一个单独的输入。我没有将它们附加到任何状态,因为每个谜题很容易有超过 100 个输入。我一直在通过 className 操作它们以相应地自动填充输入(即,如果玩家在一个输入中输入“e”,则所有类似的输入都将填充“e”)
我以为我已经弄清楚了完成确认,但很快意识到有多么缺陷我的解决方案是。我正在创建一个待解决方案状态,它是一个字符串,并尝试通过输入中的 OnChange 函数适当地填充它。每次值更改时我都会检查它,看看它是否与解决方案字符串匹配,但事实证明我没有正确设置挂起解决方案字符串的状态来更新玩家在谜题中的进度。我不确定这是否是最好的方法,因此我正在寻求一些帮助来修复 updatePendingSolution 函数,或者寻求有关解决此问题的更好方法的建议。
示例:
- “*h*** *o* *o* *o** h***!” <--待决解决方案
- “感谢您的帮助!” <-- 解决方案
这些函数用于处理输入的 OnChange 以及自动填充最多三个随机字母作为 onClick 提示。
const handleChange = (e) => {
let letter = document.getElementsByClassName(e.target.className)
for (let i=0; i < letter.length; i++) {
letter[i].value = e.target.value
letter[i].style.color = "darkblue"
}
setPendingSolution(pendingSolution.split('').map((char, i) => char.replace(/\*/, updatePendingSolution(char, e.target.value, i))).join(''))
}
const handleClick = () => {
const rand = Math.floor(Math.random() * 26)
if(solution === pendingSolution) return
if (randLetters.includes(rand)) {
handleClick()
} else {
setRandLetters([...randLetters, rand])
let letter = document.getElementsByClassName(`char_input ${shuffledAlphabet[rand].toLowerCase()}`)
if (letter.length === 0) {
letter = document.getElementsByClassName(`char_input ${shuffledAlphabet[rand]}`)
if(letter.length === 0) handleClick()
} else {
if (letter[0].value.toUpperCase() === alphabet[rand]) {
handleClick()
} else {
for (let i=0; i < letter.length; i++) {
letter[i].value = alphabet[rand]
letter[i].disabled = true
letter[i].style.color = 'green';
setPendingSolution(pendingSolution.split('').map((char, i) => char.replace('*', updatePendingSolution(char, alphabet[rand], i))).join(''))
}
}
}
setHints([...hints, alphabet[rand]])
}
}
这是我需要帮助的部分。这是我正在开发的函数,用于在玩家使用提示或更改输入时更新待解决方案状态。
useEffect(() => {
if (solution && pendingSolution) {
if (solution === pendingSolution) {
alert("You won!!!")
}
}
}, [pendingSolution])
const updatePendingSolution = (char, value, i) => {
if (solution[i].match(value.toUpperCase())) {
return value.toUpperCase()
} else if (solution[i].match(value.toLowerCase())) {
return value.toLowerCase()
} else return char
}
这是呈现引用和名称组件的代码:
function EncryptedText({ divName, onChange, words } ) {
return (
<div className={divName}>{words.split(" ").map((word, i) => {
return (
<div className="word_div" key={i}> {word.split("").map((char, j) => {
return (
<div key={j} className="char_container">
{char.match(/[A-Za-z]/) ?
char.match(/[A-Z]/) ?
<div>
<p className="puzzle_p uppercase"><input className={`char_input ${char.toLowerCase()}`} maxLength="1" onChange={onChange} type="text" /></p>
</div>
:<div>
<p className="puzzle_p lowercase"><input className={`char_input ${char}`} maxLength="1" onChange={onChange} type="text" /></p>
</div>
: <p className="puzzle_p">{char}</p>
}
<p className="puzzle_p">{char}</p>
</div>
)
})}</div>
)
})}</div>
)
}
export default EncryptedText
I'm making a cryptogram word game (decipher a famous quote) for my portfolio. I'm down to the last major hurdle: confirming that the puzzle is solved.
I don't have the reputation points to post a picture that would really help clarify, but think Wheel of Fortune where each white box is a separate input. I don't have them attached to any state because there can easily be over 100 inputs per puzzle. I've been manipulating them through className to autofill the inputs accordingly (i.e. if a player enters an 'e' in one input all similar inputs will be filled with 'e')
I thought I had completion confirmation figured out but quickly realized how flawed my solution was. I was creating a pendingSolution state that was a string and trying to populate it appropriately through an OnChange function in the inputs. I was checking this every time the value changed to see if it matched with the solution string, but it turns out I'm not setting the state of the pendingSolution string correctly to update the player's progress in the puzzle. I'm not sure if this is even the best approach, so I'm looking for either some help with fixing my updatePendingSolution function, or advice on a better way to approach this problem.
Example:
- "*h*** *o* *o* *o** h***!" <-- pendingSolution
- "Thank you for your help!" <-- solution
These are the functions to handle the OnChange of inputs and also to autofill up to three random letters as onClick hints.
const handleChange = (e) => {
let letter = document.getElementsByClassName(e.target.className)
for (let i=0; i < letter.length; i++) {
letter[i].value = e.target.value
letter[i].style.color = "darkblue"
}
setPendingSolution(pendingSolution.split('').map((char, i) => char.replace(/\*/, updatePendingSolution(char, e.target.value, i))).join(''))
}
const handleClick = () => {
const rand = Math.floor(Math.random() * 26)
if(solution === pendingSolution) return
if (randLetters.includes(rand)) {
handleClick()
} else {
setRandLetters([...randLetters, rand])
let letter = document.getElementsByClassName(`char_input ${shuffledAlphabet[rand].toLowerCase()}`)
if (letter.length === 0) {
letter = document.getElementsByClassName(`char_input ${shuffledAlphabet[rand]}`)
if(letter.length === 0) handleClick()
} else {
if (letter[0].value.toUpperCase() === alphabet[rand]) {
handleClick()
} else {
for (let i=0; i < letter.length; i++) {
letter[i].value = alphabet[rand]
letter[i].disabled = true
letter[i].style.color = 'green';
setPendingSolution(pendingSolution.split('').map((char, i) => char.replace('*', updatePendingSolution(char, alphabet[rand], i))).join(''))
}
}
}
setHints([...hints, alphabet[rand]])
}
}
This is the part that I need the help with. It is the function I'm working on to update the pendingSolution state whenever a player uses a hint or changes an input.
useEffect(() => {
if (solution && pendingSolution) {
if (solution === pendingSolution) {
alert("You won!!!")
}
}
}, [pendingSolution])
const updatePendingSolution = (char, value, i) => {
if (solution[i].match(value.toUpperCase())) {
return value.toUpperCase()
} else if (solution[i].match(value.toLowerCase())) {
return value.toLowerCase()
} else return char
}
And this is the code that renders the quote and name components:
function EncryptedText({ divName, onChange, words } ) {
return (
<div className={divName}>{words.split(" ").map((word, i) => {
return (
<div className="word_div" key={i}> {word.split("").map((char, j) => {
return (
<div key={j} className="char_container">
{char.match(/[A-Za-z]/) ?
char.match(/[A-Z]/) ?
<div>
<p className="puzzle_p uppercase"><input className={`char_input ${char.toLowerCase()}`} maxLength="1" onChange={onChange} type="text" /></p>
</div>
:<div>
<p className="puzzle_p lowercase"><input className={`char_input ${char}`} maxLength="1" onChange={onChange} type="text" /></p>
</div>
: <p className="puzzle_p">{char}</p>
}
<p className="puzzle_p">{char}</p>
</div>
)
})}</div>
)
})}</div>
)
}
export default EncryptedText
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
虽然这是一种完全不同的方法,也不是用 React 实现的,但 OP 可能会从中获得一些灵感。
主要思想是提供一种
'wheel-of-fortune'
组件。这样的组件会与所有用户交互一起处理其状态,并且它“知道”何时解决。
为此,它会清理传入的任何文本并将其拆分为单词。然后,每个单词都被拆分为字符,并为每个字符创建相应的
input
元素。单词是分组到列表中的字符,句子/引文/引文都是由这些列表组成的。简化任何其他任务的技巧是引入......
...并且任何组件内部都保存上述所有三个作为参考。
然后我们只需要在组件根节点处理任何
input
事件(事件委托)。对于每个事件目标(一个
input
元素),人们确实通过节点引用查找正确的字符。然后比较查找到的字符和节点值的小写值。如果它们相等,则有匹配项,并且可以通过小写字符继续查找其他匹配的输入元素。对于每个其他匹配元素,将自动填充该元素的相关正确字符大小写。当然,人们总是需要检查某个组件是否已解决。这是通过将所有当前元素值连接到字符串中并将其与原始传递文本的清理版本进行比较来完成的。
下面提供的组件实现具有可由第三方代码使用的响应承诺。承诺在内部通过前一段描述的过程得到解决。
Though this is an entirely different approach, also not implemented with react, the OP might take some inspiration from it.
The main idea is to provide kind of a
'wheel-of-fortune'
component.Such a component takes care of its state altogether with all user interaction and it "knows" when it is solved.
For this it sanitizes any text it got passed into and splits it into words. Each word then is split into characters, and for each character a corresponding
input
element gets created. Words are characters grouped into lists, and sentences / quotes / citations are made up of such lists.The trick which eases any other task is to introduce ...
... and any component internally holds all of the above three as references.
Then one just needs to handle any
input
event at the components root node (event delegation).For each event target (an
input
element) one does look up the correct character by the node reference. Then one compares the lowercased values of both the looked up character and the node value. In case they equal one has a match, and one can continue looking up other matching input elements by the lowercased character. For each other matching element one would autofill the element's related correct character case.Of cause one always needs to check whether a component got solved. This is done by concatenating all current element values in/to a string and comparing it with the sanitized version of the originally passed text.
The beneath provided component implementation features a
response
promise that can be utilized by third party code. The promise internally gets resolved by the process described with the before paragraph.