如何让这两个事件处理程序协同工作?
How Can I Get These Two Event Handlers to Work Together?
我正在使用 cm-chessboard and chess.js 创建国际象棋开局训练器。这个想法是从 Lichess Opening Explorer API 中提取连续的国际象棋位置,玩家通过移动下一个最佳移动来测试自己 and/or 回答关于开口名称的问题。
不过,我有点儿头疼了。
目前,它只对轮到计算机的用户进行提问。我无法找到一种方法让它等待用户按下 return 键,然后再显示有关玩家移动的答案。
这是我的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chess Opening Trainer</title>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"/>
<link rel="stylesheet" href="https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/styles/cm-chessboard.css"/>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.2/chess.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style type="text/css">
body{font-family: 'Montserrat', sans-serif;}
.grid-container {
display: grid;
grid-template-columns: 1fr 0.6fr;
grid-template-rows: 1fr;
gap: 0px 0px;
}
.board {
grid-area: 1 / 1 / 2 / 3;
width: 800px;
max-width: 800px;
}
#interface {
grid-area: 1 / 2 / 2 / 3;
border-style: solid;
border-width: thin;
width: 400px;
max-width: 400px;
}
p{
width: 100%;
align: center;
}
</style>
</head>
<body>
<div class="grid-container">
<div class="board" id="board"></div>
<div id="interface">
<span id="prompt"></span>
</div>
</div>
<script type="text/javascript">
//SETTINGS
var side = 'black' //select which side to play as
var rating = '1600';
var realistic = true;
var limit = 50;
//Define initial variables
var state = null;
var moveCount = 0;
if (side == 'black') {var notSide = 'white'} else{var notSide = 'black'}
var positionAnswer
var bestMove
var opponentMove
var PGN
//define functions
var addEvent = document.addEventListener ? function(target,type,action){
if(target){
target.addEventListener(type,action,false);
}
} : function(target,type,action){
if(target){
target.attachEvent('on' + type,action,false);
}
}
addEvent(document,'keydown',function(e){
e = e || window.event;
var key = e.which || e.keyCode;
if(key===13){ //Press the return key
keypress()
}
});
function keypress(){
if (state == 'questionPosition') { //Then reveal the answer
document.getElementById("prompt").innerHTML = positionAnswer;
setTimeout(() => {
document.getElementById("prompt").innerHTML = positionAnswer + "<br><br>key) What is " + side + "\'s best move?"
state = "questionBestMove";
}, 500)
}
}
</script>
<script type="module">
import {INPUT_EVENT_TYPE, COLOR, Chessboard, MARKER_TYPE} from "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/src/cm-chessboard/Chessboard.js"
import {BORDER_TYPE} from "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/src/cm-chessboard/Chessboard.js"
const chess = new Chess() //Creates a new Chess() object. Add a FEN string as an argument to start from a FEN.
function inputHandler(event) {
console.log("event", event)
event.chessboard.removeMarkers(undefined, MARKER_TYPE.dot)
//Before move. Clicking about, and showing dot for possible moves and such.
if (event.type === INPUT_EVENT_TYPE.moveStart) {
const moves = chess.moves({square: event.square, verbose: true});
for (const move of moves) {
event.chessboard.addMarker(move.to, MARKER_TYPE.dot)
}
return moves.length > 0
//Here is once a move has been attempted
} else if (event.type === INPUT_EVENT_TYPE.moveDone) {
if (state == "questionBestMove") {
const move = {from: event.squareFrom, to: event.squareTo} //gets which move was attempted from event
const result = chess.move(move) //gets result of move
bestMove = PGN[moveCount];
if (result){
if (result.san == bestMove.san) {
moveCount += 1;
event.chessboard.disableMoveInput()
event.chessboard.setPosition(chess.fen())
document.getElementById("prompt").innerHTML = "";
//Here is where I need it to ask a question if the bestMove.name is not null.
opponentMove = PGN[moveCount];
positionAnswer = PGN[moveCount].name;
moveCount += 1;
setTimeout(() => { // smoother with 500ms delay
chess.move({from: opponentMove.uci.slice(0,2), to: opponentMove.uci.slice(2,4)})
event.chessboard.enableMoveInput(inputHandler, side[0])
event.chessboard.setPosition(chess.fen())
if (positionAnswer){
document.getElementById("prompt").innerHTML = "event) What position is this?";
state = 'questionPosition';
}else{
document.getElementById("prompt").innerHTML = "key) What is " + side + "\'s best move?"
}
}, 500)
}else{
console.warn("That move is not the answer")
chess.undo();
event.chessboard.setPosition(chess.fen());
}
return result
} else { //If result returns null, then we will loop back to the begining of the function to have another go with new dots.
console.warn("invalid move", move)
}
return result
}
}
}
// The PGN is inputted manually here. Normally this would be extracted from the Lichess API.
if (side == 'black') {
PGN = [
{uci: "e2e4", san: "e4", name: "King's Pawn"},
{uci: "e7e5", san: "e5", name: "King's Pawn Game"},
{uci: "g1f3", san: "Nf3", name: "King's Knight Opening"},
{uci: "b8c6", san: "Nc6", name: "King's Knight Opening: Normal Variation"},
{uci: "f1b5", san: "Bb5", name: "Ruy Lopez"},
{uci: "a7a6", san: "a6", name: "Ruy Lopez: Morphy Defense"},
{uci: "b5c6", san: "Bxc6", name: "Ruy Lopez: Exchange Variation"},
{uci: "d7c6", san: "dxc6", name: null},
{uci: "b1c3", san: "Nc3", name: "Ruy Lopez: Exchange Variation, Keres Variation"},
{uci: "f7f6", san: "f6", name: null},
{uci: "e1g1", san: "O-O", name: null}
]
}else{
PGN = [
{uci: "e2e4", san: "e4", name: "King's Pawn"},
{uci: "e7e5", san: "e5", name: "King's Pawn Game"},
{uci: "g1f3", san: "Nf3", name: "King's Knight Opening"},
{uci: "b8c6", san: "Nc6", name: "King's Knight Opening: Normal Variation"},
{uci: "f1b5", san: "Bb5", name: "Ruy Lopez"},
{uci: "a7a6", san: "a6", name: "Ruy Lopez: Morphy Defense"},
{uci: "b5a4", san: "Ba4", name: null},
{uci: "b7b5", san: "b5", name: "Ruy Lopez: Morphy Defense, Caro Variation"},
{uci: "a4b3", san: "Bb3", name: null},
{uci: "g8f6", san: "Nf6", name: null},
{uci: "e1g1", san: "O-O", name: null}
]
}
console.log('PGN', PGN)
if (side == 'white'){
const board = new Chessboard(document.getElementById("board"), {
position: chess.fen(),
sprite: {url: "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/assets/images/chessboard-sprite-staunty.svg"},
style: {moveMarker: MARKER_TYPE.square, hoverMarker: undefined},
orientation: COLOR.white
})
state = 'questionBestMove';
document.getElementById("prompt").innerHTML = "What is " + side + "\'s best move?";
board.enableMoveInput(inputHandler, COLOR.white)
}
else{
const board = new Chessboard(document.getElementById("board"), {
position: chess.fen(),
sprite: {url: "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/assets/images/chessboard-sprite-staunty.svg"},
style: {moveMarker: MARKER_TYPE.square, hoverMarker: undefined},
orientation: COLOR.black
})
//Get opponent move and name
var opponentMove = PGN[moveCount];
positionAnswer = PGN[moveCount].name;
moveCount += 1;
setTimeout(() => { // smoother with 500ms delay
chess.move({from: opponentMove.uci.slice(0,2), to: opponentMove.uci.slice(2,4)})
board.enableMoveInput(inputHandler, COLOR.black)
board.setPosition(chess.fen())
if (positionAnswer){
document.getElementById("prompt").innerHTML = "What position is this?";
state = "questionPosition"
}
}, 500)
}
</script>
</body>
</html>
我为此post 压缩了它。通常,变量 PNG
是使用 Lichess API 创建的,但在本例中,我手动定义了两种游戏,一种用于黑色,另一种用于白色。我在中间选择了一些 'null' 名称的示例,以便我可以测试它是否正常工作。
如您所见,我等待对手位置问题,因为它在 keypress()
函数内。我对玩家位置问题做同样的事情时遇到的问题是,一旦问题被揭示,计算机就需要自动移动。我尝试从 keypress()
函数内部进行移动,但我无法让它工作,因为 event
未定义。我尝试以某种方式尝试将 event
传递给 keypress
但它没有用,而且我确信这不是最好的方法。
我认为正确的方法是妥善组织 inputHandler
。我已经重写了很多次,但我从来没有设法让它工作。逻辑让我感到困惑,我认为使用我的 state = "questionPosition"
想法来分离游戏状态可能会导致比它解决的问题更多的问题。如果我在 r/badcode 上看到它,我不会感到惊讶。
如果有任何关于如何解决此问题的建议,我将不胜感激。
我post编辑的这个版本是几乎可以工作的版本,我认为它比我的一些版本更容易看到发生了什么随后。
我还在 https://alftheelf.github.io/Chess-Opening-Trainer/ 上传了一个实时版本。
有谁知道我该如何解决这个问题?我花了一个星期试图修复它,但我不知道了。
我终于成功了。
我最终将 inputHander
中的 event
重命名为 chessEvent
,以便它与 keypress()
中的 event
分开。然后我克隆 chessEvent
并在按键功能中使用它。 chess
也必须与 inputHandler
一起设为全局变量,我通过将其设为变量来实现。
inputHandler1 = function inputHandler(chessEvent) {...
现在我可以在 keypress()
内控制电路板,而且效果很好。
我正在使用 cm-chessboard and chess.js 创建国际象棋开局训练器。这个想法是从 Lichess Opening Explorer API 中提取连续的国际象棋位置,玩家通过移动下一个最佳移动来测试自己 and/or 回答关于开口名称的问题。
不过,我有点儿头疼了。
目前,它只对轮到计算机的用户进行提问。我无法找到一种方法让它等待用户按下 return 键,然后再显示有关玩家移动的答案。
这是我的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chess Opening Trainer</title>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"/>
<link rel="stylesheet" href="https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/styles/cm-chessboard.css"/>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.2/chess.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style type="text/css">
body{font-family: 'Montserrat', sans-serif;}
.grid-container {
display: grid;
grid-template-columns: 1fr 0.6fr;
grid-template-rows: 1fr;
gap: 0px 0px;
}
.board {
grid-area: 1 / 1 / 2 / 3;
width: 800px;
max-width: 800px;
}
#interface {
grid-area: 1 / 2 / 2 / 3;
border-style: solid;
border-width: thin;
width: 400px;
max-width: 400px;
}
p{
width: 100%;
align: center;
}
</style>
</head>
<body>
<div class="grid-container">
<div class="board" id="board"></div>
<div id="interface">
<span id="prompt"></span>
</div>
</div>
<script type="text/javascript">
//SETTINGS
var side = 'black' //select which side to play as
var rating = '1600';
var realistic = true;
var limit = 50;
//Define initial variables
var state = null;
var moveCount = 0;
if (side == 'black') {var notSide = 'white'} else{var notSide = 'black'}
var positionAnswer
var bestMove
var opponentMove
var PGN
//define functions
var addEvent = document.addEventListener ? function(target,type,action){
if(target){
target.addEventListener(type,action,false);
}
} : function(target,type,action){
if(target){
target.attachEvent('on' + type,action,false);
}
}
addEvent(document,'keydown',function(e){
e = e || window.event;
var key = e.which || e.keyCode;
if(key===13){ //Press the return key
keypress()
}
});
function keypress(){
if (state == 'questionPosition') { //Then reveal the answer
document.getElementById("prompt").innerHTML = positionAnswer;
setTimeout(() => {
document.getElementById("prompt").innerHTML = positionAnswer + "<br><br>key) What is " + side + "\'s best move?"
state = "questionBestMove";
}, 500)
}
}
</script>
<script type="module">
import {INPUT_EVENT_TYPE, COLOR, Chessboard, MARKER_TYPE} from "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/src/cm-chessboard/Chessboard.js"
import {BORDER_TYPE} from "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/src/cm-chessboard/Chessboard.js"
const chess = new Chess() //Creates a new Chess() object. Add a FEN string as an argument to start from a FEN.
function inputHandler(event) {
console.log("event", event)
event.chessboard.removeMarkers(undefined, MARKER_TYPE.dot)
//Before move. Clicking about, and showing dot for possible moves and such.
if (event.type === INPUT_EVENT_TYPE.moveStart) {
const moves = chess.moves({square: event.square, verbose: true});
for (const move of moves) {
event.chessboard.addMarker(move.to, MARKER_TYPE.dot)
}
return moves.length > 0
//Here is once a move has been attempted
} else if (event.type === INPUT_EVENT_TYPE.moveDone) {
if (state == "questionBestMove") {
const move = {from: event.squareFrom, to: event.squareTo} //gets which move was attempted from event
const result = chess.move(move) //gets result of move
bestMove = PGN[moveCount];
if (result){
if (result.san == bestMove.san) {
moveCount += 1;
event.chessboard.disableMoveInput()
event.chessboard.setPosition(chess.fen())
document.getElementById("prompt").innerHTML = "";
//Here is where I need it to ask a question if the bestMove.name is not null.
opponentMove = PGN[moveCount];
positionAnswer = PGN[moveCount].name;
moveCount += 1;
setTimeout(() => { // smoother with 500ms delay
chess.move({from: opponentMove.uci.slice(0,2), to: opponentMove.uci.slice(2,4)})
event.chessboard.enableMoveInput(inputHandler, side[0])
event.chessboard.setPosition(chess.fen())
if (positionAnswer){
document.getElementById("prompt").innerHTML = "event) What position is this?";
state = 'questionPosition';
}else{
document.getElementById("prompt").innerHTML = "key) What is " + side + "\'s best move?"
}
}, 500)
}else{
console.warn("That move is not the answer")
chess.undo();
event.chessboard.setPosition(chess.fen());
}
return result
} else { //If result returns null, then we will loop back to the begining of the function to have another go with new dots.
console.warn("invalid move", move)
}
return result
}
}
}
// The PGN is inputted manually here. Normally this would be extracted from the Lichess API.
if (side == 'black') {
PGN = [
{uci: "e2e4", san: "e4", name: "King's Pawn"},
{uci: "e7e5", san: "e5", name: "King's Pawn Game"},
{uci: "g1f3", san: "Nf3", name: "King's Knight Opening"},
{uci: "b8c6", san: "Nc6", name: "King's Knight Opening: Normal Variation"},
{uci: "f1b5", san: "Bb5", name: "Ruy Lopez"},
{uci: "a7a6", san: "a6", name: "Ruy Lopez: Morphy Defense"},
{uci: "b5c6", san: "Bxc6", name: "Ruy Lopez: Exchange Variation"},
{uci: "d7c6", san: "dxc6", name: null},
{uci: "b1c3", san: "Nc3", name: "Ruy Lopez: Exchange Variation, Keres Variation"},
{uci: "f7f6", san: "f6", name: null},
{uci: "e1g1", san: "O-O", name: null}
]
}else{
PGN = [
{uci: "e2e4", san: "e4", name: "King's Pawn"},
{uci: "e7e5", san: "e5", name: "King's Pawn Game"},
{uci: "g1f3", san: "Nf3", name: "King's Knight Opening"},
{uci: "b8c6", san: "Nc6", name: "King's Knight Opening: Normal Variation"},
{uci: "f1b5", san: "Bb5", name: "Ruy Lopez"},
{uci: "a7a6", san: "a6", name: "Ruy Lopez: Morphy Defense"},
{uci: "b5a4", san: "Ba4", name: null},
{uci: "b7b5", san: "b5", name: "Ruy Lopez: Morphy Defense, Caro Variation"},
{uci: "a4b3", san: "Bb3", name: null},
{uci: "g8f6", san: "Nf6", name: null},
{uci: "e1g1", san: "O-O", name: null}
]
}
console.log('PGN', PGN)
if (side == 'white'){
const board = new Chessboard(document.getElementById("board"), {
position: chess.fen(),
sprite: {url: "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/assets/images/chessboard-sprite-staunty.svg"},
style: {moveMarker: MARKER_TYPE.square, hoverMarker: undefined},
orientation: COLOR.white
})
state = 'questionBestMove';
document.getElementById("prompt").innerHTML = "What is " + side + "\'s best move?";
board.enableMoveInput(inputHandler, COLOR.white)
}
else{
const board = new Chessboard(document.getElementById("board"), {
position: chess.fen(),
sprite: {url: "https://alftheelf.github.io/Chess-Opening-Trainer/cm-chessboard/assets/images/chessboard-sprite-staunty.svg"},
style: {moveMarker: MARKER_TYPE.square, hoverMarker: undefined},
orientation: COLOR.black
})
//Get opponent move and name
var opponentMove = PGN[moveCount];
positionAnswer = PGN[moveCount].name;
moveCount += 1;
setTimeout(() => { // smoother with 500ms delay
chess.move({from: opponentMove.uci.slice(0,2), to: opponentMove.uci.slice(2,4)})
board.enableMoveInput(inputHandler, COLOR.black)
board.setPosition(chess.fen())
if (positionAnswer){
document.getElementById("prompt").innerHTML = "What position is this?";
state = "questionPosition"
}
}, 500)
}
</script>
</body>
</html>
我为此post 压缩了它。通常,变量 PNG
是使用 Lichess API 创建的,但在本例中,我手动定义了两种游戏,一种用于黑色,另一种用于白色。我在中间选择了一些 'null' 名称的示例,以便我可以测试它是否正常工作。
如您所见,我等待对手位置问题,因为它在 keypress()
函数内。我对玩家位置问题做同样的事情时遇到的问题是,一旦问题被揭示,计算机就需要自动移动。我尝试从 keypress()
函数内部进行移动,但我无法让它工作,因为 event
未定义。我尝试以某种方式尝试将 event
传递给 keypress
但它没有用,而且我确信这不是最好的方法。
我认为正确的方法是妥善组织 inputHandler
。我已经重写了很多次,但我从来没有设法让它工作。逻辑让我感到困惑,我认为使用我的 state = "questionPosition"
想法来分离游戏状态可能会导致比它解决的问题更多的问题。如果我在 r/badcode 上看到它,我不会感到惊讶。
如果有任何关于如何解决此问题的建议,我将不胜感激。
我post编辑的这个版本是几乎可以工作的版本,我认为它比我的一些版本更容易看到发生了什么随后。
我还在 https://alftheelf.github.io/Chess-Opening-Trainer/ 上传了一个实时版本。
有谁知道我该如何解决这个问题?我花了一个星期试图修复它,但我不知道了。
我终于成功了。
我最终将 inputHander
中的 event
重命名为 chessEvent
,以便它与 keypress()
中的 event
分开。然后我克隆 chessEvent
并在按键功能中使用它。 chess
也必须与 inputHandler
一起设为全局变量,我通过将其设为变量来实现。
inputHandler1 = function inputHandler(chessEvent) {...
现在我可以在 keypress()
内控制电路板,而且效果很好。