diff --git a/frontend/index.html b/frontend/index.html index b2fd54e..110a5a1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,6 +5,6 @@ Chess - + diff --git a/frontend/main.js b/frontend/main.js index 2259ee2..6187056 100644 --- a/frontend/main.js +++ b/frontend/main.js @@ -1,133 +1,10 @@ -const BOX_SIDE = 80; -const INDEX_MARGIN = BOX_SIDE / 2; -const INDEX_LETTERS = 'abcdefgh'; - -function createCanvas() { - let canvas = document.createElement('canvas'); - let boardSide = BOX_SIDE * 8 + INDEX_MARGIN; - canvas.width = boardSide; - canvas.height = boardSide; - document.body.appendChild(canvas); - return canvas -} - -function drawChessBoard(canvas) { - let ctx = canvas.getContext('2d'); - - // Draw squares - ctx.fillStyle = '#c0c0c0'; - for (let r = 0; r < 8; r++) { - for (let c = 0; c < 8; c++) { - if ((r + c) % 2) ctx.fillRect(c*BOX_SIDE + INDEX_MARGIN, - r*BOX_SIDE + INDEX_MARGIN, - BOX_SIDE, BOX_SIDE); - } - } - - // Draw letters - ctx.fillStyle = 'black'; - let fontsize = INDEX_MARGIN / 2; - ctx.font = `${fontsize}px Monospace`; - for (let idx = 0; idx < 8; idx++) { - ctx.fillText(INDEX_LETTERS[idx], - BOX_SIDE*idx + BOX_SIDE/2 + INDEX_MARGIN - fontsize / 4, - INDEX_MARGIN / 2); - ctx.fillText((8-idx).toString(), - INDEX_MARGIN / 2 - fontsize / 4, - BOX_SIDE*idx + BOX_SIDE/2 + INDEX_MARGIN + fontsize / 4); - } -} - -function computeScreenCoords(position, canvas) { - let canvasRect = canvas.getBoundingClientRect(); - let rc = boardIndexToRowCol(position); - let topPx = canvasRect.top + INDEX_MARGIN + rc[0]*BOX_SIDE + window.pageYOffset; - let leftPx = canvasRect.left + INDEX_MARGIN + rc[1]*BOX_SIDE + window.pageXOffset; - return [topPx, leftPx] -} - -class Move { - constructor(kind, from, to) { - this.kind = kind; - this.from = from; - this.to = to; - } -} - -class Piece { - constructor(who, color, position) { - this.who = who; - this.color = color; - this.position = position; - this._im = null; - } - icon() { - return `./icons/${this.who}_${this.color}.svg` - } - draw(canvas) { - let [topPx, leftPx] = computeScreenCoords(this.position, canvas); - if (this._im === null) { - let im = new Image(BOX_SIDE, BOX_SIDE); - im.src = this.icon(); - im.onload = () => { - im.style.position = 'absolute'; - im.style.top = `${topPx}px`; - im.style.left = `${leftPx}px`; - }; - document.body.appendChild(im); - this._im = im; - } - else { - this._im.style.top = `${topPx}px`; - this._im.style.left = `${leftPx}px`; - } - } - undraw() { - if (this._im !== null) { - this._im.remove(); - this._im = null; - } - } -} - -class Configuration { - constructor(pieces) { - this.pieces = pieces - } - draw(canvas) { - for (let piece of this.pieces) { - piece.draw(canvas); - } - } - getAt(position) { - for (let piece of this.pieces) { - if (piece.position === position) return piece; - } - return null; - } - dropAt(position) { - for (let idx = 0; idx < this.pieces.length; idx++) { - if (this.pieces[idx].position === position) { - this.pieces[idx].undraw(); - this.pieces.splice(idx, 1); - return; - } - } - } - makeMove(move, canvas) { - let piece = this.getAt(move.from); - if (piece === null) return; - this.dropAt(move.to); - piece.position = move.to; - piece.draw(canvas); - } -} +import * as visuals from './visuals.js'; function setupBoard() { let pawns = []; - for (let x of INDEX_LETTERS) { + for (let x of 'abcdefgh') { pawns.push(new Piece('pawn', 'black', `${x}7`), - new Piece('pawn', 'white', `${x}2`)); + new Piece('pawn', 'white', `${x}2`)); } return new Configuration([ new Piece('rook', 'black', 'a8'), @@ -149,30 +26,122 @@ function setupBoard() { new Piece('bishop', 'white', 'f1'), new Piece('queen', 'white', 'd1'), new Piece('king', 'white', 'e1'), - ]) + ]); } -function boardIndexToRowCol(boardIndex) { - let colLetter = boardIndex[0]; - let rowDigit = boardIndex[1]; - let row = 8 - parseInt(rowDigit); - let col = INDEX_LETTERS.indexOf(colLetter); - return [row, col]; +class Backend { + constructor() { + this.config = setupBoard(); + } + getConfig() { + return this.config.clone(); + } + makeMove(move) { + this.config = this.config.makeMove(move); + return this.config.clone(); + } + getAvailableMoves(position) { + return [] + } } -async function demo() { - let canvas = createCanvas(); - drawChessBoard(canvas); - let config = setupBoard(); - config.draw(canvas); - await new Promise(r => setTimeout(r, 6000)); - config.makeMove({'kind': 'xxx', 'from': 'e2', 'to': 'e4'}, canvas); - await new Promise(r => setTimeout(r, 2000)); - config.makeMove({'kind': 'xxx', 'from': 'e7', 'to': 'e5'}, canvas); - await new Promise(r => setTimeout(r, 2000)); - config.makeMove({'kind': 'xxx', 'from': 'd2', 'to': 'd4'}, canvas); - await new Promise(r => setTimeout(r, 2000)); - config.makeMove({'kind': 'xxx', 'from': 'e5', 'to': 'd4'}, canvas); +class Chess { + constructor() { + this.backend = new Backend(); + this.configVis = null; + this.canvas = new visuals.Canvas(); + this.moveSource = null; + this.canMoveTo = []; + this.syncBackend(); + document.onclick = (ev) => this.click(ev); + console.log(this.moveSource); + } + syncBackend() { + let config = this.backend.getConfig(); + if (this.configVis !== null) { + this.configVis.undraw(); + } + this.configVis = new visuals.ConfigVis(config.pieces); + this.configVis.draw(this.canvas); + } + showAvailableMoves(canMoveTo) { + } + click(ev) { + console.log(this); + if (this.moveSource === null) { + this._firstClick(ev); + } + else { + this._secondClick(ev); + } + } + _firstClick(ev) { + let rcA = this.canvas.screenCoordsToRowCol(ev.clientX, ev.clientY); + if (rcA === null) return; + let positionA = visuals.rowColToBoardIndex(...rcA); + this.showAvailableMoves(this.backend.getAvailableMoves(positionA)); + this.moveSource = positionA; + } + _secondClick(ev) { + let rcB = this.canvas.screenCoordsToRowCol(ev.clientX, ev.clientY); + if (rcB !== null) { + let move = { + from: this.moveSource, + to: visuals.rowColToBoardIndex(...rcB), + } + this.backend.makeMove(move); + this.syncBackend(); + } + this.moveSource = null; + } } -demo(); +class Move { + constructor(kind, from, to) { + this.kind = kind; + this.from = from; + this.to = to; + } +} + +class Piece { + constructor(who, color, position) { + this.who = who; + this.color = color; + this.position = position; + } + clone() { + return new Piece(this.who, this.color, this.position); + } + moveTo(position) { + return new Piece(this.who, this.color, position); + } +} + +class Configuration { + constructor(pieces) { + this.pieces = pieces + } + clone() { + let pieces = []; + for (let piece of this.pieces) { + pieces.push(piece.clone()); + } + return new Configuration(pieces); + } + getAt(position) { + return this.pieces.find(piece => piece.position === position) || null; + } + dropAt(position) { + return this.pieces.filter(piece => piece.position !== position); + } + makeMove(move) { + const pieceToMove = this.getAt(move.from); + if (pieceToMove === null) return this; + else return new Configuration([ + ...this.dropAt(move.from), pieceToMove.moveTo(move.to) + ]) + } +} + +new Chess(); diff --git a/frontend/visuals.js b/frontend/visuals.js new file mode 100644 index 0000000..5c0362e --- /dev/null +++ b/frontend/visuals.js @@ -0,0 +1,123 @@ +const BOX_SIDE = 80; +const INDEX_MARGIN = BOX_SIDE / 2; +const INDEX_LETTERS = 'abcdefgh'; + +export class Canvas { + constructor() { + this._canvas = document.createElement('canvas'); + let boardSide = BOX_SIDE * 8 + INDEX_MARGIN; + this._canvas.width = boardSide; + this._canvas.height = boardSide; + document.body.appendChild(this._canvas); + this.drawChessBoard(); + } + drawChessBoard() { + // Draw squares + let ctx = this._canvas.getContext('2d'); + ctx.fillStyle = '#c0c0c0'; + for (let r = 0; r < 8; r++) { + for (let c = 0; c < 8; c++) { + if ((r + c) % 2) ctx.fillRect(c * BOX_SIDE + INDEX_MARGIN, + r * BOX_SIDE + INDEX_MARGIN, + BOX_SIDE, BOX_SIDE); + } + } + + // Draw letters + ctx.fillStyle = 'black'; + let fontsize = INDEX_MARGIN / 2; + ctx.font = `${fontsize}px Monospace`; + for (let idx = 0; idx < 8; idx++) { + ctx.fillText(INDEX_LETTERS[idx], + BOX_SIDE * idx + BOX_SIDE / 2 + INDEX_MARGIN - fontsize / 4, + INDEX_MARGIN / 2); + ctx.fillText((8 - idx).toString(), + INDEX_MARGIN / 2 - fontsize / 4, + BOX_SIDE * idx + BOX_SIDE / 2 + INDEX_MARGIN + fontsize / 4); + } + } + screenCoordsToRowCol(cx, cy) { + let canvasRect = this._canvas.getBoundingClientRect(); + if (cx > canvasRect.right || cy > canvasRect.bottom) { + return null; + } + let canvasX = cx - canvasRect.left; + let canvasY = cy - canvasRect.top; + let row = Math.floor((canvasY - INDEX_MARGIN) / BOX_SIDE); + let col = Math.floor((canvasX - INDEX_MARGIN) / BOX_SIDE); + return [row, col]; + } + rowColToScreenCoords(row, col) { + let canvasRect = this._canvas.getBoundingClientRect(); + let topPx = canvasRect.top + INDEX_MARGIN + row * BOX_SIDE + window.pageYOffset; + let leftPx = canvasRect.left + INDEX_MARGIN + col * BOX_SIDE + window.pageXOffset; + return [topPx, leftPx] + } +} + +function boardIndexToRowCol(boardIndex) { + let [colLetter, rowDigit] = boardIndex; + let row = 8 - parseInt(rowDigit); + let col = INDEX_LETTERS.indexOf(colLetter); + return [row, col]; +} + +export function rowColToBoardIndex(row, col) { + let rowDigit = 8 - row; + let colLetter = INDEX_LETTERS[col]; + return `${colLetter}${rowDigit}`; +} + +export class PieceVis { + constructor(piece) { + this.icon = `./icons/${piece.who}_${piece.color}.svg`; + this.position = piece.position; + this.im = null; + } + draw(canvas) { + let [topPx, leftPx] = canvas.rowColToScreenCoords( + ...boardIndexToRowCol(this.position), canvas + ); + if (this.im === null) { + let im = new Image(BOX_SIDE, BOX_SIDE); + im.src = this.icon; + im.onload = () => { + im.style.position = 'absolute'; + im.style.top = `${topPx}px`; + im.style.left = `${leftPx}px`; + }; + document.body.appendChild(im); + this.im = im; + } + else { + this.im.style.top = `${topPx}px`; + this.im.style.left = `${leftPx}px`; + } + } + undraw() { + if (this.im !== null) { + this.im.remove(); + this.im = null; + } + } +} + +export class ConfigVis { + constructor(configuration) { + this.configuration = configuration; + this.piecesVis = new Map(); + } + draw(canvas) { + for (const piece of this.configuration) { + let pieceVis = new PieceVis(piece); + pieceVis.draw(canvas); + this.piecesVis.set(piece.position, pieceVis); + } + } + undraw() { + for (let [position, pieceVis] of this.piecesVis) { + pieceVis.undraw(); + } + this.piecesVis.clear(); + } +}