【发布时间】:2021-06-15 16:55:06
【问题描述】:
我正在使用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,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>
我已经为这篇文章浓缩了它。通常,变量PNG 是使用 Lichess API 创建的,但在本示例中,我手动定义了两个游戏,一个用于黑色,一个用于白色。我选择了中间有一些“空”名称的示例,以便测试它是否正常工作。
如您所见,我等待对手位置问题,因为它在 keypress() 函数内。我对玩家位置问题做同样的事情时遇到的问题是,一旦问题被揭示,计算机需要自动移动。我尝试从keypress() 函数内部进行移动,但我无法让它工作,因为event 没有定义。我尝试以某种方式将event 传递给keypress,但没有成功,而且我确信这并不是最好的方法。
我认为正确的方法是正确组织inputHandler。我已经重写了很多次,但我从来没有设法让它工作。逻辑让我感到困惑,我认为使用我的state = "questionPosition" 想法来分离游戏状态可能会导致比它解决的问题更多的问题。如果我在r/badcode 上看到这一点,我不会感到惊讶。 ????
我非常感谢有关如何解决此问题的任何建议。
我发布的这个版本是一个几乎可以工作的版本,我认为它比我之前的一些版本更容易看到正在发生的事情。
我还在https://alftheelf.github.io/Chess-Opening-Trainer/ 上发布了一个实时版本。
有谁知道我该如何解决这个问题?我花了一周的时间试图修复它,但我只是不知道了。
【问题讨论】:
标签: javascript html event-handling chess chessboard.js