问题描述
我正在使用 cm-chessboard 和 chess.js 创建国际象棋开局训练器。这个想法是从 Lichess opening Explorer API 中提取连续的国际象棋位置,玩家通过移动下一个最佳移动和/或回答关于开局名称的问题来测试自己。
但是,我有点不适应。
目前,它只在计算机轮到用户提问。我想不出办法让它等待用户按下返回键,然后才显示有关玩家移动的答案。
这是我的代码:
<!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,action){
if(target){
target.attachEvent('on' + type,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",{uci: "e1g1",san: "O-O",name: null}
]
}else{
PGN = [
{uci: "e2e4",{uci: "b5a4",san: "Ba4",{uci: "b7b5",san: "b5",name: "Ruy Lopez: Morphy Defense,Caro Variation"},{uci: "a4b3",san: "Bb3",{uci: "g8f6",san: "Nf6",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"),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,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>
我已经为这篇文章浓缩了它。通常,变量 PNG
是使用 Lichess API 创建的,但在本示例中,我改为手动定义了两种游戏,一种用于黑色,一种用于白色。我选择了中间带有一些“空”名称的示例,以便我可以测试它是否正常工作。
如您所见,我让对手位置问题等待,因为它在 keypress()
函数中。我在对球员位置问题做同样的事情时遇到的问题是,一旦问题被揭示,计算机需要自动移动。我尝试从 keypress()
函数内部进行移动,但我无法使其工作,因为 event
未定义。我以某种方式尝试将 event
传递给 keypress
,但没有奏效,而且我确信这无论如何都不是最好的方法。
我认为正确的方法是正确组织 inputHandler
。我已经重写了很多次,但我从来没有设法让它工作。这个逻辑让我很困惑,我认为使用我的 state = "questionPosition"
想法来分离游戏状态可能会导致比它解决的问题更多的问题。如果我在 r/badcode 上看到它,我不会感到惊讶。 ?
我非常感谢有关如何解决此问题的任何建议。
我发布的这个版本是一个几乎可以运行的版本,我认为与我遵循的一些版本相比,它会更容易看出发生了什么。
我还在 https://alftheelf.github.io/Chess-Opening-Trainer/ 发布了实时版本。
有谁知道我该如何解决这个问题?我花了一个星期试图修复它,但我不知道了。
解决方法
我终于成功了。
我最终将 event
中的 inputHander
重命名为 chessEvent
,使其与 event
中的 keypress()
分开。然后我克隆了 chessEvent
并在按键功能中使用了它。 chess
还必须与 inputHandler
一起成为全局变量,这是我通过将其设为变量来实现的。
inputHandler1 = function inputHandler(chessEvent) {...
现在我可以从 keypress()
内控制板子,它运行良好。 ?