Files
chess.js/main.js
2019-04-08 21:52:07 +02:00

579 lines
13 KiB
JavaScript

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');