From e377dc36680f96e145e6c0c8cd89cb3d0d9336c8 Mon Sep 17 00:00:00 2001 From: KingOfDog Date: Mon, 8 Apr 2019 21:52:07 +0200 Subject: [PATCH] Initial commit --- assets/bishop_black.svg | 1 + assets/bishop_white.svg | 1 + assets/king_black.svg | 1 + assets/king_white.svg | 1 + assets/knight_black.svg | 1 + assets/knight_white.svg | 1 + assets/pawn_black.svg | 1 + assets/pawn_white.svg | 1 + assets/queen_black.svg | 1 + assets/queen_white.svg | 1 + assets/rook_black.svg | 1 + assets/rook_white.svg | 1 + index.html | 14 + main.js | 578 ++++++++++++++++++++++++++++++++++++++++ style.css | 183 +++++++++++++ 15 files changed, 787 insertions(+) create mode 100644 assets/bishop_black.svg create mode 100644 assets/bishop_white.svg create mode 100644 assets/king_black.svg create mode 100644 assets/king_white.svg create mode 100644 assets/knight_black.svg create mode 100644 assets/knight_white.svg create mode 100644 assets/pawn_black.svg create mode 100644 assets/pawn_white.svg create mode 100644 assets/queen_black.svg create mode 100644 assets/queen_white.svg create mode 100644 assets/rook_black.svg create mode 100644 assets/rook_white.svg create mode 100644 index.html create mode 100644 main.js create mode 100644 style.css diff --git a/assets/bishop_black.svg b/assets/bishop_black.svg new file mode 100644 index 0000000..e101803 --- /dev/null +++ b/assets/bishop_black.svg @@ -0,0 +1 @@ +bishop_black \ No newline at end of file diff --git a/assets/bishop_white.svg b/assets/bishop_white.svg new file mode 100644 index 0000000..4db693a --- /dev/null +++ b/assets/bishop_white.svg @@ -0,0 +1 @@ +bishop_white \ No newline at end of file diff --git a/assets/king_black.svg b/assets/king_black.svg new file mode 100644 index 0000000..acca166 --- /dev/null +++ b/assets/king_black.svg @@ -0,0 +1 @@ +king_black \ No newline at end of file diff --git a/assets/king_white.svg b/assets/king_white.svg new file mode 100644 index 0000000..be66210 --- /dev/null +++ b/assets/king_white.svg @@ -0,0 +1 @@ +king_white \ No newline at end of file diff --git a/assets/knight_black.svg b/assets/knight_black.svg new file mode 100644 index 0000000..6033e8d --- /dev/null +++ b/assets/knight_black.svg @@ -0,0 +1 @@ +knight_black \ No newline at end of file diff --git a/assets/knight_white.svg b/assets/knight_white.svg new file mode 100644 index 0000000..0368848 --- /dev/null +++ b/assets/knight_white.svg @@ -0,0 +1 @@ +knight_white \ No newline at end of file diff --git a/assets/pawn_black.svg b/assets/pawn_black.svg new file mode 100644 index 0000000..ddefd35 --- /dev/null +++ b/assets/pawn_black.svg @@ -0,0 +1 @@ +pawn_black \ No newline at end of file diff --git a/assets/pawn_white.svg b/assets/pawn_white.svg new file mode 100644 index 0000000..ec044f8 --- /dev/null +++ b/assets/pawn_white.svg @@ -0,0 +1 @@ +pawn_white \ No newline at end of file diff --git a/assets/queen_black.svg b/assets/queen_black.svg new file mode 100644 index 0000000..8512e0b --- /dev/null +++ b/assets/queen_black.svg @@ -0,0 +1 @@ +queen_black \ No newline at end of file diff --git a/assets/queen_white.svg b/assets/queen_white.svg new file mode 100644 index 0000000..be8b357 --- /dev/null +++ b/assets/queen_white.svg @@ -0,0 +1 @@ +queen_white \ No newline at end of file diff --git a/assets/rook_black.svg b/assets/rook_black.svg new file mode 100644 index 0000000..80b3393 --- /dev/null +++ b/assets/rook_black.svg @@ -0,0 +1 @@ +rook_black \ No newline at end of file diff --git a/assets/rook_white.svg b/assets/rook_white.svg new file mode 100644 index 0000000..d48a76c --- /dev/null +++ b/assets/rook_white.svg @@ -0,0 +1 @@ +rook_white \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..80ec1dd --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + Chess.js + + + + +
+ + + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..d4b6b6d --- /dev/null +++ b/main.js @@ -0,0 +1,578 @@ +HTMLElement.prototype.addChild = function (tagName, classList) { + const el = document.createElement(tagName); + + if (typeof classList === 'string') { + classList = [classList]; + } + + el.classList.add(...classList); + + this.appendChild(el); + return el; +}; + +const container = document.getElementById('game-container'); +const figuresContainer = container.addChild('div', 'figures'); +const deadFiguresContainers = { + black: container.addChild('div', ['dead-figures', 'black']), + white: container.addChild('div', ['dead-figures', 'white']), +}; +const board = []; +const figures = []; + +let currentPlayer = 'w'; +let castlings = { + w: { + short: true, + long: true, + }, + b: { + short: true, + long: true, + }, +}; +let enpassant = null; +let halfTurns = 0; +let turn = 1; + +let selectedFigure; + +class Field { + constructor(x, y, el) { + this.element = el; + this.x = x; + this.y = y; + + if ((x % 2 === 0 && y % 2 === 0) || (x % 2 !== 0 && y % 2 !== 0)) { + this.color = 'w'; + } else { + this.color = 'b'; + } + this.element.classList.add(this.color === 'w' ? 'white' : 'black'); + + this.element.addEventListener('click', () => { + const figure = figures.find(figure => figure.color === currentPlayer && figure.x === this.x && figure.y === this.y); + + if (!!selectedFigure) { + selectedFigure.deselectFigure(); + } + + if (!!figure) { + figure.selectFigure(); + } + + return false; + }); + + this.element.addEventListener('contextmenu', (e) => { + e.preventDefault(); + + if (!!selectedFigure) { + selectedFigure.moveTo(this.x, this.y); + } + + return false; + }); + + this.isFocused = false; + } + + setFocus(isFocused) { + if (this.isFocused === isFocused) + return; + + this.isFocused = isFocused; + + if (isFocused) { + this.element.classList.add('possible-target'); + } else { + this.element.classList.remove('possible-target'); + } + } +} + +class Figure { + constructor(x, y, color, name, char) { + this.element = figuresContainer.addChild('div', ['figure', color === 'b' ? 'black' : 'white', name]); + this.element.style.transform = `translate(${x * 100}%, ${y * 100}%)`; + + this.hasMoved = false; + this.x = x; + this.y = y; + this.color = color; + this.char = char; + } + + destroy() { + const index = figures.findIndex(figure => figure === this); + figures.splice(index, 1); + + this.element.parentNode.removeChild(this.element); + + this.element.style.transform = 'none'; + if (this.color === 'b') { + deadFiguresContainers.black.appendChild(this.element); + } else { + deadFiguresContainers.white.appendChild(this.element); + } + } + + getMoveSet() { + return []; + } + + getMoves(moveX, moveY, x, y) { + if (x < 0 || x >= 8 || y < 0 || y >= 8) + return []; + + const moves = []; + + let stepX = moveX, + stepY = moveY, + repeatX = false, + repeatY = false; + + if (typeof moveX === 'string') { + stepX = parseInt(moveX.split('*')[0]); + repeatX = true; + } + if (typeof moveY === 'string') { + stepY = parseInt(moveY.split('*')[0]); + repeatY = true; + } + + x += stepX; + y += stepY; + + if (x < 0 || x >= 8 || y < 0 || y >= 8) + return []; + + const figure = figures.find(figure => figure.color === this.color && figure.x === x && figure.y === y); + if (!!figure) + return []; + + moves.push([x, y]); + + if (repeatX || repeatY) { + moves.push(...this.getMoves(moveX, moveY, x, y)); + } + + return moves; + } + + getPossibleTargets() { + const moveSet = this.getMoveSet(); + + const targets = []; + + moveSet.forEach(move => { + const moved = this.getMoves(move[0], move[1], this.x, this.y); + targets.push(...moved.map(move => board[move[0]][move[1]])); + }); + + return targets; + } + + moveTo(x, y) { + this.deselectFigure(); + const targets = this.getPossibleTargets(); + if (!targets.includes(board[x][y])) { + return; + } + + if (checkKing(this.color)) { + return; + } + + const figure = figures.find(figure => figure.x === x && figure.y === y); + if (!!figure) { + if (figure.color === this.color) { + return; + } else { + figure.destroy(); + } + } + + if (!this.hasMoved) { + this.hasMoved = true; + } + + this.x = x; + this.y = y; + + this.element.style.transform = `translate(${this.x * 100}%, ${this.y * 100}%)`; + + switchSides(); + } + + selectFigure() { + if (!!selectedFigure) + selectedFigure.deselectFigure(); + + selectedFigure = this; + const targets = this.getPossibleTargets(); + targets.forEach(target => { + target.setFocus(true); + }); + + container.classList.add('focused'); + } + + deselectFigure() { + selectedFigure = null; + + const targets = this.getPossibleTargets(); + targets.forEach(target => { + target.setFocus(false); + }); + + container.classList.remove('focused'); + } + + getExportChar() { + if (this.color === 'w') { + return this.char.toUpperCase(); + } else { + return this.char.toLowerCase(); + } + } + + static deselectAll() { + selectedFigure = null; + + board.forEach(col => { + col.forEach(field => { + field.setFocus(false); + }); + }); + } +} + +class Pawn extends Figure { + constructor(x, y, color) { + super(x, y, color, 'pawn', 'p'); + } + + getMoveSet() { + let moves = []; + if (this.color === 'b') { + moves.push([0, 1, true]); + if (!this.hasMoved) { + moves.push([0, 2, true]); + } + moves.push([1, 1, false]); + moves.push([-1, 1, false]); + } else { + moves.push([0, -1, true]); + if (!this.hasMoved) { + moves.push([0, -2, true]); + } + moves.push([1, -1, false]); + moves.push([-1, -1, false]); + } + + for (let i = 0; i < moves.length; i++) { + const move = moves[i]; + const figure = getFigure(this.x + move[0], this.y + move[1]); + if ((move[2] && !!figure) || (!move[2] && !figure)) { + moves.splice(i, 1); + i--; + } + } + + moves = moves.map(move => [move[0], move[1]]); + + return moves; + } + + moveTo(x, y) { + super.moveTo(x, y); + + if (this.color === 'w' && this.y === 0) { + this.destroy(); + // TODO: Add new figure + } else if (this.color === 'b' && this.y === 7) { + this.destroy(); + // TODO: Add new figure + } + } +} + +class Rook extends Figure { + constructor(x, y, color) { + super(x, y, color, 'rook', 'r'); + } + + getMoveSet() { + return [ + [0, '1*'], + ['1*', 0], + [0, '-1*'], + ['-1*', 0], + ]; + } +} + +class Knight extends Figure { + constructor(x, y, color) { + super(x, y, color, 'knight', 'n'); + } + + getMoveSet() { + return [ + [2, 1], + [2, -1], + [1, 2], + [-1, 2], + [-2, 1], + [-2, -1], + [1, -2], + [-1, -2], + ]; + } +} + +class Bishop extends Figure { + constructor(x, y, color) { + super(x, y, color, 'bishop', 'b'); + } + + getMoveSet() { + return [ + ['1*', '1*'], + ['-1*', '1*'], + ['-1*', '-1*'], + ['1*', '-1*'], + ]; + } +} + +class Queen extends Figure { + constructor(x, y, color) { + super(x, y, color, 'queen', 'q'); + } + + getMoveSet() { + return [ + ['1*', 0], + ['-1*', 0], + ['1*', '1*'], + ['-1*', '1*'], + ['-1*', '-1*'], + ['1*', '-1*'], + [0, '1*'], + [0, '-1*'], + ]; + } +} + +class King extends Figure { + constructor(x, y, color) { + super(x, y, color, 'king', 'k'); + } + + getMoveSet() { + return [ + [-1, 0], + [-1, -1], + [0, -1], + [1, -1], + [1, 0], + [1, 1], + [0, 1], + [-1, 1], + ]; + } +} + +function createBoard() { + const boardEl = container.addChild('div', 'board'); + for (let x = 0; x < 8; x++) { + board.push([]); + const colEl = boardEl.addChild('div', 'col'); + + for (let y = 0; y < 8; y++) { + const element = colEl.addChild('div', 'field'); + const field = new Field(x, y, element); + board[x].push(field); + } + } +} + +function createFigures(color) { + for (let i = 0; i < 8; i++) { + figures.push(new Pawn(i, color === 'b' ? 1 : 6, color)); + } + figures.push(new Rook(0, color === 'b' ? 0 : 7, color)); + figures.push(new Knight(1, color === 'b' ? 0 : 7, color)); + figures.push(new Bishop(2, color === 'b' ? 0 : 7, color)); + figures.push(new Queen(3, color === 'b' ? 0 : 7, color)); + figures.push(new King(4, color === 'b' ? 0 : 7, color)); + figures.push(new Bishop(5, color === 'b' ? 0 : 7, color)); + figures.push(new Knight(6, color === 'b' ? 0 : 7, color)); + figures.push(new Rook(7, color === 'b' ? 0 : 7, color)); +} + +function getFigure(x, y) { + return figures.find(figure => figure.x === x && figure.y === y); +} + +function checkKing(color) { + const king = figures.find(figure => figure instanceof King && figure.color === color); + + let isChecked = false; + figures.forEach(figure => { + if (figure.color === color) + return; + + const possibleMoves = figure.getPossibleTargets(); + possibleMoves.forEach(move => { + if (move[0] === king.x && move[1] === king.y) { + isChecked = true; + return false; + } + }); + + if (isChecked) + return false; + }); + + return isChecked; +} + +function switchSides() { + currentPlayer = currentPlayer === 'b' ? 'w' : 'b'; + + if (currentPlayer === 'b') { + container.classList.add('black-turn'); + } else { + container.classList.remove('black-turn'); + + turn++; + } +} + +function exportGame() { + let exportedGame = ''; + + for (let y = 0; y < 8; y++) { + let emptyCount = 0; + for (let x = 0; x < 8; x++) { + const figure = getFigure(x, y); + + if (!!figure) { + if (emptyCount > 0) { + exportedGame += emptyCount; + emptyCount = 0; + } + + exportedGame += figure.getExportChar(); + } else { + emptyCount++; + } + } + + if (emptyCount > 0) { + exportedGame += emptyCount; + } + + if (y !== 7) { + exportedGame += '/'; + } + } + + exportedGame += ` ${currentPlayer} `; + + let castling = false; + if (castlings.w.short) { + exportedGame += 'K'; + castling = true; + } + if (castlings.w.long) { + exportedGame += 'Q'; + castling = true; + } + if (castlings.b.short) { + exportedGame += 'k'; + castling = true; + } + if (castlings.b.long) { + exportedGame += 'q'; + castling = true; + } + + if (!castling) { + exportedGame += '-'; + } + + exportedGame += ` ${!!enpassant ? coordinatesToString(enpassant) : '-'} ${halfTurns} ${turn}`; + + return exportedGame; +} + +const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; + +function coordinatesToString(coordinates) { + return letters[coordinates[0]] + (8 - coordinates[1]); +} + +function stringToCoordinates() { + +} + +const figuresTable = { + p: Pawn, + r: Rook, + n: Knight, + b: Bishop, + q: Queen, + k: King, +}; + +function importGame(string) { + const groups = string.split(' '); + + const rows = groups[0].split('/'); + if (rows.length !== 8) + return false; + rows.forEach((row, y) => { + let len = 0; + for (let x = 0; x < row.length; x++) { + const char = row.charAt(x); + + if (char.isNumber()) { + len += parseInt(char); + } else { + const figureType = figuresTable[char.toLowerCase()]; + console.log(char, figureType); + const figure = new figureType(x, y, char.isUpperCase() ? 'w' : 'b'); + figures.push(figure); + len++; + } + } + + if(len !== 8) { + return false; + } + }); + + +} + +function isDefiniteTurn() { + +} + +String.prototype.isNumber = function () { + return !isNaN(parseInt(this), 10); +}; + +String.prototype.isUpperCase = function () { + return this.toString() === this.toUpperCase(); +}; + +createBoard(); +// createFigures('w'); +// createFigures('b'); + +importGame('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'); diff --git a/style.css b/style.css new file mode 100644 index 0000000..f198294 --- /dev/null +++ b/style.css @@ -0,0 +1,183 @@ +#game-container { + position: relative; + display: grid; + width: 800px; + height: 1220px; + grid-gap: 10px; + grid-template-areas: "dead-black" "field" "dead-white"; + grid-template-rows: 1fr 4fr 1fr; + -webkit-transform-origin: center center; + -moz-transform-origin: center center; + -ms-transform-origin: center center; + -o-transform-origin: center center; + transform-origin: center center; + -webkit-transition: transform 1s 1s; + -moz-transition: transform 1s 1s; + -ms-transition: transform 1s 1s; + -o-transition: transform 1s 1s; + transition: transform 1s 1s; +} + +#game-container.black-turn { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); +} + +#game-container .board { + position: relative; + grid-area: field; +} + +#game-container .board .col { + display: inline-block; + width: 12.5%; + height: 100%; +} + +#game-container .board .col .field { + position: relative; + box-sizing: border-box; + width: 100%; + height: 12.5%; +} + +#game-container .board .col .field.white { + background-color: #eee; +} + +#game-container .board .col .field.black { + background-color: #000; +} + +#game-container .board .col .field.possible-target { + border: 4px solid #f0f; +} + +#game-container.focused .board .col .field:after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, .1); + content: ""; + pointer-events: none; +} + +#game-container.focused .board .col .field.possible-target:after { + background: transparent; +} + +#game-container .figures { + position: relative; + z-index: 1; + pointer-events: none; + grid-area: field; +} + +#game-container .figures .figure { + position: absolute; + top: 0; + left: 0; + width: 12.5%; + height: 12.5%; + transition: transform .3s; +} + +.figure:before { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 75%; + height: 75%; + background-position: center center; + background-repeat: no-repeat; + background-size: 100% 100%; + color: #f0f; + content: ''; + -webkit-transition: transform 1s 1s; + -moz-transition: transform 1s 1s; + -ms-transition: transform 1s 1s; + -o-transition: transform 1s 1s; + transition: transform 1s 1s; +} + +.black-turn .figure:before { + -webkit-transform: translate(-50%, -50%) rotate(-180deg); + -moz-transform: translate(-50%, -50%) rotate(-180deg); + -ms-transform: translate(-50%, -50%) rotate(-180deg); + -o-transform: translate(-50%, -50%) rotate(-180deg); + transform: translate(-50%, -50%) rotate(-180deg); +} + +.pawn.white:before { + background-image: url("assets/pawn_white.svg"); +} + +.rook.white:before { + background-image: url("assets/rook_white.svg"); +} + +.knight.white:before { + background-image: url("assets/knight_white.svg"); +} + +.bishop.white:before { + background-image: url("assets/bishop_white.svg"); +} + +.queen.white:before { + background-image: url("assets/queen_white.svg"); +} + +.king.white:before { + background-image: url("assets/king_white.svg"); +} + +.pawn.black:before { + background-image: url("assets/pawn_black.svg"); +} + +.rook.black:before { + background-image: url("assets/rook_black.svg"); +} + +.knight.black:before { + background-image: url("assets/knight_black.svg"); +} + +.bishop.black:before { + background-image: url("assets/bishop_black.svg"); +} + +.queen.black:before { + background-image: url("assets/queen_black.svg"); +} + +.king.black:before { + background-image: url("assets/king_black.svg"); +} + +.dead-figures { + display: grid; + grid-template-columns: repeat(8, 1fr); + grid-template-rows: repeat(2, 1fr); +} + +.dead-figures .figure { + position: relative; + width: 100%; + height: 100%; +} + +.dead-figures.black { + grid-area: dead-white; +} + +.dead-figures.white { + grid-area: dead-black; +}