cm-chessboard-eight-queens-puzzle
Version:
TheeEight queens puzzle as an extensions for the cm-chessboard
200 lines (191 loc) • 7.54 kB
JavaScript
/**
* Author and copyright: Stefan Haack (https://shaack.com)
* Repository: https://github.com/shaack/cm-chessboard-eight-queens-puzzle
* License: MIT, see file 'LICENSE'
*/
import {INPUT_EVENT_TYPE} from "cm-chessboard/src/Chessboard.js"
import {Extension} from "cm-chessboard/src/model/Extension.js"
import {MARKER_TYPE, Markers} from "cm-chessboard/src/extensions/markers/Markers.js"
import {Position} from "cm-chessboard/src/model/Position.js"
import {MOVE_CANCELED_REASON} from "cm-chessboard/src/view/VisualMoveInput.js"
import {HtmlLayer} from "cm-chessboard/src/extensions/html-layer/HtmlLayer.js"
export const GAME_EVENT_TYPE = {
move: "move",
createPiece: "createPiece",
removePiece: "removePiece",
won: "won"
}
export class EightQueensPuzzle extends Extension {
/** @constructor */
constructor(chessboard, props = {}) {
super(chessboard)
this.props = {
wonText: "Very good,<br/>you did it!", // the text that is shown when the puzzle is solved
onGameEvent: undefined, // callback after each position change
markers: { // the markers used to show the moves and threats
moveInput: MARKER_TYPE.frame,
failure: MARKER_TYPE.circleDanger
}
}
Object.assign(this.props, props)
chessboard.addExtension(Markers, {
autoMarkers: this.props.markers.moveInput
})
chessboard.addExtension(HtmlLayer)
this.clickListener = this.onSquareClick.bind(this)
chessboard.context.addEventListener("click", this.clickListener)
chessboard.enableMoveInput((event) => {
switch (event.type) {
case INPUT_EVENT_TYPE.moveInputStarted:
return true
case INPUT_EVENT_TYPE.moveInputCanceled:
if (event.reason === MOVE_CANCELED_REASON.movedOutOfBoard) {
this.chessboard.setPiece(event.squareFrom, null)
if (this.props.onGameEvent) {
this.props.onGameEvent({
position: this.chessboard.getPosition(),
type: GAME_EVENT_TYPE.removePiece
})
}
}
return true
case INPUT_EVENT_TYPE.validateMoveInput:
if (this.props.onGameEvent) {
this.props.onGameEvent({
position: this.chessboard.getPosition(),
type: GAME_EVENT_TYPE.move
})
}
return true
case INPUT_EVENT_TYPE.moveInputFinished:
}
this.markThreatened()
})
this.markThreatened()
}
onSquareClick(event) {
const square = event.target.getAttribute("data-square")
if (square && !this.chessboard.getPiece(square)) {
const pieces = this.chessboard.state.position.getPieces()
if (pieces.length < 8) {
this.chessboard.setPiece(square, "bq")
if (this.props.onGameEvent) {
this.props.onGameEvent({
position: this.chessboard.getPosition(),
type: GAME_EVENT_TYPE.createPiece
})
}
this.markThreatened()
}
}
}
markThreatened() {
if(this.chessboard.getPosition() !== this.previousPosition) {
this.chessboard.removeMarkers()
let i = 0
let threadsFound = 0
for (let square of this.chessboard.state.position.squares) {
if (square) {
// mark all squares threatened by this piece
const rank = String.fromCharCode(97 + i % 8)
const file = Math.floor(i / 8) + 1
const square = "" + rank + file
if (this.isThreatened(square)) {
this.chessboard.addMarker(this.props.markers.failure, square)
threadsFound++
}
}
i++
}
if (threadsFound === 0) {
const pieces = this.chessboard.state.position.getPieces()
if (pieces.length === 8) {
this.wonAnimation()
if (this.props.onGameEvent) {
this.props.onGameEvent({
position: this.chessboard.getPosition(),
type: GAME_EVENT_TYPE.won
})
}
}
}
this.previousPosition = this.chessboard.getPosition()
}
}
wonAnimation() {
const layer = this.chessboard.addHtmlLayer(`<div class='text fx-won'><div>${this.props.wonText}</div></div>`)
const text = layer.querySelector("div.text")
layer.classList.add("fx-fade-in")
setTimeout(() => {
layer.addEventListener("click", () => {
text.classList.remove("fx-won")
layer.classList.add("fx-fade-in")
text.classList.add("fx-vanish")
layer.classList.add("fx-fade-out")
setTimeout(() => {
this.chessboard.removeHtmlLayer(layer)
}, 1000)
})
}, 1000)
}
isThreatened(square) {
const [x, y] = Position.squareToCoordinates(square)
// console.log("isThreatened", square, x, y)
// test file
for (let sx = 0; sx < 8; sx++) {
const sSquare = Position.coordinatesToSquare([sx, y])
if (sSquare === square) {
continue
}
const piece = this.chessboard.getPiece(sSquare)
if (piece) {
// console.log("a) threatened by", piece, sSquare)
return true
}
}
// test rank
for (let sy = 0; sy < 8; sy++) {
const sSquare = Position.coordinatesToSquare([x, sy])
if (sSquare === square) {
continue
}
const piece = this.chessboard.getPiece(sSquare)
if (piece) {
// console.log("b) threatened by", piece, sSquare)
return true
}
}
// test diagonal
for (let sy = 0; sy < 8; sy++) {
const sx = x + sy - y
if (sx < 0 || sx > 7) {
continue
}
const sSquare = Position.coordinatesToSquare([sx, sy])
if (sSquare === square) {
continue
}
const piece = this.chessboard.getPiece(sSquare)
if (piece) {
// console.log("c) threatened by", piece, sSquare)
return true
}
}
// test diagonal
for (let sy = 0; sy < 8; sy++) {
const sx = x - sy + y
if (sx < 0 || sx > 7) {
continue
}
const sSquare = Position.coordinatesToSquare([sx, sy])
if (sSquare === square) {
continue
}
const piece = this.chessboard.getPiece(sSquare)
if (piece) {
// console.log("d) threatened by", piece, sSquare)
return true
}
}
}
}