I'm using cm-chessboard to create a chess opening trainer game. I will be controlling one side and eventually I will get the computer to move specific openings.
cm-chessboard has an examples page, and I started of with this example which is closest to my final idea. At the moment, the computer moves randomly from the set of possible moves.
The example from github is only ever played from white's side. I have tweaked the code to allow for black to play. However, when the game starts and I am playing black, I have to click a black piece to stimulate an event which causes white to make its first move, and we play on from there. This is because white always goes first.
Here is my code:
<!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="/cm-chessboard/styles/cm-chessboard.css"/>
<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>
</head>
<body>
<div class="board" id="board" style="width: 800px; max-width: 800px"></div>
<div id="output"></div>
<script type="module">
var side = 'black'
if (side == 'white'){var colour = 'w'}else{var colour = 'b'}
import {INPUT_EVENT_TYPE, COLOR, Chessboard, MARKER_TYPE} from "/cm-chessboard/src/cm-chessboard/Chessboard.js"
import {BORDER_TYPE} from "/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.
var oneTimeBool = true;
function inputHandler(event) {
console.log("event", event)
event.chessboard.removeMarkers(undefined, MARKER_TYPE.dot)
//Do this only once
if (oneTimeBool == true && side == 'black'){
event.chessboard.disableMoveInput()
event.chessboard.setPosition(chess.fen())
const possibleMoves = chess.moves({verbose: true})
if (possibleMoves.length > 0) {
const randomIndex = Math.floor(Math.random() * possibleMoves.length)
const randomMove = possibleMoves[randomIndex]
setTimeout(() => { // smoother with 500ms delay
chess.move({from: randomMove.from, to: randomMove.to})
event.chessboard.enableMoveInput(inputHandler, colour)
event.chessboard.setPosition(chess.fen())
}, 500)
}
oneTimeBool = false;
}
//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) {
const move = {from: event.squareFrom, to: event.squareTo} //gets which move was attempted from event
const result = chess.move(move) //gets result of move
if (result) { //if result is anyting then we processed
event.chessboard.disableMoveInput()
event.chessboard.setPosition(chess.fen())
const possibleMoves = chess.moves({verbose: true})
if (possibleMoves.length > 0) {
const randomIndex = Math.floor(Math.random() * possibleMoves.length)
const randomMove = possibleMoves[randomIndex]
setTimeout(() => { // smoother with 500ms delay
chess.move({from: randomMove.from, to: randomMove.to})
event.chessboard.enableMoveInput(inputHandler, colour)
event.chessboard.setPosition(chess.fen())
}, 500)
}
} 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
}
}
const board = new Chessboard(document.getElementById("board"), {
position: chess.fen(),
sprite: {url: "/cm-chessboard/assets/images/chessboard-sprite-staunty.svg"},
style: {moveMarker: MARKER_TYPE.square, hoverMarker: undefined},
orientation: colour
})
board.enableMoveInput(inputHandler, colour)
</script>
</body>
</html>
It's pretty bad, I know. I'm sure there's much more elegant ways to do this.
My problem is that when board.enableMoveInput(inputHandler_black, COLOR.black)
calls the inputhandler_black
function, the function waits for an event in order to begin. When I play as white and inputHandler_white
is called, this is fine because I have to make the first move anyway. When I play as black, I want the computer to automatically play the move without me having to click a random black piece to get it to start.
Anyone know how can I do that? Is there a way to force it into the function somehow without manually creating an event with the mouse?