├── README.md ├── assets ├── avatar │ ├── acharya.png │ └── devki.png ├── capture piece │ ├── capture-bb-1.png │ ├── capture-bb-2.png │ ├── capture-bn-1.png │ ├── capture-bn-2.png │ ├── capture-bp-1.png │ ├── capture-bp-2.png │ ├── capture-bp-3.png │ ├── capture-bp-4.png │ ├── capture-bp-5.png │ ├── capture-bp-6.png │ ├── capture-bp-7.png │ ├── capture-bp-8.png │ ├── capture-bq-1.png │ ├── capture-br-1.png │ ├── capture-br-2.png │ ├── capture-wb-1.png │ ├── capture-wb-2.png │ ├── capture-wn-1.png │ ├── capture-wn-2.png │ ├── capture-wp-1.png │ ├── capture-wp-2.png │ ├── capture-wp-3.png │ ├── capture-wp-4.png │ ├── capture-wp-5.png │ ├── capture-wp-6.png │ ├── capture-wp-7.png │ ├── capture-wp-8.png │ ├── capture-wq-1.png │ ├── capture-wr-1.png │ └── capture-wr-2.png ├── captured-pieces.png ├── icons │ ├── black-crown-alt.png │ ├── blackKing.png │ ├── book.svg │ ├── challengelink.svg │ ├── computer.svg │ ├── copy.png │ ├── cross.png │ ├── crown-shadow.svg │ ├── delete.png │ ├── download.png │ ├── favIcon.png │ ├── handshake.svg │ ├── playwhite.svg │ ├── setting.svg │ ├── share.png │ ├── tick.png │ ├── tournaments.svg │ ├── upload.png │ ├── white-crown-alt.png │ └── whiteKing.png ├── screenshot.png ├── sounds │ ├── capture.mp3 │ ├── castle.mp3 │ ├── game-end.mp3 │ ├── game-start.mp3 │ ├── illegal.mp3 │ ├── move-check.mp3 │ ├── move-opponent.mp3 │ ├── move-self.mp3 │ ├── notify.mp3 │ ├── premove.mp3 │ ├── promote.mp3 │ └── tenseconds.mp3 └── theme │ ├── classic │ ├── board.png │ ├── pieces │ │ ├── bb.png │ │ ├── bk.png │ │ ├── bn.png │ │ ├── bp.png │ │ ├── bq.png │ │ ├── br.png │ │ ├── wb.png │ │ ├── wk.png │ │ ├── wn.png │ │ ├── wp.png │ │ ├── wq.png │ │ └── wr.png │ └── preview.png │ ├── newspaper │ ├── board.png │ ├── pieces │ │ ├── Screenshot 2025-01-05 180521.png │ │ ├── bb.png │ │ ├── bk.png │ │ ├── bn.png │ │ ├── bp.png │ │ ├── bq.png │ │ ├── br.png │ │ ├── wb.png │ │ ├── wk.png │ │ ├── wn.png │ │ ├── wp.png │ │ ├── wq.png │ │ └── wr.png │ └── preview.png │ └── walnut │ ├── board.png │ ├── pieces │ ├── bb.png │ ├── bk.png │ ├── bn.png │ ├── bp.png │ ├── bq.png │ ├── br.png │ ├── wb.png │ ├── wk.png │ ├── wn.png │ ├── wp.png │ ├── wq.png │ └── wr.png │ └── preview.png ├── css ├── style.css ├── theme.css └── util.css ├── desktop.ini ├── gm2600.bin ├── index.html └── js ├── bitBoard.js ├── board.js ├── defs.js ├── evaluation.js ├── generateMove.js ├── gui ├── board.js ├── controlls.js └── pieces.js ├── main.js ├── moveStructure.js ├── perft.js ├── pgnCompiler.js ├── polyglot.js ├── pvTable.js ├── search.js ├── searchWorker.js └── sounds.js /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Chess Engine (elo 2100) 2 | 3 | This is a lightweight and powerful chess engine written in JavaScript. It includes a bitboard-based architecture, move generation, evaluation, and a basic GUI. The project is modular and structured to make learning, debugging, and extending easier. 4 | 5 | ## Features 6 | 7 | - **Bitboard Representation** for efficient board handling (`bitBoard.js`) 8 | - **Move Generation** including legal move filtering (`generateMove.js`) 9 | - **Search Algorithm** with basic evaluation (`search.js`, `evaluation.js`) 10 | - **PGN Compiler** to parse/export games (`pgnCompiler.js`) 11 | - **Polyglot Opening Book Support** (`polyglot.js`) 12 | - **Perft Testing** for move generation accuracy (`perft.js`) 13 | - **Worker Support** for parallelized searching (`searchWorker.js`) 14 | - **Modular UI Components** in `gui/` folder 15 | 16 | ## File Structure 17 | 18 | - `bitBoard.js` - Core representation of the chessboard 19 | - `board.js` - Board setup, FEN parsing 20 | - `defs.js` - Constants and definitions 21 | - `evaluation.js` - Evaluation function to score positions 22 | - `generateMove.js` - Generates all legal and pseudo-legal moves 23 | - `main.js` - Entry point and initialization 24 | - `moveStructure.js` - Move representation and utilities 25 | - `perft.js` - Perft functionality for debugging 26 | - `pgnCompiler.js` - Handles PGN parsing and generation 27 | - `polyglot.js` - Integration for opening books 28 | - `pvTable.js` - Principal variation table 29 | - `search.js` - Minimax/Alpha-beta logic 30 | - `searchWorker.js` - Web worker for background search 31 | - `gui/` - Contains the graphical interface files 32 | 33 | ## Getting Started 34 | 35 | 1. Clone the repo: 36 | ```bash 37 | git clone https://github.com/abhijeetSinghRajput/chessEngine.git 38 | cd chessEngine -------------------------------------------------------------------------------- /assets/avatar/acharya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/avatar/acharya.png -------------------------------------------------------------------------------- /assets/avatar/devki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/avatar/devki.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bb-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bb-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bb-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bb-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bn-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bn-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bn-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bn-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-3.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-4.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-5.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-6.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-7.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bp-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bp-8.png -------------------------------------------------------------------------------- /assets/capture piece/capture-bq-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-bq-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-br-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-br-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-br-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-br-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wb-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wb-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wb-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wb-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wn-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wn-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wn-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wn-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-2.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-3.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-4.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-5.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-6.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-7.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wp-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wp-8.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wq-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wq-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wr-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wr-1.png -------------------------------------------------------------------------------- /assets/capture piece/capture-wr-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/capture piece/capture-wr-2.png -------------------------------------------------------------------------------- /assets/captured-pieces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/captured-pieces.png -------------------------------------------------------------------------------- /assets/icons/black-crown-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/black-crown-alt.png -------------------------------------------------------------------------------- /assets/icons/blackKing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/blackKing.png -------------------------------------------------------------------------------- /assets/icons/book.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/challengelink.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/computer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/copy.png -------------------------------------------------------------------------------- /assets/icons/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/cross.png -------------------------------------------------------------------------------- /assets/icons/crown-shadow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/delete.png -------------------------------------------------------------------------------- /assets/icons/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/download.png -------------------------------------------------------------------------------- /assets/icons/favIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/favIcon.png -------------------------------------------------------------------------------- /assets/icons/handshake.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/playwhite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/setting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/share.png -------------------------------------------------------------------------------- /assets/icons/tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/tick.png -------------------------------------------------------------------------------- /assets/icons/tournaments.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/upload.png -------------------------------------------------------------------------------- /assets/icons/white-crown-alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/white-crown-alt.png -------------------------------------------------------------------------------- /assets/icons/whiteKing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/icons/whiteKing.png -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/screenshot.png -------------------------------------------------------------------------------- /assets/sounds/capture.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/capture.mp3 -------------------------------------------------------------------------------- /assets/sounds/castle.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/castle.mp3 -------------------------------------------------------------------------------- /assets/sounds/game-end.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/game-end.mp3 -------------------------------------------------------------------------------- /assets/sounds/game-start.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/game-start.mp3 -------------------------------------------------------------------------------- /assets/sounds/illegal.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/illegal.mp3 -------------------------------------------------------------------------------- /assets/sounds/move-check.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/move-check.mp3 -------------------------------------------------------------------------------- /assets/sounds/move-opponent.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/move-opponent.mp3 -------------------------------------------------------------------------------- /assets/sounds/move-self.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/move-self.mp3 -------------------------------------------------------------------------------- /assets/sounds/notify.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/notify.mp3 -------------------------------------------------------------------------------- /assets/sounds/premove.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/premove.mp3 -------------------------------------------------------------------------------- /assets/sounds/promote.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/promote.mp3 -------------------------------------------------------------------------------- /assets/sounds/tenseconds.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/sounds/tenseconds.mp3 -------------------------------------------------------------------------------- /assets/theme/classic/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/board.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/bb.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/bk.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/bn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/bn.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/bp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/bp.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/bq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/bq.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/br.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/wb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/wb.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/wk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/wk.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/wn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/wn.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/wp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/wp.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/wq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/wq.png -------------------------------------------------------------------------------- /assets/theme/classic/pieces/wr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/pieces/wr.png -------------------------------------------------------------------------------- /assets/theme/classic/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/classic/preview.png -------------------------------------------------------------------------------- /assets/theme/newspaper/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/board.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/Screenshot 2025-01-05 180521.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/Screenshot 2025-01-05 180521.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/bb.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/bk.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/bn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/bn.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/bp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/bp.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/bq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/bq.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/br.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/wb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/wb.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/wk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/wk.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/wn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/wn.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/wp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/wp.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/wq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/wq.png -------------------------------------------------------------------------------- /assets/theme/newspaper/pieces/wr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/pieces/wr.png -------------------------------------------------------------------------------- /assets/theme/newspaper/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/newspaper/preview.png -------------------------------------------------------------------------------- /assets/theme/walnut/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/board.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/bb.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/bk.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/bn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/bn.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/bp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/bp.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/bq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/bq.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/br.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/wb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/wb.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/wk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/wk.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/wn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/wn.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/wp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/wp.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/wq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/wq.png -------------------------------------------------------------------------------- /assets/theme/walnut/pieces/wr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/pieces/wr.png -------------------------------------------------------------------------------- /assets/theme/walnut/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/assets/theme/walnut/preview.png -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --bg-dark: #302e2b; 3 | --clr-dark: #262522; 4 | --clr-dark: #0e1514; 5 | --clr-dark: #e2e2e2; 6 | --clr-dark: #989795; 7 | --clr-dark: #ffffff14; 8 | --clr-dark: #ffffffb8; 9 | --sq-light: #ebecd0; 10 | --sq-dark: #739552; 11 | --highlight: #ffff33; 12 | --alpha-dark: rgba(0, 0, 0, 0.14); 13 | } 14 | 15 | .icon { 16 | aspect-ratio: 1; 17 | overflow: hidden; 18 | width: 20px; 19 | cursor: pointer; 20 | } 21 | 22 | .icon img { 23 | width: 100%; 24 | height: 100%; 25 | object-fit: cover; 26 | } 27 | 28 | *, 29 | *::before, 30 | *::after { 31 | font-family: 'Roboto', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 32 | list-style: none; 33 | margin: 0; 34 | padding: 0; 35 | box-sizing: border-box; 36 | text-decoration: none; 37 | } 38 | 39 | .win-animation{ 40 | position: fixed; 41 | width: 100vw; 42 | height: 100vh; 43 | top: 0; 44 | left: 0; 45 | z-index: 100; 46 | display: none; 47 | } 48 | 49 | body { 50 | width: 100vw; 51 | min-height: 100svh; 52 | background-color: var(--bg-dark); 53 | display: flex; 54 | justify-content: center; 55 | overflow-x: hidden; 56 | } 57 | 58 | body::-webkit-scrollbar { 59 | display: none; 60 | } 61 | 62 | .container { 63 | display: grid; 64 | grid-template-columns: minmax(300px, 600px) 400px; 65 | grid-gap: 20px; 66 | max-height: 100svh; 67 | position: relative; 68 | } 69 | 70 | .board-layout { 71 | display: flex; 72 | flex-direction: column; 73 | gap: 10px; 74 | height: max-content; 75 | } 76 | 77 | .board-layout.flipped { 78 | flex-direction: column-reverse; 79 | } 80 | 81 | .board-layout .profile-photo { 82 | width: 50px; 83 | border-radius: 3px; 84 | } 85 | 86 | .board-layout .player { 87 | display: flex; 88 | align-items: center; 89 | justify-content: space-between; 90 | padding: 0 10px; 91 | color: #e2e2e2; 92 | text-transform: capitalize; 93 | font-size: 13px; 94 | font-weight: 600; 95 | } 96 | .board-layout .player .user{ 97 | display: flex; 98 | align-items: center; 99 | gap: 10px; 100 | } 101 | 102 | .player.white .profile-photo { 103 | border: 5px solid white; 104 | } 105 | 106 | .player.black .profile-photo { 107 | border: 5px solid black; 108 | } 109 | 110 | .board-layout .player .profile-photo { 111 | border-width: 3px; 112 | } 113 | 114 | .board-layout .player .user .name{ 115 | margin-bottom: 3px; 116 | } 117 | .board-layout .player .control{ 118 | display: flex; 119 | align-items: center; 120 | gap: 10px; 121 | } 122 | 123 | .captures { 124 | display: flex; 125 | align-items: center; 126 | gap: 4px; 127 | } 128 | 129 | .capture { 130 | background-position: center; 131 | background-size: auto 100%; 132 | background-repeat: no-repeat; 133 | height: 12px; 134 | } 135 | 136 | .drop-menu{ 137 | position: relative; 138 | min-width: 60px; 139 | height: max-content; 140 | color: #e2e2e2; 141 | z-index: 100; 142 | } 143 | .drop-menu .selection{ 144 | cursor: pointer; 145 | background-color: #3c3a38; 146 | border: 1px solid #ffffff1a; 147 | padding: 4px 8px; 148 | display: flex; 149 | justify-content: space-between; 150 | align-items: center; 151 | border-radius: 3px; 152 | overflow: hidden; 153 | } 154 | .selection:hover, 155 | .drop-menu.active .selection{ 156 | border-color: #ffffff4d; 157 | } 158 | .selection i{ 159 | font-size: 22px; 160 | width: 16px; 161 | height: 16px; 162 | display: flex; 163 | align-items: center; 164 | justify-content: center; 165 | } 166 | 167 | .drop-menu.active .options{ 168 | display: flex; 169 | } 170 | .drop-menu .options{ 171 | width: 100px; 172 | right: 0; 173 | position: absolute; 174 | background-color: #21201d; 175 | border-radius: 3px; 176 | overflow: hidden; 177 | padding: 8px 0; 178 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); 179 | display: flex; 180 | flex-direction: column; 181 | display: none; 182 | } 183 | 184 | .options .option{ 185 | cursor: pointer; 186 | padding: 8px 8px; 187 | } 188 | .options .option.select, 189 | .options .option:hover{ 190 | background-color: #0e1514; 191 | } 192 | 193 | .player.black .drop-menu .options, 194 | .flipped .player.white .drop-menu .options{ 195 | top: calc(100% + 8px); 196 | bottom: auto; 197 | } 198 | .flipped .player.black .drop-menu .options, 199 | .player.white .drop-menu .options{ 200 | bottom: calc(100% + 8px); 201 | flex-direction: column-reverse; 202 | top: auto; 203 | } 204 | 205 | 206 | #board { 207 | aspect-ratio: 1; 208 | background: url('../assets/theme/classic/board.png') no-repeat; 209 | background-size: contain; 210 | border-radius: 3px; 211 | overflow: hidden; 212 | position: relative; 213 | } 214 | 215 | .coordinates { 216 | position: absolute; 217 | left: 0; 218 | top: 0; 219 | user-select: none; 220 | z-index: 3; 221 | } 222 | 223 | .coordinate-light { 224 | fill: var(--sq-dark); 225 | } 226 | 227 | .coordinate-dark { 228 | fill: var(--sq-light); 229 | } 230 | 231 | .coordinate-dark, 232 | .coordinate-light { 233 | font-weight: 600; 234 | } 235 | 236 | .flipped .promotion-window.black, 237 | .promotion-window { 238 | background-color: white; 239 | position: absolute; 240 | width: 12.5%; 241 | display: flex; 242 | flex-direction: column; 243 | top: 0; 244 | bottom: auto; 245 | border-radius: 3px; 246 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.65); 247 | z-index: 10; 248 | display: none; 249 | } 250 | 251 | .flipped .promotion-window, 252 | .promotion-window.black { 253 | top: auto; 254 | bottom: 0; 255 | flex-direction: column-reverse; 256 | } 257 | 258 | .promotion-window .piece { 259 | position: relative; 260 | width: 100%; 261 | } 262 | 263 | .promotion-window .piece:hover { 264 | background-color: var(--highlight); 265 | } 266 | 267 | .piece { 268 | background-size: contain; 269 | background-repeat: no-repeat; 270 | width: 12.5%; 271 | aspect-ratio: 1; 272 | position: absolute; 273 | cursor: grab; 274 | transition: transform .3s ease; 275 | z-index: 3; 276 | } 277 | .setting-window{ 278 | padding: 30px 14px 279 | } 280 | .theme.active, 281 | .theme:hover{ 282 | background-color: #302e2b; 283 | } 284 | .theme .name{ 285 | text-transform: capitalize; 286 | } 287 | .theme{ 288 | user-select: none; 289 | padding: 15px; 290 | display: flex; 291 | justify-content: space-between; 292 | align-items: flex-start; 293 | } 294 | 295 | .theme .preview img{ 296 | width: 100%; 297 | height: 100%; 298 | object-fit: cover; 299 | } 300 | 301 | .theme .preview{ 302 | aspect-ratio: 1; 303 | width: 100px; 304 | } 305 | 306 | .game-controller { 307 | background-color: #262522; 308 | display: flex; 309 | flex-direction: column; 310 | border-radius: 5px; 311 | overflow: hidden; 312 | height: 100svh; 313 | } 314 | 315 | .game-controller .header { 316 | background-color: #21201d; 317 | padding: 14px; 318 | color: #ffffffb8; 319 | display: flex; 320 | align-items: center; 321 | justify-content: space-between; 322 | font-weight: 600; 323 | font-size: 13px; 324 | } 325 | 326 | .game-controller .header .icons { 327 | display: flex; 328 | gap: 10px; 329 | align-items: center; 330 | } 331 | 332 | .game-controller .header .icons .icon { 333 | width: 20px; 334 | } 335 | 336 | .game-controller .header .icon:hover { 337 | filter: brightness(200%); 338 | } 339 | 340 | .lines{ 341 | background-color: #1b1917; 342 | color: #e2e2e2; 343 | color: #81b64c; 344 | font-weight: 600; 345 | min-width: 100%; 346 | font-size: 12px; 347 | white-space: nowrap; 348 | margin-bottom: 5px; 349 | } 350 | .lines .line{ 351 | margin: 0 8px; 352 | line-height: 25px; 353 | overflow-x: scroll; 354 | } 355 | .line::-webkit-scrollbar{ 356 | display: none; 357 | } 358 | 359 | .game-controller .movelist { 360 | min-height: 150px; 361 | height: 100%; 362 | overflow-y: scroll; 363 | } 364 | 365 | .move { 366 | /* display: none !important; */ 367 | } 368 | 369 | .movelist::-webkit-scrollbar { 370 | background-color: #262522; 371 | width: 6px; 372 | } 373 | 374 | .movelist::-webkit-scrollbar-thumb { 375 | background-color: #151413; 376 | } 377 | 378 | .movelist::-webkit-scrollbar-thumb:hover { 379 | background-color: #fff; 380 | } 381 | 382 | .movelist .move { 383 | display: grid; 384 | align-items: center; 385 | grid-template-columns: 10px 55px 55px; 386 | grid-gap: 10px; 387 | font-size: 11px; 388 | padding: 2px 5px 2px 15px; 389 | } 390 | 391 | .move .ply { 392 | color: #ffffff80; 393 | margin-right: 8px; 394 | } 395 | 396 | .move .node { 397 | min-width: 40px; 398 | width: max-content; 399 | color: #ffffffb8; 400 | font-weight: 700; 401 | padding: 4px; 402 | border-radius: 2px; 403 | border-bottom: 3px solid transparent; 404 | user-select: none; 405 | cursor: pointer; 406 | } 407 | 408 | .move:nth-child(odd) { 409 | background-color: #2a2926; 410 | } 411 | 412 | .node.selected { 413 | background-color: #454441; 414 | border-bottom: 3px solid #575754; 415 | color: white; 416 | } 417 | 418 | .controller { 419 | display: grid; 420 | grid-gap: 3px; 421 | grid-row-gap: 10px; 422 | grid-template-columns: repeat(4, 1fr); 423 | background-color: #21201d; 424 | padding: 16px 24px 20px; 425 | color: rgba(255, 255, 255, 0.72); 426 | } 427 | 428 | 429 | .backdrop { 430 | position: fixed; 431 | width: 100vw; 432 | height: 100vh; 433 | background-color: hsla(0, 0%, 0%, 0.7); 434 | z-index: 101; 435 | visibility: hidden; 436 | } 437 | 438 | .window{ 439 | position: fixed; 440 | top: 50%; 441 | left: 50%; 442 | transform: translate(-50%, -50%) scale(0); 443 | background-color: #21201d; 444 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.65); 445 | border-radius: 10px; 446 | overflow: hidden; 447 | width: 100%; 448 | max-width: 450px; 449 | color: #ffffff80; 450 | font-size: 13px; 451 | font-weight: 600; 452 | transition: .4s 0s cubic-bezier(0.51, 0.01, 0.47, 1.3); 453 | visibility: hidden; 454 | opacity: 0; 455 | z-index: 100; 456 | } 457 | 458 | 459 | .download-window input, 460 | .download-window textarea { 461 | color: #ffffffA1; 462 | background-color: #3c3a38; 463 | border: 1px solid #777574; 464 | outline: none; 465 | padding: 4px 8px; 466 | font-size: 14px; 467 | border-radius: 2px; 468 | margin-bottom: 6px; 469 | display: block; 470 | width: 100%; 471 | resize: vertical; 472 | max-height: 300px; 473 | } 474 | .upload-book .icon{ 475 | cursor: default; 476 | } 477 | .upload-book .icon.delete:hover{ 478 | cursor: pointer; 479 | filter: brightness(200%); 480 | } 481 | 482 | .upload-book{ 483 | padding: 30px 14px; 484 | font-weight: 400; 485 | } 486 | 487 | .upload-book > h3{ 488 | text-align: center; 489 | color: #fff; 490 | font-size: 14px; 491 | margin-bottom: 20px; 492 | } 493 | .book{ 494 | display: flex; 495 | align-items: center; 496 | gap: 10px; 497 | color: #ffffffA1; 498 | background-color: #151413; 499 | margin-bottom: 6px; 500 | padding: 16px 20px; 501 | border-radius: 5px; 502 | display: flex; 503 | gap: 10px; 504 | cursor: pointer; 505 | } 506 | .book.selected{ 507 | background-color: #81b64c; 508 | } 509 | .book .content{ 510 | /* border: 1px solid wheat; */ 511 | width: 100%; 512 | } 513 | .book .content .detail{ 514 | display: flex; 515 | justify-content: space-between; 516 | margin-bottom: 5px; 517 | } 518 | .progress-bar{ 519 | width: 100%; 520 | height: 6px; 521 | background-color: #ebecd0; 522 | overflow: hidden; 523 | border-radius: 50px; 524 | } 525 | .progress-bar .progress{ 526 | width: 0%; 527 | background-color: #81b64c; 528 | height: 100%; 529 | } 530 | 531 | .library{ 532 | height: 312px; 533 | margin-bottom: 20px; 534 | overflow-y: scroll; 535 | } 536 | .library::-webkit-scrollbar{ 537 | display: none; 538 | } 539 | .library .book .detail{ 540 | width: 100%; 541 | } 542 | 543 | textarea { 544 | min-height: 200px; 545 | max-height: 400px; 546 | } 547 | 548 | textarea::-webkit-scrollbar-thumb { 549 | background-color: #21201f; 550 | } 551 | 552 | textarea::-webkit-scrollbar { 553 | width: 6px; 554 | background-color: #3c3a38; 555 | } 556 | 557 | .download-window .header { 558 | position: relative; 559 | background-color: #1b1917; 560 | padding-top: 16px; 561 | } 562 | 563 | .download-window .header .option { 564 | display: flex; 565 | } 566 | 567 | .download-window .header .option div { 568 | width: 100%; 569 | text-align: center; 570 | color: #81b64c; 571 | padding: 15px; 572 | border-bottom: 4px solid transparent; 573 | cursor: pointer; 574 | position: relative; 575 | } 576 | 577 | .download-window .header .option div::before { 578 | content: ''; 579 | width: 0%; 580 | height: 4px; 581 | position: absolute; 582 | bottom: 0; 583 | left: 50%; 584 | transform: translateX(-50%); 585 | background-color: #81b64c; 586 | transition: width .3s; 587 | } 588 | 589 | .download-window .header .option div:hover::before, 590 | .download-window .header .option div.active::before { 591 | width: 100% 592 | } 593 | 594 | .download-window .content { 595 | padding: 10px 15px; 596 | display: flex; 597 | flex-direction: column; 598 | } 599 | 600 | .download-window .content p { 601 | padding: 5px 0; 602 | } 603 | 604 | .download-window .btn { 605 | font-size: 14px; 606 | font-weight: 600; 607 | padding: 5px 18px; 608 | text-shadow: none; 609 | height: 48px; 610 | align-self: center; 611 | margin-top: 8px; 612 | margin-bottom: 20px; 613 | } 614 | 615 | .download-window .btn .icon { 616 | filter: brightness(200%); 617 | width: 17px; 618 | padding: 0; 619 | margin-right: 10px; 620 | } 621 | 622 | .confirm-popover { 623 | text-align: center; 624 | width: 500px; 625 | padding: 30px 14px; 626 | } 627 | .backdrop.active{ 628 | visibility: visible; 629 | } 630 | .backdrop.active>.active { 631 | transform: translate(-50%, -50%) scale(1); 632 | opacity: 1; 633 | visibility: visible; 634 | } 635 | 636 | .window .message { 637 | font-size: 14px; 638 | text-transform: capitalize; 639 | color: #e2e2e2; 640 | margin-bottom: 20px; 641 | } 642 | 643 | .confirm-popover .buttons { 644 | display: flex; 645 | gap: 10px; 646 | } 647 | 648 | .confirm-popover .buttons .btn { 649 | flex: 1; 650 | height: 48px; 651 | } 652 | 653 | .profile-photo { 654 | aspect-ratio: 1; 655 | width: 80px; 656 | border-radius: 10px; 657 | overflow: hidden; 658 | position: relative; 659 | box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.4); 660 | } 661 | 662 | .profile-photo .crown-alt { 663 | display: none; 664 | position: absolute; 665 | bottom: -5px; 666 | right: -5px; 667 | width: 30px; 668 | background-color: #81b64c; 669 | border: 7px solid #81b64c; 670 | border-top-left-radius: 5px; 671 | } 672 | 673 | .winner .profile-photo .crown-alt { 674 | display: block; 675 | } 676 | 677 | 678 | .profile-photo img { 679 | width: 100%; 680 | height: 100%; 681 | object-fit: cover; 682 | } 683 | 684 | .game-over { 685 | text-align: center; 686 | width: 350px; 687 | color: #e2e2e2; 688 | } 689 | .game-over::before{ 690 | content: ''; 691 | position: absolute; 692 | width: 200%; 693 | height: 60%; 694 | background-color: #0e1514; 695 | border-radius: 100%; 696 | left: 50%; 697 | top: 0; 698 | transform: translate(-50%, -50%); 699 | z-index: -2; 700 | 701 | } 702 | .close { 703 | position: absolute; 704 | z-index: 10; 705 | font-size: 24px; 706 | padding: 2px; 707 | top: 0; 708 | right: 0; 709 | width: 30px; 710 | aspect-ratio: 1; 711 | cursor: pointer; 712 | display: flex; 713 | align-items: center; 714 | justify-content: center; 715 | color: #989795; 716 | } 717 | 718 | .close:hover { 719 | color: #fff; 720 | background-color: #2a2926; 721 | } 722 | 723 | .game-over .header { 724 | margin-top: 20px; 725 | margin-bottom: 30px; 726 | } 727 | 728 | .game-over .header .title { 729 | font-size: 32px; 730 | font-weight: 800; 731 | } 732 | 733 | .game-over .header .subtitle { 734 | color: #989795; 735 | } 736 | 737 | .game-over .players { 738 | display: flex; 739 | align-items: center; 740 | justify-content: space-evenly; 741 | } 742 | 743 | .game-over .winner .profile-photo { 744 | border-color: #81b64c; 745 | box-shadow: 0px 0px 16px 0px #aef46721; 746 | } 747 | 748 | .players .name { 749 | margin-top: 5px; 750 | text-transform: capitalize; 751 | } 752 | 753 | .game-over .crowns { 754 | margin: 16px 0; 755 | display: flex; 756 | justify-content: center; 757 | gap: 10px; 758 | } 759 | 760 | .crown { 761 | width: 50px; 762 | } 763 | 764 | .crown.dark { 765 | filter: grayscale(1); 766 | } 767 | 768 | .game-over .buttons { 769 | display: grid; 770 | grid-template-columns: 1fr 1fr; 771 | grid-template-rows: 58px 45px; 772 | grid-gap: 8px; 773 | padding: 10px; 774 | } 775 | 776 | .game-over .buttons .btn { 777 | height: 100%; 778 | } 779 | 780 | .setup-position.active{ 781 | visibility: visible; 782 | } 783 | 784 | .game-over.active { 785 | transition-delay: .7s; 786 | transform: translate(-50%, -50%) scale(1); 787 | opacity: 1; 788 | visibility: visible; 789 | } 790 | 791 | 792 | .upload { 793 | overflow: hidden; 794 | border-radius: 5px; 795 | margin-bottom: 20px; 796 | border: 2px dashed transparent; 797 | transition: .2s; 798 | } 799 | 800 | .upload.file-hover { 801 | border: 2px dashed #ffffffA1; 802 | } 803 | 804 | #upload-fen-input { 805 | color: #ffffffA1; 806 | background-color: #3c3a38; 807 | border: none; 808 | outline: none; 809 | padding: 4px 8px; 810 | font-size: 14px; 811 | border-radius: 2px; 812 | margin-bottom: 6px; 813 | width: 100%; 814 | } 815 | 816 | .upload textarea { 817 | color: #ffffffA1; 818 | background-color: #3c3a38; 819 | outline: none; 820 | padding: 4px 8px; 821 | font-size: 14px; 822 | display: block; 823 | width: 100%; 824 | resize: none; 825 | max-height: 300px; 826 | border: none; 827 | border-bottom: 1px solid #777574; 828 | } 829 | 830 | #upload-bar { 831 | width: 0%; 832 | height: 3px; 833 | margin-top: -3px; 834 | background-color: #777574; 835 | transition: width .3s; 836 | } 837 | 838 | 839 | .upload .btn { 840 | border-radius: 0; 841 | height: 48px; 842 | display: flex; 843 | align-items: center; 844 | justify-content: center; 845 | border: none; 846 | gap: 5px; 847 | } 848 | 849 | .upload .btn .icon { 850 | width: 20px; 851 | } 852 | 853 | 854 | 855 | 856 | .setup-position{ 857 | background-color: #575754; 858 | display: grid; 859 | bottom: 0; 860 | right: 0; 861 | left: auto; 862 | } 863 | .setup-position.active{ 864 | opacity: 1; 865 | transform: scale(1); 866 | } 867 | 868 | .setup-position .pieces{ 869 | padding: 16px; 870 | display: grid; 871 | grid-template-columns: repeat(6, minmax(30px, 75px)); 872 | } 873 | .setup-position .pieces .piece{ 874 | position: relative; 875 | width: 100%; 876 | } 877 | .setup-position .piece.selected{ 878 | background-color: var(--highlight); 879 | } 880 | .setup-position .buttons{ 881 | display: grid; 882 | grid-template-columns: 1fr 1fr; 883 | grid-gap: 10px; 884 | padding: 16px; 885 | } 886 | .setup-position .buttons button{ 887 | height: 50px; 888 | } 889 | .setup-position .close{ 890 | background-color: transparent; 891 | } 892 | 893 | .castle-permission{ 894 | font-weight: 600; 895 | text-transform: capitalize; 896 | grid-gap: 10px; 897 | padding: 16px; 898 | color: #e2e2e2; 899 | display: grid; 900 | grid-template-columns: 1fr 1fr; 901 | } 902 | .checkbox-grid{ 903 | gap: 5px; 904 | grid-column: 1/3; 905 | display: grid; 906 | grid-template-columns: 1fr 1fr; 907 | } 908 | .checkbox-grid input{ 909 | transform: scale(1.4); 910 | margin-right: 10px; 911 | cursor: pointer; 912 | } 913 | 914 | 915 | 916 | 917 | @media screen and (max-width: 900px) { 918 | .icon { 919 | width: 28px; 920 | } 921 | 922 | .close { 923 | width: 50px; 924 | font-size: 32px; 925 | } 926 | 927 | #board { 928 | justify-self: center; 929 | } 930 | 931 | .board-layout .player { 932 | padding: 0; 933 | } 934 | 935 | 936 | .container { 937 | grid-template-columns: minmax(200px, 600px); 938 | grid-template-rows: auto 1fr; 939 | margin-top: 10px; 940 | margin-bottom: 84px; 941 | } 942 | 943 | .movelist { 944 | padding-top: 10px; 945 | padding-bottom: 20px; 946 | overflow-y: auto; 947 | } 948 | 949 | .game-controller { 950 | min-height: 500px; 951 | max-height: 81svh; 952 | display: grid; 953 | grid-template-rows: repeat(3, max-content); 954 | } 955 | .game-controller .controller{ 956 | grid-row: 1/2; 957 | border-bottom: 1px solid #2a2926; 958 | } 959 | .game-controller .controller .btn { 960 | height: 48px; 961 | } 962 | .game-controller .controller .btn { 963 | font-size: 18px; 964 | } 965 | 966 | .square { 967 | font-size: clamp(7px, 3vmin, 11px); 968 | } 969 | } 970 | 971 | 972 | 973 | 974 | @media screen and (max-width: 480px) { 975 | .toggle-btn { 976 | width: 40px; 977 | height: 20px; 978 | } 979 | 980 | .confirm-popover { 981 | padding: 40px 10px; 982 | } 983 | .confirm-popover, 984 | .download-window, 985 | .game-over { 986 | width: 92vw; 987 | } 988 | 989 | #upload-fen-input { 990 | padding: 8px; 991 | } 992 | 993 | .board-layout { 994 | margin: 0 16px; 995 | } 996 | 997 | 998 | .confirm-popover .message { 999 | font-size: 16px; 1000 | margin-bottom: 30px; 1001 | } 1002 | 1003 | .confirm-popover .buttons .btn { 1004 | height: 50px; 1005 | } 1006 | 1007 | .game-over .profile-photo { 1008 | width: 100px; 1009 | } 1010 | 1011 | .game-over .crown { 1012 | width: 60px; 1013 | } 1014 | 1015 | .game-controller { 1016 | flex-direction: column; 1017 | grid-template-rows: repeat(2, max-content); 1018 | } 1019 | 1020 | .game-over .buttons { 1021 | grid-template-rows: 64px 50px; 1022 | } 1023 | 1024 | .game-controller .controller { 1025 | position: fixed; 1026 | width: 100%; 1027 | left: 0; 1028 | bottom: 0; 1029 | padding: 16px 10px 20px; 1030 | z-index: 100; 1031 | } 1032 | 1033 | .controller .btn i::before { 1034 | font-size: 30px; 1035 | } 1036 | 1037 | .game-controller { 1038 | padding-bottom: 100px; 1039 | } 1040 | 1041 | .game-controller .controller .btn { 1042 | height: 48px; 1043 | } 1044 | 1045 | .movelist .move { 1046 | font-size: 12px; 1047 | grid-template-columns: 10px 80px 55px; 1048 | } 1049 | } -------------------------------------------------------------------------------- /css/theme.css: -------------------------------------------------------------------------------- 1 | /* ======================================= */ 2 | /* =============== classic =============== */ 3 | /* ======================================= */ 4 | #board.classic { 5 | background: url('../assets/theme/classic/board.png') no-repeat contain; 6 | } 7 | #board.classic .highlight { 8 | background-color: var(--highlight); 9 | } 10 | .classic .piece.wp { 11 | background-image: url(../assets/theme/classic/pieces/wp.png); 12 | } 13 | 14 | .classic .piece.wr { 15 | background-image: url(../assets/theme/classic/pieces/wr.png); 16 | } 17 | 18 | .classic .piece.wn { 19 | background-image: url(../assets/theme/classic/pieces/wn.png); 20 | } 21 | 22 | .classic .piece.wb { 23 | background-image: url(../assets/theme/classic/pieces/wb.png); 24 | } 25 | 26 | .classic .piece.wq { 27 | background-image: url(../assets/theme/classic/pieces/wq.png); 28 | } 29 | 30 | .classic .piece.wk { 31 | background-image: url(../assets/theme/classic/pieces/wk.png); 32 | } 33 | 34 | .classic .piece.bp { 35 | background-image: url(../assets/theme/classic/pieces/bp.png); 36 | } 37 | 38 | .classic .piece.br { 39 | background-image: url(../assets/theme/classic/pieces/br.png); 40 | } 41 | 42 | .classic .piece.bn { 43 | background-image: url(../assets/theme/classic/pieces/bn.png); 44 | } 45 | 46 | .classic .piece.bb { 47 | background-image: url(../assets/theme/classic/pieces/bb.png); 48 | } 49 | 50 | .classic .piece.bq { 51 | background-image: url(../assets/theme/classic/pieces/bq.png); 52 | } 53 | 54 | .classic .piece.bk { 55 | background-image: url(../assets/theme/classic/pieces/bk.png); 56 | } 57 | 58 | 59 | /* ======================================= */ 60 | /* =============== walnut =============== */ 61 | /* ======================================= */ 62 | #board.walnut .coordinate-light { 63 | fill: #835f42; 64 | } 65 | #board.walnut .highlight { 66 | background-color: #d1a52d; 67 | } 68 | #board.walnut .coordinate-dark { 69 | fill: #c0a684; 70 | } 71 | 72 | #board.walnut { 73 | background: url('../assets/theme/walnut/board.png') no-repeat; 74 | background-size: contain; 75 | } 76 | 77 | 78 | .walnut .piece.wp { 79 | background-image: url(../assets/theme/walnut/pieces/wp.png); 80 | } 81 | 82 | .walnut .piece.wr { 83 | background-image: url(../assets/theme/walnut/pieces/wr.png); 84 | } 85 | 86 | .walnut .piece.wn { 87 | background-image: url(../assets/theme/walnut/pieces/wn.png); 88 | } 89 | 90 | .walnut .piece.wb { 91 | background-image: url(../assets/theme/walnut/pieces/wb.png); 92 | } 93 | 94 | .walnut .piece.wq { 95 | background-image: url(../assets/theme/walnut/pieces/wq.png); 96 | } 97 | 98 | .walnut .piece.wk { 99 | background-image: url(../assets/theme/walnut/pieces/wk.png); 100 | } 101 | 102 | .walnut .piece.bp { 103 | background-image: url(../assets/theme/walnut/pieces/bp.png); 104 | } 105 | 106 | .walnut .piece.br { 107 | background-image: url(../assets/theme/walnut/pieces/br.png); 108 | } 109 | 110 | .walnut .piece.bn { 111 | background-image: url(../assets/theme/walnut/pieces/bn.png); 112 | } 113 | 114 | .walnut .piece.bb { 115 | background-image: url(../assets/theme/walnut/pieces/bb.png); 116 | } 117 | 118 | .walnut .piece.bq { 119 | background-image: url(../assets/theme/walnut/pieces/bq.png); 120 | } 121 | 122 | .walnut .piece.bk { 123 | background-image: url(../assets/theme/walnut/pieces/bk.png); 124 | } 125 | 126 | 127 | /* ======================================= */ 128 | /* =============== newspaper =============== */ 129 | /* ======================================= */ 130 | #board.newspaper .coordinate-light { 131 | fill: #5a5956; 132 | } 133 | #board.newspaper .highlight { 134 | background-color: #99976e; 135 | } 136 | #board.newspaper .coordinate-dark { 137 | fill: #5a5956; 138 | } 139 | #board.newspaper{ 140 | background: url('../assets/theme/newspaper/board.png') no-repeat; 141 | background-size: contain; 142 | } 143 | .newspaper .piece.wp { 144 | background-image: url(../assets/theme/newspaper/pieces/wp.png); 145 | } 146 | 147 | .newspaper .piece.wr { 148 | background-image: url(../assets/theme/newspaper/pieces/wr.png); 149 | } 150 | 151 | .newspaper .piece.wn { 152 | background-image: url(../assets/theme/newspaper/pieces/wn.png); 153 | } 154 | 155 | .newspaper .piece.wb { 156 | background-image: url(../assets/theme/newspaper/pieces/wb.png); 157 | } 158 | 159 | .newspaper .piece.wq { 160 | background-image: url(../assets/theme/newspaper/pieces/wq.png); 161 | } 162 | 163 | .newspaper .piece.wk { 164 | background-image: url(../assets/theme/newspaper/pieces/wk.png); 165 | } 166 | 167 | .newspaper .piece.bp { 168 | background-image: url(../assets/theme/newspaper/pieces/bp.png); 169 | } 170 | 171 | .newspaper .piece.br { 172 | background-image: url(../assets/theme/newspaper/pieces/br.png); 173 | } 174 | 175 | .newspaper .piece.bn { 176 | background-image: url(../assets/theme/newspaper/pieces/bn.png); 177 | } 178 | 179 | .newspaper .piece.bb { 180 | background-image: url(../assets/theme/newspaper/pieces/bb.png); 181 | } 182 | 183 | .newspaper .piece.bq { 184 | background-image: url(../assets/theme/newspaper/pieces/bq.png); 185 | } 186 | 187 | .newspaper .piece.bk { 188 | background-image: url(../assets/theme/newspaper/pieces/bk.png); 189 | } -------------------------------------------------------------------------------- /css/util.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | white-space: nowrap; 3 | border: none; 4 | outline: none; 5 | padding: 5px 20px; 6 | height: 40px; 7 | border-radius: 4px; 8 | color: #c5c5c4; 9 | cursor: pointer; 10 | text-transform: capitalize; 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | border-bottom: 3px solid transparent; 15 | user-select: none; 16 | } 17 | body{ 18 | /* background-color: white !important; */ 19 | } 20 | .popup{ 21 | position: fixed; 22 | bottom: 100px; 23 | padding: 6px 8px; 24 | border-radius: 5px; 25 | background-color: #e8e7e6; 26 | display: flex; 27 | gap: 10px; 28 | align-items: center; 29 | box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.3); 30 | z-index: 1000; 31 | transform: scale(0); 32 | opacity: 0; 33 | visibility: hidden; 34 | transition: .2s 0s cubic-bezier(0.27, 0.63, 0.51, 1.58); 35 | } 36 | .popup.active{ 37 | transform: scale(1); 38 | opacity: 1; 39 | visibility: visible; 40 | } 41 | .popup .icon{ 42 | background: url('../assets/icons/tick.png') no-repeat; 43 | background-size: contain; 44 | width: 18px; 45 | } 46 | 47 | .popup.danger .icon{ 48 | background: url('../assets/icons/cross.png') no-repeat; 49 | background-size: contain; 50 | } 51 | 52 | .toggle-btn { 53 | background-color: #75757566; 54 | color: #ffffffb8; 55 | border: 1px solid #ffffff33; 56 | width: 30px; 57 | height: 16px; 58 | border-radius: 50px; 59 | position: relative; 60 | cursor: pointer; 61 | transition: all .3s ease; /* Unified transition */ 62 | } 63 | 64 | .toggle-btn::before { 65 | content: ''; 66 | position: absolute; 67 | background-color: #e8e7e6; 68 | border: 1px solid #dad7d5; 69 | border-radius: 50%; 70 | height: 120%; 71 | aspect-ratio: 1; 72 | top: 50%; 73 | transform: translateY(-50%); 74 | left: -1px; 75 | transition: all .3s ease; 76 | } 77 | 78 | .toggle-btn.active { 79 | background-color: #5d9948; 80 | } 81 | 82 | .toggle-btn.active::before { 83 | left: 100%; 84 | transform: translate(-90%, -50%); 85 | } 86 | 87 | .btn i::before { 88 | font-size: 22px; 89 | font-weight: 800; 90 | } 91 | 92 | .white.crown-alt { 93 | aspect-ratio: 1; 94 | width: 16px; 95 | background: url('../assets/icons/white-crown-alt.png') no-repeat; 96 | background-size: 100%; 97 | } 98 | 99 | .black.crown-alt { 100 | aspect-ratio: 1; 101 | width: 16px; 102 | background: url('../assets/icons/black-crown-alt.png') no-repeat; 103 | background-size: 100%; 104 | } 105 | 106 | .btn.primary { 107 | background-color: #81b64c; 108 | border-color: #45753c; 109 | color: white; 110 | } 111 | 112 | .btn.primary:hover { 113 | filter: brightness(120%); 114 | box-shadow: 0px 0px 16px 0px #aef46721; 115 | } 116 | 117 | .btn.bold { 118 | font-weight: 800; 119 | font-size: 16px; 120 | border-radius: 10px; 121 | border-width: 4px; 122 | height: 60px; 123 | grid-column: 1/-1; 124 | text-shadow: 0 0.1rem 0 rgba(0, 0, 0, .4); 125 | height: 48px; 126 | } 127 | 128 | .btn.secondary { 129 | background-color: #32312f; 130 | border-color: #2c2b28; 131 | } 132 | 133 | .highlight, 134 | .capture-hint, 135 | .hint, 136 | .check { 137 | width: 12.5%; 138 | height: 12.5%; 139 | position: absolute; 140 | z-index: 2; 141 | } 142 | 143 | .highlight { 144 | background-color: var(--highlight); 145 | opacity: 0.5; 146 | } 147 | 148 | .capture-hint { 149 | border: 6px solid var(--alpha-dark); 150 | border-radius: 50%; 151 | } 152 | 153 | .hint { 154 | padding: 4.2%; 155 | border-radius: 50%; 156 | background-color: var(--alpha-dark); 157 | background-clip: content-box; 158 | } 159 | 160 | .check { 161 | background-color: rgba(255, 0, 0, 0.5); 162 | } 163 | 164 | .btn.secondary:hover { 165 | color: white; 166 | background-color: #454441; 167 | } 168 | 169 | 170 | 171 | .capture.bp-1 { 172 | aspect-ratio: .8/1; 173 | background-image: url('../assets/capture piece/capture-bp-1.png'); 174 | } 175 | 176 | .capture.bp-2 { 177 | background-image: url('../assets/capture piece/capture-bp-2.png'); 178 | aspect-ratio: 1.3/1; 179 | } 180 | 181 | .capture.bp-3 { 182 | background-image: url('../assets/capture piece/capture-bp-3.png'); 183 | aspect-ratio: 1.7/1; 184 | } 185 | 186 | .capture.bp-4 { 187 | background-image: url('../assets/capture piece/capture-bp-4.png'); 188 | aspect-ratio: 2.1/1; 189 | } 190 | 191 | .capture.bp-5 { 192 | background-image: url('../assets/capture piece/capture-bp-5.png'); 193 | aspect-ratio: 2.5/1; 194 | } 195 | 196 | .capture.bp-6 { 197 | background-image: url('../assets/capture piece/capture-bp-6.png'); 198 | aspect-ratio: 3/1; 199 | } 200 | 201 | .capture.bp-7 { 202 | background-image: url('../assets/capture piece/capture-bp-7.png'); 203 | aspect-ratio: 3.4/1; 204 | } 205 | 206 | .capture.bp-8 { 207 | background-image: url('../assets/capture piece/capture-bp-8.png'); 208 | aspect-ratio: 4/1; 209 | } 210 | 211 | .capture.wp-1 { 212 | aspect-ratio: .8/1; 213 | background-image: url('../assets/capture piece/capture-wp-1.png'); 214 | } 215 | 216 | .capture.wp-2 { 217 | background-image: url('../assets/capture piece/capture-wp-2.png'); 218 | aspect-ratio: 1.3/1; 219 | } 220 | 221 | .capture.wp-3 { 222 | background-image: url('../assets/capture piece/capture-wp-3.png'); 223 | aspect-ratio: 1.8/1; 224 | } 225 | 226 | .capture.wp-4 { 227 | background-image: url('../assets/capture piece/capture-wp-4.png'); 228 | aspect-ratio: 2.3/1; 229 | } 230 | 231 | .capture.wp-5 { 232 | background-image: url('../assets/capture piece/capture-wp-5.png'); 233 | aspect-ratio: 2.6/1; 234 | } 235 | 236 | .capture.wp-6 { 237 | background-image: url('../assets/capture piece/capture-wp-6.png'); 238 | aspect-ratio: 3.1/1; 239 | } 240 | 241 | .capture.wp-7 { 242 | background-image: url('../assets/capture piece/capture-wp-7.png'); 243 | aspect-ratio: 3.5/1; 244 | } 245 | 246 | .capture.wp-8 { 247 | background-image: url('../assets/capture piece/capture-wp-8.png'); 248 | aspect-ratio: 4/1; 249 | } 250 | 251 | .capture.wb-1 { 252 | background-image: url('../assets/capture piece/capture-wb-1.png'); 253 | aspect-ratio: .8/1; 254 | } 255 | 256 | .capture.wb-2 { 257 | background-image: url('../assets/capture piece/capture-wb-2.png'); 258 | aspect-ratio: 1.2/1; 259 | } 260 | 261 | .capture.wn-1 { 262 | background-image: url('../assets/capture piece/capture-wn-1.png'); 263 | aspect-ratio: .8/1; 264 | } 265 | 266 | .capture.wn-2 { 267 | background-image: url('../assets/capture piece/capture-wn-2.png'); 268 | aspect-ratio: 1.2/1; 269 | } 270 | 271 | .capture.wr-1 { 272 | background-image: url('../assets/capture piece/capture-wr-1.png'); 273 | aspect-ratio: .8/1; 274 | } 275 | 276 | .capture.wr-2 { 277 | background-image: url('../assets/capture piece/capture-wr-2.png'); 278 | aspect-ratio: 1.3/1; 279 | } 280 | 281 | .capture.wq-1 { 282 | background-image: url('../assets/capture piece/capture-wq-1.png'); 283 | aspect-ratio: 1/1; 284 | } 285 | 286 | 287 | .capture.bb-1 { 288 | background-image: url('../assets/capture piece/capture-bb-1.png'); 289 | aspect-ratio: .8/1; 290 | } 291 | 292 | .capture.bb-2 { 293 | background-image: url('../assets/capture piece/capture-bb-2.png'); 294 | aspect-ratio: 1.2/1; 295 | } 296 | 297 | .capture.bn-1 { 298 | background-image: url('../assets/capture piece/capture-bn-1.png'); 299 | aspect-ratio: .8/1; 300 | } 301 | 302 | .capture.bn-2 { 303 | background-image: url('../assets/capture piece/capture-bn-2.png'); 304 | aspect-ratio: 1.2/1; 305 | } 306 | 307 | .capture.br-1 { 308 | background-image: url('../assets/capture piece/capture-br-1.png'); 309 | aspect-ratio: .8/1; 310 | } 311 | 312 | .capture.br-2 { 313 | background-image: url('../assets/capture piece/capture-br-2.png'); 314 | aspect-ratio: 1.3/1; 315 | } 316 | 317 | .capture.bq-1 { 318 | background-image: url('../assets/capture piece/capture-bq-1.png'); 319 | aspect-ratio: 1/1; 320 | } 321 | 322 | 323 | 324 | 325 | 326 | .piece.wp { 327 | background-image: url(../assets/theme/classic/pieces/wp.png); 328 | } 329 | 330 | .piece.wr { 331 | background-image: url(../assets/theme/classic/pieces/wr.png); 332 | } 333 | 334 | .piece.wn { 335 | background-image: url(../assets/theme/classic/pieces/wn.png); 336 | } 337 | 338 | .piece.wb { 339 | background-image: url(../assets/theme/classic/pieces/wb.png); 340 | } 341 | 342 | .piece.wq { 343 | background-image: url(../assets/theme/classic/pieces/wq.png); 344 | } 345 | 346 | .piece.wk { 347 | background-image: url(../assets/theme/classic/pieces/wk.png); 348 | } 349 | 350 | .piece.bp { 351 | background-image: url(../assets/theme/classic/pieces/bp.png); 352 | } 353 | 354 | .piece.br { 355 | background-image: url(../assets/theme/classic/pieces/br.png); 356 | } 357 | 358 | .piece.bn { 359 | background-image: url(../assets/theme/classic/pieces/bn.png); 360 | } 361 | 362 | .piece.bb { 363 | background-image: url(../assets/theme/classic/pieces/bb.png); 364 | } 365 | 366 | .piece.bq { 367 | background-image: url(../assets/theme/classic/pieces/bq.png); 368 | } 369 | 370 | .piece.bk { 371 | background-image: url(../assets/theme/classic/pieces/bk.png); 372 | } 373 | 374 | 375 | .promotion-window.file-a { 376 | transform: translateX(0%); 377 | } 378 | 379 | .promotion-window.file-b { 380 | transform: translateX(100%); 381 | } 382 | 383 | .promotion-window.file-c { 384 | transform: translateX(200%); 385 | } 386 | 387 | .promotion-window.file-d { 388 | transform: translateX(300%); 389 | } 390 | 391 | .promotion-window.file-e { 392 | transform: translateX(400%); 393 | } 394 | 395 | .promotion-window.file-f { 396 | transform: translateX(500%); 397 | } 398 | 399 | .promotion-window.file-g { 400 | transform: translateX(600%); 401 | } 402 | 403 | .promotion-window.file-h { 404 | transform: translateX(700%); 405 | } 406 | 407 | .flipped .promotion-window.file-a { 408 | transform: translateX(700%); 409 | } 410 | 411 | .flipped .promotion-window.file-b { 412 | transform: translateX(600%); 413 | } 414 | 415 | .flipped .promotion-window.file-c { 416 | transform: translateX(500%); 417 | } 418 | 419 | .flipped .promotion-window.file-d { 420 | transform: translateX(400%); 421 | } 422 | 423 | .flipped .promotion-window.file-e { 424 | transform: translateX(34px); 425 | } 426 | 427 | .flipped .promotion-window.file-f { 428 | transform: translateX(200%); 429 | } 430 | 431 | .flipped .promotion-window.file-g { 432 | transform: translateX(100%); 433 | } 434 | 435 | .flipped .promotion-window.file-h { 436 | transform: translateX(0%); 437 | } 438 | 439 | .flipped #a1 { 440 | transform: translate(700%, 0%); 441 | } 442 | 443 | .flipped #b1 { 444 | transform: translate(600%, 0%); 445 | } 446 | 447 | .flipped #c1 { 448 | transform: translate(500%, 0%); 449 | } 450 | 451 | .flipped #d1 { 452 | transform: translate(400%, 0%); 453 | } 454 | 455 | .flipped #e1 { 456 | transform: translate(300%, 0%); 457 | } 458 | 459 | .flipped #f1 { 460 | transform: translate(200%, 0%); 461 | } 462 | 463 | .flipped #g1 { 464 | transform: translate(100%, 0%); 465 | } 466 | 467 | .flipped #h1 { 468 | transform: translate(0%, 0%); 469 | } 470 | 471 | 472 | 473 | 474 | .flipped #a2 { 475 | transform: translate(700%, 100%); 476 | } 477 | 478 | .flipped #b2 { 479 | transform: translate(600%, 100%); 480 | } 481 | 482 | .flipped #c2 { 483 | transform: translate(500%, 100%); 484 | } 485 | 486 | .flipped #d2 { 487 | transform: translate(400%, 100%); 488 | } 489 | 490 | .flipped #e2 { 491 | transform: translate(300%, 100%); 492 | } 493 | 494 | .flipped #f2 { 495 | transform: translate(200%, 100%); 496 | } 497 | 498 | .flipped #g2 { 499 | transform: translate(100%, 100%); 500 | } 501 | 502 | .flipped #h2 { 503 | transform: translate(0%, 100%); 504 | } 505 | 506 | 507 | 508 | 509 | .flipped #a3 { 510 | transform: translate(700%, 200%); 511 | } 512 | 513 | .flipped #b3 { 514 | transform: translate(600%, 200%); 515 | } 516 | 517 | .flipped #c3 { 518 | transform: translate(500%, 200%); 519 | } 520 | 521 | .flipped #d3 { 522 | transform: translate(400%, 200%); 523 | } 524 | 525 | .flipped #e3 { 526 | transform: translate(300%, 200%); 527 | } 528 | 529 | .flipped #f3 { 530 | transform: translate(200%, 200%); 531 | } 532 | 533 | .flipped #g3 { 534 | transform: translate(100%, 200%); 535 | } 536 | 537 | .flipped #h3 { 538 | transform: translate(0%, 200%); 539 | } 540 | 541 | 542 | 543 | 544 | .flipped #a4 { 545 | transform: translate(700%, 300%); 546 | } 547 | 548 | .flipped #b4 { 549 | transform: translate(600%, 300%); 550 | } 551 | 552 | .flipped #c4 { 553 | transform: translate(500%, 300%); 554 | } 555 | 556 | .flipped #d4 { 557 | transform: translate(400%, 300%); 558 | } 559 | 560 | .flipped #e4 { 561 | transform: translate(300%, 300%); 562 | } 563 | 564 | .flipped #f4 { 565 | transform: translate(200%, 300%); 566 | } 567 | 568 | .flipped #g4 { 569 | transform: translate(100%, 300%); 570 | } 571 | 572 | .flipped #h4 { 573 | transform: translate(0%, 300%); 574 | } 575 | 576 | 577 | 578 | 579 | .flipped #a5 { 580 | transform: translate(700%, 400%); 581 | } 582 | 583 | .flipped #b5 { 584 | transform: translate(600%, 400%); 585 | } 586 | 587 | .flipped #c5 { 588 | transform: translate(500%, 400%); 589 | } 590 | 591 | .flipped #d5 { 592 | transform: translate(400%, 400%); 593 | } 594 | 595 | .flipped #e5 { 596 | transform: translate(300%, 400%); 597 | } 598 | 599 | .flipped #f5 { 600 | transform: translate(200%, 400%); 601 | } 602 | 603 | .flipped #g5 { 604 | transform: translate(100%, 400%); 605 | } 606 | 607 | .flipped #h5 { 608 | transform: translate(0%, 400%); 609 | } 610 | 611 | 612 | 613 | 614 | .flipped #a6 { 615 | transform: translate(700%, 500%); 616 | } 617 | 618 | .flipped #b6 { 619 | transform: translate(600%, 500%); 620 | } 621 | 622 | .flipped #c6 { 623 | transform: translate(500%, 500%); 624 | } 625 | 626 | .flipped #d6 { 627 | transform: translate(400%, 500%); 628 | } 629 | 630 | .flipped #e6 { 631 | transform: translate(300%, 500%); 632 | } 633 | 634 | .flipped #f6 { 635 | transform: translate(200%, 500%); 636 | } 637 | 638 | .flipped #g6 { 639 | transform: translate(100%, 500%); 640 | } 641 | 642 | .flipped #h6 { 643 | transform: translate(0%, 500%); 644 | } 645 | 646 | 647 | 648 | 649 | .flipped #a7 { 650 | transform: translate(700%, 600%); 651 | } 652 | 653 | .flipped #b7 { 654 | transform: translate(600%, 600%); 655 | } 656 | 657 | .flipped #c7 { 658 | transform: translate(500%, 600%); 659 | } 660 | 661 | .flipped #d7 { 662 | transform: translate(400%, 600%); 663 | } 664 | 665 | .flipped #e7 { 666 | transform: translate(300%, 600%); 667 | } 668 | 669 | .flipped #f7 { 670 | transform: translate(200%, 600%); 671 | } 672 | 673 | .flipped #g7 { 674 | transform: translate(100%, 600%); 675 | } 676 | 677 | .flipped #h7 { 678 | transform: translate(0%, 600%); 679 | } 680 | 681 | 682 | 683 | 684 | .flipped #a8 { 685 | transform: translate(700%, 700%); 686 | } 687 | 688 | .flipped #b8 { 689 | transform: translate(600%, 700%); 690 | } 691 | 692 | .flipped #c8 { 693 | transform: translate(500%, 700%); 694 | } 695 | 696 | .flipped #d8 { 697 | transform: translate(400%, 700%); 698 | } 699 | 700 | .flipped #e8 { 701 | transform: translate(300%, 700%); 702 | } 703 | 704 | .flipped #f8 { 705 | transform: translate(200%, 700%); 706 | } 707 | 708 | .flipped #g8 { 709 | transform: translate(100%, 700%); 710 | } 711 | 712 | .flipped #h8 { 713 | transform: translate(0%, 700%); 714 | } 715 | 716 | 717 | #a1 { 718 | transform: translate(0%, 700%); 719 | } 720 | 721 | #b1 { 722 | transform: translate(100%, 700%); 723 | } 724 | 725 | #c1 { 726 | transform: translate(200%, 700%); 727 | } 728 | 729 | #d1 { 730 | transform: translate(300%, 700%); 731 | } 732 | 733 | #e1 { 734 | transform: translate(400%, 700%); 735 | } 736 | 737 | #f1 { 738 | transform: translate(500%, 700%); 739 | } 740 | 741 | #g1 { 742 | transform: translate(600%, 700%); 743 | } 744 | 745 | #h1 { 746 | transform: translate(700%, 700%); 747 | } 748 | 749 | 750 | #a2 { 751 | transform: translate(0%, 600%); 752 | } 753 | 754 | #b2 { 755 | transform: translate(100%, 600%); 756 | } 757 | 758 | #c2 { 759 | transform: translate(200%, 600%); 760 | } 761 | 762 | #d2 { 763 | transform: translate(300%, 600%); 764 | } 765 | 766 | #e2 { 767 | transform: translate(400%, 600%); 768 | } 769 | 770 | #f2 { 771 | transform: translate(500%, 600%); 772 | } 773 | 774 | #g2 { 775 | transform: translate(600%, 600%); 776 | } 777 | 778 | #h2 { 779 | transform: translate(700%, 600%); 780 | } 781 | 782 | 783 | #a3 { 784 | transform: translate(0%, 500%); 785 | } 786 | 787 | #b3 { 788 | transform: translate(100%, 500%); 789 | } 790 | 791 | #c3 { 792 | transform: translate(200%, 500%); 793 | } 794 | 795 | #d3 { 796 | transform: translate(300%, 500%); 797 | } 798 | 799 | #e3 { 800 | transform: translate(400%, 500%); 801 | } 802 | 803 | #f3 { 804 | transform: translate(500%, 500%); 805 | } 806 | 807 | #g3 { 808 | transform: translate(600%, 500%); 809 | } 810 | 811 | #h3 { 812 | transform: translate(700%, 500%); 813 | } 814 | 815 | 816 | #a4 { 817 | transform: translate(0%, 400%); 818 | } 819 | 820 | #b4 { 821 | transform: translate(100%, 400%); 822 | } 823 | 824 | #c4 { 825 | transform: translate(200%, 400%); 826 | } 827 | 828 | #d4 { 829 | transform: translate(300%, 400%); 830 | } 831 | 832 | #e4 { 833 | transform: translate(400%, 400%); 834 | } 835 | 836 | #f4 { 837 | transform: translate(500%, 400%); 838 | } 839 | 840 | #g4 { 841 | transform: translate(600%, 400%); 842 | } 843 | 844 | #h4 { 845 | transform: translate(700%, 400%); 846 | } 847 | 848 | 849 | #a5 { 850 | transform: translate(0%, 300%); 851 | } 852 | 853 | #b5 { 854 | transform: translate(100%, 300%); 855 | } 856 | 857 | #c5 { 858 | transform: translate(200%, 300%); 859 | } 860 | 861 | #d5 { 862 | transform: translate(300%, 300%); 863 | } 864 | 865 | #e5 { 866 | transform: translate(400%, 300%); 867 | } 868 | 869 | #f5 { 870 | transform: translate(500%, 300%); 871 | } 872 | 873 | #g5 { 874 | transform: translate(600%, 300%); 875 | } 876 | 877 | #h5 { 878 | transform: translate(700%, 300%); 879 | } 880 | 881 | 882 | #a6 { 883 | transform: translate(0%, 200%); 884 | } 885 | 886 | #b6 { 887 | transform: translate(100%, 200%); 888 | } 889 | 890 | #c6 { 891 | transform: translate(200%, 200%); 892 | } 893 | 894 | #d6 { 895 | transform: translate(300%, 200%); 896 | } 897 | 898 | #e6 { 899 | transform: translate(400%, 200%); 900 | } 901 | 902 | #f6 { 903 | transform: translate(500%, 200%); 904 | } 905 | 906 | #g6 { 907 | transform: translate(600%, 200%); 908 | } 909 | 910 | #h6 { 911 | transform: translate(700%, 200%); 912 | } 913 | 914 | #a7 { 915 | transform: translate(0%, 100%); 916 | } 917 | 918 | #b7 { 919 | transform: translate(100%, 100%); 920 | } 921 | 922 | #c7 { 923 | transform: translate(200%, 100%); 924 | } 925 | 926 | #d7 { 927 | transform: translate(300%, 100%); 928 | } 929 | 930 | #e7 { 931 | transform: translate(400%, 100%); 932 | } 933 | 934 | #f7 { 935 | transform: translate(500%, 100%); 936 | } 937 | 938 | #g7 { 939 | transform: translate(600%, 100%); 940 | } 941 | 942 | #h7 { 943 | transform: translate(700%, 100%); 944 | } 945 | 946 | #a8 { 947 | transform: translate(0%, 0%); 948 | } 949 | 950 | #b8 { 951 | transform: translate(100%, 0%); 952 | } 953 | 954 | #c8 { 955 | transform: translate(200%, 0%); 956 | } 957 | 958 | #d8 { 959 | transform: translate(300%, 0%); 960 | } 961 | 962 | #e8 { 963 | transform: translate(400%, 0%); 964 | } 965 | 966 | #f8 { 967 | transform: translate(500%, 0%); 968 | } 969 | 970 | #g8 { 971 | transform: translate(600%, 0%); 972 | } 973 | 974 | #h8 { 975 | transform: translate(700%, 0%); 976 | } -------------------------------------------------------------------------------- /desktop.ini: -------------------------------------------------------------------------------- 1 | [ViewState] 2 | Mode= 3 | Vid= 4 | FolderType=Generic 5 | -------------------------------------------------------------------------------- /gm2600.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhijeetSinghRajput/ChessEngine/36c371be11c9d601c74a9b0dc3442d2efdf71661/gm2600.bin -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | chess league 33 | 34 | 35 | 36 | 40 | 41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 |
53 |

devki

54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
2s
69 |
70 |
1
71 |
2
72 |
3
73 |
4
74 |
5
75 |
6
76 |
7
77 |
8
78 |
9
79 |
10
80 |
81 |
82 | 83 |
84 |
85 |
86 | 87 | 88 | 89 | 90 |
91 | 92 | 93 | 8 94 | 7 95 | 6 96 | 5 97 | 4 98 | 3 99 | 2 100 | 1 101 | a 102 | b 103 | c 104 | d 105 | e 106 | f 107 | g 108 | h 109 | 110 | 111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | 125 | 126 | 127 | 128 |
129 |
130 | 131 |
132 |
133 | 134 |
135 |
136 |
137 |

acharya

138 | 139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | 151 | 152 |
153 |
2s
154 |
155 |
1
156 |
2
157 |
3
158 |
4
159 |
5
160 |
6
161 |
7
162 |
8
163 |
9
164 |
10
165 |
166 |
167 | 168 |
169 |
170 |
171 | 172 |
173 | 174 | 175 |
176 | 177 |
178 |
179 | hash key : 180 | 5300d2e1 181 |
182 | 183 | 184 | 185 |
186 |
188 |
189 |
190 |
191 |
192 |
193 |

194 |

195 |

196 |
197 | 198 |
199 | 200 |
201 | 204 | 205 | 208 | 209 | 212 | 213 | 216 |
217 | 218 |
219 |
220 | 221 | 222 | 223 | 225 | 226 |
227 | 228 |
229 |
230 |
231 | Draw 232 |
233 |
by stalemate
234 |
235 |
236 |
237 |
238 | 239 |
240 |
241 |

acharya

242 |
243 |

vs

244 |
245 |
246 | 247 |
248 |
249 |

devki

250 |
251 |
252 |
253 | 254 | 255 | 256 |
257 |
258 | 259 | 260 | 261 |
262 |
263 | 264 |
265 | 266 |
267 |
268 |
269 |
270 |
PGN
271 |
Image
272 |
273 |
274 |
275 |
276 |

FEN

277 | 279 |
280 |
281 |

PGN

282 | 283 |
284 | 290 |
291 |
292 | 293 | 294 |
295 |
296 |
Theme
297 |
298 |
299 |
classic
300 |
301 | 302 |
303 |
304 | 305 |
306 |
walnut
307 |
308 | 309 |
310 |
311 | 312 |
313 |
newspaper
314 |
315 | 316 |
317 |
318 | 319 |
320 |
321 | 322 | 323 |
324 |
325 |
add new game
326 | 327 | 328 | 329 |
330 | 331 | 332 |
333 |
334 |
335 | upload 336 |
337 |
338 | 339 |
340 | 341 | 342 |
343 |
344 | 345 |
346 |
347 |

Books

348 | 349 | 361 | 362 |
363 |
364 | 365 |
366 |
gm2600.bin
367 |
338.61 KB
368 |
369 |
370 |
371 | 372 | 373 |
374 | 375 |
376 |
377 | Upload Book 378 |
379 |
380 |
381 | 382 |
383 | 384 | 385 | 386 |
387 |
388 | 389 |
390 |
white
391 |
black
392 |
393 | 394 | 395 | 396 | 397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 | 407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 | 415 |
416 | 417 | 418 |
419 |
420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | -------------------------------------------------------------------------------- /js/bitBoard.js: -------------------------------------------------------------------------------- 1 | //example:- 2 | 3 | // bitBoard file[0] nth Bit 4 | // 1 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 5 | // 1 0 0 0 0 0 0 0 8 9 10 11 12 13 14 15 6 | // 1 0 0 0 0 0 0 0 16 17 18 19 20 21 22 23 7 | // 1 0 0 0 0 0 0 0 24 25 26 27 28 29 30 31 8 | // 1 0 0 0 0 0 0 0 32 33 34 35 36 37 38 39 9 | // 1 0 0 0 0 0 0 0 40 41 42 43 44 45 46 47 10 | // 1 0 0 0 0 0 0 0 48 49 50 51 52 53 54 55 11 | // 1 0 0 0 0 0 0 0 56 57 58 59 60 61 62 63 12 | 13 | const FileBitMask = new Array(8).fill(0n); 14 | const RankBitMask = new Array(8).fill(0n); 15 | const PassedPawnBitMask = new Array(2); 16 | const IsolatedBitMask = new Array(64).fill(0n); 17 | 18 | 19 | 20 | const PawnBitBoard = { 21 | 0: 0n, 22 | 1: 0n, 23 | setBit: function (color, sq) { 24 | this[color] |= (1n << BigInt(sq)); 25 | }, 26 | clearBit: function (color, sq) { 27 | this[color] &= ~(1n << BigInt(sq)); 28 | }, 29 | getBoth: function () { 30 | return this[Color.white] | this[Color.black]; 31 | }, 32 | init: function () { 33 | for (let sq = 0; sq < 64; sq++) { 34 | let piece = gameBoard.pieces[Sq64To120[sq]]; 35 | if (piece == Pieces.empty) continue; 36 | if (piece == Pieces.wp) { 37 | this.setBit(Color.white, sq); 38 | } 39 | else if (piece == Pieces.bp) { 40 | this.setBit(Color.black, sq); 41 | } 42 | } 43 | } 44 | }; 45 | 46 | 47 | 48 | function printBitBoard(num) { 49 | for (let row = 0; row < 8; row++) { 50 | let rowString = ''; 51 | for (let col = 0; col < 8; col++) { 52 | const bit = (num >> BigInt(row * 8 + col)) & 1n; 53 | rowString += bit + ' '; 54 | } 55 | console.log(row + ". " + rowString); 56 | } 57 | console.log(''); 58 | } -------------------------------------------------------------------------------- /js/board.js: -------------------------------------------------------------------------------- 1 | let gameBoard = {}; 2 | 3 | gameBoard.pieces = new Array(120); 4 | gameBoard.side = Color.white; 5 | gameBoard.castlePermission = 0; 6 | gameBoard.enPassantSq = Squares.noSq; 7 | gameBoard.fiftyMove = 0; 8 | gameBoard.checkSq = Squares.noSq; 9 | gameBoard.positionKey = 0; 10 | 11 | gameBoard.pieceList = new Array(13); 12 | gameBoard.pieceCount = new Array(13); 13 | gameBoard.material = new Array(2); 14 | gameBoard.history = []; 15 | 16 | function resetBoard() { 17 | for (let i = 0; i < 120; ++i) { 18 | gameBoard.pieces[i] = Squares.offBoard; 19 | } 20 | for (const sq of Sq64To120) { 21 | gameBoard.pieces[sq] = Pieces.empty; 22 | } 23 | 24 | for (let i = 0; i < 13; ++i) { 25 | gameBoard.pieceList[i] = []; 26 | gameBoard.pieceCount[i] = 0; 27 | } 28 | 29 | gameBoard.material[Color.white] = 0; 30 | gameBoard.material[Color.black] = 0; 31 | 32 | gameBoard.side = Color.white; 33 | gameBoard.castlePermission = 0; 34 | gameBoard.enPassantSq = Squares.noSq; 35 | gameBoard.fiftyMove = 0; 36 | gameBoard.checkSq = Squares.noSq; 37 | gameBoard.positionKey = 0; 38 | gameBoard.history = []; 39 | } 40 | 41 | function isValidFen(fen) { 42 | const [position, side, castling, enPassant] = fen.split(' '); 43 | 44 | const rows = position.split('/'); 45 | if (rows.length !== 8) { 46 | throw new Error('FEN string should contain exactly 8 rows separated by slashes.'); 47 | } 48 | 49 | const validPosition = rows.every(row => { 50 | let count = 0; 51 | for (let char of row) { 52 | if (char >= '1' && char <= '8') { 53 | count += parseInt(char); 54 | } else if ('pnbrqkPNBRQK'.includes(char)) { 55 | count++; 56 | } else { 57 | throw new Error(`Invalid character "${char}" found in FEN position.`); 58 | } 59 | } 60 | return count === 8; 61 | }); 62 | 63 | if (!validPosition) { 64 | throw new Error('Each row in FEN position must sum to exactly 8 squares.'); 65 | } 66 | 67 | if (!['w', 'b'].includes(side)) { 68 | throw new Error('Side to move must be either "w" (white) or "b" (black).'); 69 | } 70 | 71 | // Validate castling availability 72 | if (!/^(-|[KQkq]{1,4})$/.test(castling)) { 73 | throw new Error('Castling availability must be "-" or a combination of "K", "Q", "k", and "q".'); 74 | } 75 | 76 | // Validate en passant target square 77 | if (!/^(-|[a-h][36])$/.test(enPassant)) { 78 | throw new Error('En passant target square must be "-" or a valid square (e.g., "e3", "d6").'); 79 | } 80 | 81 | return true; 82 | } 83 | 84 | function parseFen(fen) { 85 | try { 86 | isValidFen(fen) 87 | } 88 | catch (error) { 89 | console.error(error); 90 | alert(error.message); 91 | return; 92 | } 93 | resetBoard(); 94 | 95 | const [position, side, castlePermission, enPassantSq] = fen.split(' '); 96 | const map = { 97 | P: Pieces.wp, R: Pieces.wr, N: Pieces.wn, B: Pieces.wb, Q: Pieces.wq, K: Pieces.wk, 98 | p: Pieces.bp, r: Pieces.br, n: Pieces.bn, b: Pieces.bb, q: Pieces.bq, k: Pieces.bk, 99 | }; 100 | 101 | let rank = Rank8; 102 | let file = FileA; 103 | 104 | for (const char of position) { 105 | if (char === '/') { 106 | rank--; 107 | file = FileA; 108 | continue; 109 | } 110 | 111 | if (char >= '1' && char <= '8') { 112 | file += +char; 113 | } 114 | else { 115 | let sq = fileRank2Sq(file++, rank); 116 | gameBoard.pieces[sq] = map[char]; 117 | } 118 | } 119 | 120 | gameBoard.side = (side === 'w') ? Color.white : Color.black; 121 | 122 | for (const char of castlePermission) { 123 | gameBoard.castlePermission |= CastleBit[char]; 124 | } 125 | 126 | if (enPassantSq !== '-') { 127 | gameBoard.enPassantSq = Squares[enPassantSq]; 128 | } 129 | 130 | //update value of pieces, pieceCount, and piece points 131 | updateMaterial(); 132 | 133 | //is opponent king in check 134 | let opponentKing = gameBoard.pieceList[Kings[gameBoard.side]][0]; 135 | if (isUnderAttack(opponentKing, gameBoard.side ^ 1)) { 136 | gameBoard.checkSq = gameBoard.pieceList[Kings[gameBoard.side]][0]; 137 | } 138 | 139 | //assign a unique key for each different position 140 | gameBoard.positionKey = generatePositionKey(); 141 | } 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | function generatePositionKey() { 153 | let hashKey = 0; 154 | for (const sq of Sq64To120) { 155 | let piece = gameBoard.pieces[sq]; 156 | if (piece !== Pieces.empty) { 157 | hashKey ^= PieceKeys[piece][sq]; 158 | } 159 | } 160 | hashKey ^= CastleKeys[gameBoard.castlePermission]; 161 | 162 | if (gameBoard.side === Color.white) { 163 | hashKey ^= SideKey; 164 | } 165 | if (gameBoard.enPassantSq !== Squares.noSq) { 166 | hashKey ^= PieceKeys[Pieces.empty][gameBoard.enPassantSq]; 167 | } 168 | return hashKey; 169 | } 170 | 171 | function updateMaterial() { 172 | for (const sq of Sq64To120) { 173 | let piece = gameBoard.pieces[sq]; 174 | if (piece !== Pieces.empty) { 175 | gameBoard.pieceList[piece].push(sq); 176 | gameBoard.pieceCount[piece]++; 177 | gameBoard.material[PieceColor[piece]] += PieceValue[piece]; 178 | } 179 | } 180 | PawnBitBoard.init(); 181 | } 182 | 183 | function printBoard() { 184 | let line = ''; 185 | for (let rank = Rank8; rank >= Rank1; --rank) { 186 | line = rank + ' '; 187 | for (let file = FileA; file <= FileH; ++file) { 188 | let sq = fileRank2Sq(file, rank); 189 | let piece = gameBoard.pieces[sq]; 190 | line += ' ' + PieceChar[piece] + ' '; 191 | } 192 | console.log(line); 193 | } 194 | 195 | line = '\n '; 196 | for (let file = 0; file < 8; ++file) { 197 | line += FileChar[file] + ' '; 198 | } 199 | console.log(line); 200 | 201 | console.log('side :', SideChar[gameBoard.side]); 202 | console.log('castle :', gameBoard.castlePermission.toString(2)); 203 | if (gameBoard.enPassantSq !== Squares.noSq) { 204 | console.log('enPassant :', SquaresChar[gameBoard.enPassantSq]) 205 | } 206 | console.log('\x1b[33m' + 'hashKey : ' + gameBoard.positionKey.toString(16)); 207 | 208 | if (gameBoard.checkSq !== Squares.noSq) { 209 | console.log('\x1b[31m' + 'check : ' + SquaresChar[gameBoard.checkSq]) 210 | } 211 | } 212 | 213 | function getFen() { 214 | let fen = ''; 215 | for (let rank = Rank8; rank >= Rank1; --rank) { 216 | let emptySq = 0; 217 | for (let file = FileA; file <= FileH; ++file) { 218 | let sq = fileRank2Sq(file, rank); 219 | let piece = gameBoard.pieces[sq]; 220 | 221 | if (piece === Pieces.empty) { 222 | emptySq++; 223 | } 224 | else { 225 | if (emptySq) { 226 | fen += emptySq; 227 | emptySq = 0; 228 | } 229 | fen += PieceChar[piece]; 230 | } 231 | } 232 | if (emptySq) { 233 | fen += emptySq; 234 | } 235 | if (rank !== Rank1) { 236 | fen += '/' 237 | } 238 | } 239 | 240 | fen += ' ' + SideChar[gameBoard.side] + ' '; 241 | if (gameBoard.castlePermission) { 242 | if (gameBoard.castlePermission & CastleBit.K) fen += 'K'; 243 | if (gameBoard.castlePermission & CastleBit.Q) fen += 'Q'; 244 | if (gameBoard.castlePermission & CastleBit.k) fen += 'k'; 245 | if (gameBoard.castlePermission & CastleBit.q) fen += 'q'; 246 | } 247 | else { 248 | fen += '-'; 249 | } 250 | 251 | if (gameBoard.enPassantSq !== Squares.noSq) { 252 | fen += ' ' + SquaresChar[gameBoard.enPassantSq] + ' '; 253 | } 254 | else { 255 | fen += ' - ' 256 | } 257 | fen += gameBoard.fiftyMove + ' '; 258 | //ply that visible on gui 259 | fen += ply ? ply : 1; 260 | return fen; 261 | } 262 | 263 | function isUnderAttack(sq, attakingSide) { 264 | if (attakingSide === Color.white) { 265 | if (gameBoard.pieces[sq - 9] === Pieces.wp || gameBoard.pieces[sq - 11] === Pieces.wp) { 266 | return true; 267 | } 268 | } 269 | else { 270 | if (gameBoard.pieces[sq + 9] === Pieces.bp || gameBoard.pieces[sq + 11] === Pieces.bp) { 271 | return true; 272 | } 273 | } 274 | 275 | //non sliding attack 276 | for (const attakingPiece of NonSlidingPieces[attakingSide]) { 277 | for (const direction of PieceDirections[attakingPiece]) { 278 | let targetSq = sq + direction; 279 | 280 | if (gameBoard.pieces[targetSq] === attakingPiece) { 281 | return true; 282 | } 283 | } 284 | } 285 | 286 | for (const attakingPiece of SlidingPieces[attakingSide]) { 287 | for (const direction of PieceDirections[attakingPiece]) { 288 | let targetSq = sq + direction; 289 | while (targetSq !== Squares.offBoard) { 290 | if (gameBoard.pieces[targetSq] !== Pieces.empty) { 291 | if (gameBoard.pieces[targetSq] === attakingPiece) { 292 | return true; 293 | } 294 | break; 295 | } 296 | targetSq += direction; 297 | } 298 | } 299 | } 300 | 301 | return false; 302 | } 303 | -------------------------------------------------------------------------------- /js/evaluation.js: -------------------------------------------------------------------------------- 1 | const PawnTable = [ 2 | 0, 0, 0, 0, 0, 0, 0, 0, 3 | 10, 10, 0, -10, -10, 0, 10, 10, 4 | 5, 0, 0, 5, 5, 0, 0, 5, 5 | 0, 0, 10, 20, 20, 10, 0, 0, 6 | 5, 5, 5, 10, 10, 5, 5, 5, 7 | 10, 10, 10, 20, 20, 10, 10, 10, 8 | 20, 20, 20, 30, 30, 20, 20, 20, 9 | 0, 0, 0, 0, 0, 0, 0, 0 10 | ]; 11 | 12 | 13 | const KnightTable = [ 14 | 0, -10, 0, 0, 0, 0, -10, 0, 15 | 0, 0, 0, 5, 5, 0, 0, 0, 16 | 0, 0, 10, 10, 10, 10, 0, 0, 17 | 0, 0, 10, 20, 20, 10, 5, 0, 18 | 5, 10, 15, 20, 20, 15, 10, 5, 19 | 5, 10, 10, 20, 20, 10, 10, 5, 20 | 0, 0, 5, 10, 10, 5, 0, 0, 21 | 0, 0, 0, 0, 0, 0, 0, 0 22 | ]; 23 | 24 | const BishopTable = [ 25 | 0, 0, -10, 0, 0, -10, 0, 0, 26 | 0, 0, 0, 10, 10, 0, 0, 0, 27 | 0, 0, 10, 15, 15, 10, 0, 0, 28 | 0, 10, 15, 20, 20, 15, 10, 0, 29 | 0, 10, 15, 20, 20, 15, 10, 0, 30 | 0, 0, 10, 15, 15, 10, 0, 0, 31 | 0, 0, 0, 10, 10, 0, 0, 0, 32 | 0, 0, 0, 0, 0, 0, 0, 0 33 | ]; 34 | 35 | const RookTable = [ 36 | 0, 0, 5, 10, 10, 5, 0, 0, 37 | 0, 0, 5, 10, 10, 5, 0, 0, 38 | 0, 0, 5, 10, 10, 5, 0, 0, 39 | 0, 0, 5, 10, 10, 5, 0, 0, 40 | 0, 0, 5, 10, 10, 5, 0, 0, 41 | 0, 0, 5, 10, 10, 5, 0, 0, 42 | 25, 25, 25, 25, 25, 25, 25, 25, 43 | 0, 0, 5, 10, 10, 5, 0, 0 44 | ]; 45 | const KingE = [ 46 | -50, -10, 0, 0, 0, 0, -10, -50, 47 | -10, 0, 10, 10, 10, 10, 0, -10, 48 | 0, 10, 20, 20, 20, 20, 10, 0, 49 | 0, 10, 20, 40, 40, 20, 10, 0, 50 | 0, 10, 20, 40, 40, 20, 10, 0, 51 | 0, 10, 20, 20, 20, 20, 10, 0, 52 | -10, 0, 10, 10, 10, 10, 0, -10, 53 | -50, -10, 0, 0, 0, 0, -10, -50 54 | ]; 55 | 56 | const KingO = [ 57 | 0, 5, 5, -10, -10, 0, 10, 5, 58 | -30, -30, -30, -30, -30, -30, -30, -30, 59 | -50, -50, -50, -50, -50, -50, -50, -50, 60 | -70, -70, -70, -70, -70, -70, -70, -70, 61 | -70, -70, -70, -70, -70, -70, -70, -70, 62 | -70, -70, -70, -70, -70, -70, -70, -70, 63 | -70, -70, -70, -70, -70, -70, -70, -70, 64 | -70, -70, -70, -70, -70, -70, -70, -70 65 | ]; 66 | 67 | const BishopPair = 40; 68 | const PawnIsolated = -10; 69 | const PawnPassed = [0, 5, 10, 20, 35, 60, 100, 200]; 70 | const RookOpenFile = 10; 71 | const RookSemiOpenFile = 5; 72 | const QueenOpenFile = 5; 73 | const QueenSemiOpenFile = 3; 74 | const EndGame_Material = (1 * PieceValue[Pieces.wr]) + (2 * PieceValue[Pieces.wn]) + (2 * PieceValue[Pieces.wp]) + (PieceValue[Pieces.wk]); 75 | 76 | 77 | function evalPosition() { 78 | 79 | var score = gameBoard.material[Color.white] - gameBoard.material[Color.black]; 80 | //pawn 81 | for (const sq of gameBoard.pieceList[Pieces.wp]) { 82 | score += PawnTable[Sq120To64[sq]]; 83 | 84 | if ((IsolatedBitMask[Sq120To64[sq]] & PawnBitBoard[Color.white]) == 0) { 85 | score += PawnIsolated; 86 | } 87 | 88 | if ((PassedPawnBitMask[Color.white][Sq120To64[sq]] & PawnBitBoard[Color.black]) == 0) { 89 | score += PawnPassed[rankOf(sq)]; 90 | } 91 | } 92 | for (const sq of gameBoard.pieceList[Pieces.bp]) { 93 | score -= PawnTable[Mirror64[Sq120To64[sq]]]; 94 | if ((IsolatedBitMask[Sq120To64[sq]] & PawnBitBoard[Color.black]) == 0) { 95 | score -= PawnIsolated; 96 | } 97 | 98 | if ((PassedPawnBitMask[Color.black][Sq120To64[sq]] & PawnBitBoard[Color.white]) == 0) { 99 | score -= PawnPassed[7 - rankOf(sq)]; 100 | } 101 | } 102 | 103 | //knight 104 | for (const sq of gameBoard.pieceList[Pieces.wn]) { 105 | score += KnightTable[Sq120To64[sq]]; 106 | } 107 | for (const sq of gameBoard.pieceList[Pieces.bn]) { 108 | score -= KnightTable[Mirror64[Sq120To64[sq]]]; 109 | } 110 | 111 | //bishop 112 | for (const sq of gameBoard.pieceList[Pieces.wb]) { 113 | score += BishopTable[Sq120To64[sq]]; 114 | } 115 | for (const sq of gameBoard.pieceList[Pieces.bb]) { 116 | score -= BishopTable[Mirror64[Sq120To64[sq]]]; 117 | } 118 | 119 | //rook 120 | for (const sq of gameBoard.pieceList[Pieces.wr]) { 121 | score += RookTable[Sq120To64[sq]]; 122 | //open file bonus 123 | if ((PawnBitBoard.getBoth() & FileBitMask[fileOf(sq)]) == 0) { 124 | score += RookOpenFile; 125 | } 126 | else if ((PawnBitBoard[Color.white] & FileBitMask[fileOf(sq)]) == 0) { 127 | score += RookSemiOpenFile; 128 | } 129 | } 130 | for (const sq of gameBoard.pieceList[Pieces.br]) { 131 | score -= RookTable[Mirror64[Sq120To64[sq]]]; 132 | //open file bonus 133 | if ((PawnBitBoard.getBoth() & FileBitMask[fileOf(sq)]) == 0) { 134 | score -= RookOpenFile; 135 | } 136 | else if ((PawnBitBoard[Color.black] & FileBitMask[fileOf(sq)]) == 0) { 137 | score -= RookSemiOpenFile; 138 | } 139 | } 140 | 141 | //queen 142 | for (const sq of gameBoard.pieceList[Pieces.wq]) { 143 | //open file bonus 144 | if ((PawnBitBoard.getBoth() & FileBitMask[fileOf(sq)]) == 0) { 145 | score += QueenOpenFile; 146 | } 147 | else if ((PawnBitBoard[Color.white] & FileBitMask[fileOf(sq)]) == 0) { 148 | score += QueenSemiOpenFile; 149 | } 150 | } 151 | 152 | for (const sq of gameBoard.pieceList[Pieces.bq]) { 153 | //open file bonus 154 | if ((PawnBitBoard.getBoth() & FileBitMask[fileOf(sq)]) == 0) { 155 | score -= QueenOpenFile; 156 | } 157 | else if ((PawnBitBoard[Color.black] & FileBitMask[fileOf(sq)]) == 0) { 158 | score -= QueenSemiOpenFile; 159 | } 160 | } 161 | 162 | //white king 163 | let kingSq = gameBoard.pieceList[Pieces.wk][0]; 164 | if (gameBoard.material[Color.black] <= EndGame_Material) { 165 | score += KingE[Sq120To64[kingSq]]; 166 | } 167 | else { 168 | score += KingO[Sq120To64[kingSq]]; 169 | } 170 | 171 | //black king 172 | kingSq = gameBoard.pieceList[Pieces.bk][0]; 173 | if (gameBoard.material[Color.white] <= EndGame_Material) { 174 | score -= KingE[Mirror64[Sq120To64[kingSq]]]; 175 | } 176 | else { 177 | score -= KingO[Mirror64[Sq120To64[kingSq]]]; 178 | } 179 | 180 | 181 | //bonus 182 | if (gameBoard.pieceCount[Pieces.wb] >= 2) { 183 | score += BishopPair; 184 | } 185 | if (gameBoard.pieceCount[Pieces.bb] >= 2) { 186 | score -= BishopPair; 187 | } 188 | 189 | return (gameBoard.side == Color.white) ? score : -score; 190 | } 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /js/generateMove.js: -------------------------------------------------------------------------------- 1 | let moves; 2 | function generateMoves() { 3 | moves = []; 4 | 5 | if (gameBoard.side == Color.white) { 6 | for (const sq of gameBoard.pieceList[Pieces.wp]) { 7 | if (gameBoard.pieces[sq + 10] == Pieces.empty) { 8 | addWhitePawnQuietMove(sq, sq + 10); 9 | if (gameBoard.pieces[sq + 20] == Pieces.empty && rankOf(sq) == Rank2) { 10 | addQuiteMove(buildMove(sq, sq + 20, 0, 0, PawnStartFlag)); 11 | } 12 | } 13 | 14 | //add capture move 15 | if (gameBoard.pieces[sq + 9] != Squares.offBoard && PieceColor[gameBoard.pieces[sq + 9]] == Color.black) { 16 | addWhiteCaptureMove(sq, sq + 9, gameBoard.pieces[sq + 9]); 17 | } 18 | if (gameBoard.pieces[sq + 11] != Squares.offBoard && PieceColor[gameBoard.pieces[sq + 11]] == Color.black) { 19 | addWhiteCaptureMove(sq, sq + 11, gameBoard.pieces[sq + 11]); 20 | } 21 | 22 | //add enpassant move 23 | if (sq + 9 == gameBoard.enPassantSq) { 24 | addEnPassantMove(buildMove(sq, sq + 9, 0, 0, EnPassantFlag)); 25 | } 26 | if (sq + 11 == gameBoard.enPassantSq) { 27 | addEnPassantMove(buildMove(sq, sq + 11, 0, 0, EnPassantFlag)); 28 | } 29 | 30 | } 31 | 32 | 33 | //castling 34 | 35 | if (gameBoard.castlePermission & CastleBit.K) { 36 | if (gameBoard.pieces[Squares.f1] == Pieces.empty && gameBoard.pieces[Squares.g1] == Pieces.empty) { 37 | if (gameBoard.checkSq == Squares.noSq && !isUnderAttack(Squares.f1, Color.black)) { 38 | addQuiteMove(buildMove(Squares.e1, Squares.g1, 0, 0, CastleFlag)); 39 | } 40 | } 41 | } 42 | if (gameBoard.castlePermission & CastleBit.Q) { 43 | if (gameBoard.pieces[Squares.d1] == Pieces.empty && gameBoard.pieces[Squares.c1] == Pieces.empty && gameBoard.pieces[Squares.b1] == Pieces.empty) { 44 | if (gameBoard.checkSq == Squares.noSq && !isUnderAttack(Squares.d1, Color.black)) { 45 | addQuiteMove(buildMove(Squares.e1, Squares.c1, 0, 0, CastleFlag)); 46 | } 47 | } 48 | } 49 | 50 | } 51 | else { 52 | for (const sq of gameBoard.pieceList[Pieces.bp]) { 53 | if (gameBoard.pieces[sq - 10] == Pieces.empty) { 54 | //add a white quite move 55 | addBlackPawnQuietMove(sq, sq - 10); 56 | 57 | if (gameBoard.pieces[sq - 20] == Pieces.empty && rankOf(sq) == Rank7) { 58 | //add pawn first move 59 | addQuiteMove(buildMove(sq, sq - 20, 0, 0, PawnStartFlag)); 60 | } 61 | } 62 | 63 | //add capture move 64 | if (gameBoard.pieces[sq - 9] != Squares.offBoard && PieceColor[gameBoard.pieces[sq - 9]] == Color.white) { 65 | addBlackCaptureMove(sq, sq - 9, gameBoard.pieces[sq - 9]); 66 | } 67 | if (gameBoard.pieces[sq - 11] != Squares.offBoard && PieceColor[gameBoard.pieces[sq - 11]] == Color.white) { 68 | addBlackCaptureMove(sq, sq - 11, gameBoard.pieces[sq - 11]); 69 | } 70 | 71 | //add enpassant move 72 | if (sq - 9 == gameBoard.enPassantSq) { 73 | addEnPassantMove(buildMove(sq, sq - 9, 0, 0, EnPassantFlag)); 74 | } 75 | if (sq - 11 == gameBoard.enPassantSq) { 76 | addEnPassantMove(buildMove(sq, sq - 11, 0, 0, EnPassantFlag)); 77 | } 78 | } 79 | 80 | 81 | //castling 82 | if (gameBoard.castlePermission & CastleBit.k) { 83 | if (gameBoard.pieces[Squares.f8] == Pieces.empty && gameBoard.pieces[Squares.g8] == Pieces.empty) { 84 | if (gameBoard.checkSq == Squares.noSq && !isUnderAttack(Squares.f8, Color.white)) { 85 | addQuiteMove(buildMove(Squares.e8, Squares.g8, 0, 0, CastleFlag)); 86 | } 87 | } 88 | } 89 | if (gameBoard.castlePermission & CastleBit.q) { 90 | if (gameBoard.pieces[Squares.d8] == Pieces.empty && gameBoard.pieces[Squares.c8] == Pieces.empty && gameBoard.pieces[Squares.b8] == Pieces.empty) { 91 | if (gameBoard.checkSq == Squares.noSq && !isUnderAttack(Squares.d8, Color.white)) { 92 | addQuiteMove(buildMove(Squares.e8, Squares.c8, 0, 0, CastleFlag)); 93 | } 94 | } 95 | } 96 | 97 | } 98 | 99 | generateSlidingMoves(); 100 | generateNonSlidingMoves(); 101 | 102 | return moves; 103 | } 104 | 105 | function generateSlidingMoves({ captureOnly = false } = {}) { 106 | for (const piece of SlidingPieces[gameBoard.side]) { 107 | for (const sq of gameBoard.pieceList[piece]) { 108 | for (const direction of PieceDirections[piece]) { 109 | let targetSq = sq + direction; 110 | 111 | while (gameBoard.pieces[targetSq] != Squares.offBoard) { 112 | if (gameBoard.pieces[targetSq] != Pieces.empty) { 113 | if (PieceColor[gameBoard.pieces[targetSq]] != gameBoard.side) { 114 | addCaptureMove(buildMove(sq, targetSq, gameBoard.pieces[targetSq], 0, 0)); 115 | } 116 | break; 117 | } 118 | else if (!captureOnly) { 119 | addQuiteMove(buildMove(sq, targetSq, 0, 0, 0)); 120 | } 121 | targetSq += direction; 122 | } 123 | 124 | } 125 | } 126 | } 127 | } 128 | 129 | function generateNonSlidingMoves({ captureOnly = false } = {}) { 130 | for (const piece of NonSlidingPieces[gameBoard.side]) { 131 | for (const sq of gameBoard.pieceList[piece]) { 132 | for (const direction of PieceDirections[piece]) { 133 | 134 | let targetSq = sq + direction; 135 | if (gameBoard.pieces[targetSq] == Squares.offBoard) { 136 | continue; 137 | } 138 | if (gameBoard.pieces[targetSq] == Pieces.empty) { 139 | if (!captureOnly) { 140 | addQuiteMove(buildMove(sq, targetSq, 0, 0, 0)); 141 | } 142 | } 143 | else if (PieceColor[gameBoard.pieces[targetSq]] != gameBoard.side) { 144 | addCaptureMove(buildMove(sq, targetSq, gameBoard.pieces[targetSq], 0, 0)); 145 | } 146 | } 147 | } 148 | } 149 | } 150 | 151 | 152 | function generateCaptureMoves() { 153 | moves = []; 154 | 155 | if (gameBoard.side == Color.white) { 156 | for (const sq of gameBoard.pieceList[Pieces.wp]) { 157 | 158 | //add capture move 159 | if (sq + 9 != Squares.offBoard && PieceColor[gameBoard.pieces[sq + 9]] == Color.black) { 160 | addWhiteCaptureMove(sq, sq + 9, gameBoard.pieces[sq + 9]); 161 | } 162 | if (sq + 11 != Squares.offBoard && PieceColor[gameBoard.pieces[sq + 11]] == Color.black) { 163 | addWhiteCaptureMove(sq, sq + 11, gameBoard.pieces[sq + 11]); 164 | } 165 | 166 | //add enpassant move 167 | if (sq + 9 == gameBoard.enPassantSq) { 168 | addEnPassantMove(buildMove(sq, sq + 9, 0, 0, EnPassantFlag)); 169 | } 170 | if (sq + 11 == gameBoard.enPassantSq) { 171 | addEnPassantMove(buildMove(sq, sq + 11, 0, 0, EnPassantFlag)); 172 | } 173 | 174 | } 175 | 176 | } 177 | else { 178 | for (const sq of gameBoard.pieceList[Pieces.bp]) { 179 | 180 | //add capture move 181 | if (sq - 9 != Squares.offBoard && PieceColor[gameBoard.pieces[sq - 9]] == Color.white) { 182 | addBlackCaptureMove(sq, sq - 9, gameBoard.pieces[sq - 9]); 183 | } 184 | if (sq - 11 != Squares.offBoard && PieceColor[gameBoard.pieces[sq - 11]] == Color.white) { 185 | addBlackCaptureMove(sq, sq - 11, gameBoard.pieces[sq - 11]); 186 | } 187 | 188 | //add enpassant move 189 | if (sq - 9 == gameBoard.enPassantSq) { 190 | addEnPassantMove(buildMove(sq, sq - 9, 0, 0, EnPassantFlag)); 191 | } 192 | if (sq - 11 == gameBoard.enPassantSq) { 193 | addEnPassantMove(buildMove(sq, sq - 11, 0, 0, EnPassantFlag)); 194 | } 195 | } 196 | } 197 | 198 | generateSlidingMoves({ captureOnly: true }); 199 | generateNonSlidingMoves({ captureOnly: true }); 200 | 201 | return moves; 202 | } 203 | 204 | //MvvLva = [victim][attacker] 205 | let MvvLva = [ 206 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 207 | [0, 105, 102, 104, 103, 101, 100, 105, 102, 104, 103, 101, 100], 208 | [0, 405, 402, 404, 403, 401, 400, 405, 402, 404, 403, 401, 400], 209 | [0, 205, 202, 204, 203, 201, 200, 205, 202, 204, 203, 201, 200], 210 | [0, 305, 302, 304, 303, 301, 300, 305, 302, 304, 303, 301, 300], 211 | [0, 505, 502, 504, 503, 501, 500, 505, 502, 504, 503, 501, 500], 212 | [0, 605, 602, 604, 603, 601, 600, 605, 602, 604, 603, 601, 600], 213 | [0, 105, 102, 104, 103, 101, 100, 105, 102, 104, 103, 101, 100], 214 | [0, 405, 402, 404, 403, 401, 400, 405, 402, 404, 403, 401, 400], 215 | [0, 205, 202, 204, 203, 201, 200, 205, 202, 204, 203, 201, 200], 216 | [0, 305, 302, 304, 303, 301, 300, 305, 302, 304, 303, 301, 300], 217 | [0, 505, 502, 504, 503, 501, 500, 505, 502, 504, 503, 501, 500], 218 | [0, 605, 602, 604, 603, 601, 600, 605, 602, 604, 603, 601, 600], 219 | ]; 220 | 221 | 222 | function addCaptureMove(move) { 223 | let victim = moveCapturePiece(move); 224 | let attacker = gameBoard.pieces[moveFrom(move)]; 225 | let score = MvvLva[victim][attacker] + 1000000; 226 | moves.push({ score, move }); 227 | } 228 | 229 | function addQuiteMove(move) { 230 | let score = 0; 231 | if (searchController.killers[searchController.ply][0] == move) { 232 | score = 900000; 233 | } 234 | else if (searchController.killers[searchController.ply][1] == move) { 235 | score = 800000; 236 | } 237 | else { 238 | let piece = gameBoard.pieces[moveFrom(move)]; 239 | let toSq = moveTo(move); 240 | score = searchController.history[piece][toSq]; 241 | } 242 | 243 | moves.push({ score, move }); 244 | } 245 | 246 | function addEnPassantMove(move) { 247 | //MvvLva[Pieces.wp][Pieces.bp] == MvvLva[Pieces.bp][Pieces.wp] 248 | let score = MvvLva[Pieces.wp][Pieces.bp] + 1000000; 249 | moves.push({ score, move }); 250 | } 251 | 252 | 253 | function addWhitePawnQuietMove(from, to) { 254 | //handling promotion move 255 | if (rankOf(to) === Rank8) { 256 | addQuiteMove(buildMove(from, to, 0, Pieces.wq, 0)); 257 | addQuiteMove(buildMove(from, to, 0, Pieces.wr, 0)); 258 | addQuiteMove(buildMove(from, to, 0, Pieces.wb, 0)); 259 | addQuiteMove(buildMove(from, to, 0, Pieces.wn, 0)); 260 | } 261 | else { 262 | addQuiteMove(buildMove(from, to, 0, 0, 0)); 263 | } 264 | } 265 | function addBlackPawnQuietMove(from, to) { 266 | //handling promotion move 267 | if (rankOf(to) === Rank1) { 268 | addQuiteMove(buildMove(from, to, 0, Pieces.bq, 0)); 269 | addQuiteMove(buildMove(from, to, 0, Pieces.br, 0)); 270 | addQuiteMove(buildMove(from, to, 0, Pieces.bb, 0)); 271 | addQuiteMove(buildMove(from, to, 0, Pieces.bn, 0)); 272 | } 273 | else { 274 | addQuiteMove(buildMove(from, to, 0, 0, 0)); 275 | } 276 | } 277 | 278 | 279 | 280 | function addWhiteCaptureMove(from, to, capture) { 281 | //handling promotion move 282 | if (rankOf(to) === Rank8) { 283 | addCaptureMove(buildMove(from, to, capture, Pieces.wq, 0)); 284 | addCaptureMove(buildMove(from, to, capture, Pieces.wr, 0)); 285 | addCaptureMove(buildMove(from, to, capture, Pieces.wb, 0)); 286 | addCaptureMove(buildMove(from, to, capture, Pieces.wn, 0)); 287 | } 288 | else { 289 | addCaptureMove(buildMove(from, to, capture, 0, 0)); 290 | } 291 | } 292 | function addBlackCaptureMove(from, to, capture) { 293 | //handling promotion move 294 | if (rankOf(to) === Rank1) { 295 | addCaptureMove(buildMove(from, to, capture, Pieces.bq, 0)); 296 | addCaptureMove(buildMove(from, to, capture, Pieces.br, 0)); 297 | addCaptureMove(buildMove(from, to, capture, Pieces.bb, 0)); 298 | addCaptureMove(buildMove(from, to, capture, Pieces.bn, 0)); 299 | } 300 | else { 301 | addCaptureMove(buildMove(from, to, capture, 0, 0)); 302 | } 303 | } -------------------------------------------------------------------------------- /js/gui/controlls.js: -------------------------------------------------------------------------------- 1 | const backdrop = document.querySelector('.backdrop'); 2 | const downloadWindow = backdrop.querySelector('.download-window'); 3 | const settingWindow = backdrop.querySelector('.setting-window'); 4 | const confirmWindow = backdrop.querySelector('.confirm-popover'); 5 | 6 | const downloadOptions = downloadWindow.querySelectorAll('.option>*'); 7 | const downloadBtn = downloadWindow.querySelector('.btn.download'); 8 | 9 | const confirmBtn = document.getElementById('confirm'); 10 | const setupBtn = document.getElementById('setup-btn'); 11 | const gameOver = document.querySelector('.game-over'); 12 | const closeResult = gameOver.querySelector('.close'); 13 | 14 | const pgnOutput = downloadWindow.querySelector('.pgn textarea'); 15 | const fenOutput = downloadWindow.querySelector('.fen input'); 16 | 17 | const uploadPgnBtn = document.getElementById('upload-pgn-btn'); 18 | const uploadPgnInput = document.getElementById('upload-pgn-input'); 19 | const uploadPgnContainer = document.querySelector('.upload'); 20 | const uploadFenInput = document.getElementById('upload-fen-input'); 21 | const uploadProgressBar = document.getElementById('upload-bar'); 22 | const winAnimation = document.querySelector('dotlottie-player.win-animation'); 23 | const uploadBookWindow = document.querySelector('.upload-book'); 24 | 25 | const themes = document.querySelectorAll('.theme'); 26 | themes.forEach(theme=>{ 27 | theme.addEventListener('click', ()=>{ 28 | themes.forEach(e=>e.classList.remove('active')); 29 | theme.classList.add('active'); 30 | board.className = theme.textContent; 31 | }) 32 | }) 33 | document.getElementById('clear-board').addEventListener('click', () => { 34 | parseFen('8/8/8/8/8/8/8/8 w KQkq -'); 35 | gui.renderPieces(); 36 | }) 37 | document.getElementById('reset-board').addEventListener('click', () => { 38 | parseFen(StartingFen); 39 | gui.renderPieces(); 40 | }) 41 | 42 | document.addEventListener('mousedown', (e) => { 43 | if (!gameOver.contains(e.target)) { 44 | gameOver.classList.remove('active'); 45 | } 46 | }); 47 | gameOver.querySelectorAll('.btn').forEach(btn => { 48 | btn.addEventListener('click', () => { 49 | gameOver.classList.remove('active'); 50 | }) 51 | }) 52 | closeResult.addEventListener('click', () => { 53 | gameOver.classList.remove('active'); 54 | }) 55 | 56 | const popup = document.querySelector('.popup'); 57 | const popupMessage = popup.querySelector('.message'); 58 | function showPopup(message, success = true) { 59 | popupMessage.textContent = message; 60 | popup.classList.remove('success', 'danger'); 61 | popup.classList.add('active', success ? 'success' : 'danger') 62 | setTimeout(()=>{ 63 | popup.classList.remove('active'); 64 | },2000); 65 | } 66 | 67 | function removeAllWindows(){ 68 | document.querySelectorAll('.window').forEach(window=>{ 69 | window.classList.remove('active'); 70 | }) 71 | } 72 | function showSettingWindow() { 73 | removeAllWindows(); 74 | backdrop.classList.add('active'); 75 | settingWindow.classList.add('active'); 76 | } 77 | function showConfirmWindow() { 78 | uploadPgnInput.value = ''; 79 | confirmBtn.textContent = 'new game'; 80 | 81 | removeAllWindows(); 82 | backdrop.classList.add('active'); 83 | confirmWindow.classList.add('active'); 84 | uploadProgressBar.style.width = `0%`; 85 | } 86 | 87 | 88 | function showDownloadWindow() { 89 | pgnOutput.value = getPGN(); 90 | fenOutput.value = getFen(); 91 | 92 | removeAllWindows(); 93 | backdrop.classList.add('active'); 94 | downloadWindow.classList.add('active'); 95 | } 96 | 97 | function showUploadBookWindow() { 98 | removeAllWindows(); 99 | backdrop.classList.add('active'); 100 | uploadBookWindow.classList.add('active'); 101 | } 102 | 103 | backdrop.addEventListener('click', (e) => { 104 | if (e.target.contains(backdrop)) { 105 | removeBackdrop(); 106 | } 107 | }) 108 | 109 | function removeBackdrop() { 110 | backdrop.classList.remove('active'); 111 | } 112 | 113 | setupBtn.addEventListener('click', () => { 114 | setup = true; 115 | setupPosition.classList.add('active'); 116 | removeBackdrop(); 117 | }) 118 | 119 | confirmBtn.addEventListener('click', () => { 120 | if (uploadPgnInput.value) { 121 | try { 122 | parsePGN(uploadPgnInput.value); 123 | } catch (error) { 124 | console.error(error); 125 | alert(error); 126 | } 127 | } 128 | else { 129 | newGame(uploadFenInput.value); 130 | } 131 | removeBackdrop(); 132 | }) 133 | 134 | downloadOptions.forEach(option => { 135 | option.addEventListener('click', () => { 136 | downloadOptions.forEach(e => e.classList.remove('active')); 137 | option.classList.add('active'); 138 | downloadFormate = option.textContent; 139 | }) 140 | }) 141 | 142 | let downloadFormate = 'PGN'; 143 | downloadBtn.addEventListener('click', () => { 144 | if (downloadFormate == 'PGN') { 145 | downloadPgn(); 146 | } 147 | else if (downloadFormate == 'Image') { 148 | downloadImage(); 149 | } 150 | removeBackdrop(); 151 | }) 152 | 153 | function downloadPgn() { 154 | let pgn = pgnOutput.value; 155 | if (pgn === '') return; 156 | 157 | const a = document.createElement('a'); 158 | const blob = new Blob([pgn], { type: 'application/x-chess-pgn' }); 159 | 160 | a.href = URL.createObjectURL(blob); 161 | a.download = 'game.pgn'; 162 | 163 | a.click(); 164 | a.remove(); 165 | } 166 | 167 | function downloadImage() { 168 | html2canvas(graphicalBoard) 169 | .then(canvas => { 170 | const a = document.createElement('a'); 171 | a.href = canvas.toDataURL('image/jpeg'); 172 | a.download = 'game.jpg'; 173 | 174 | a.click(); 175 | a.remove(); 176 | }); 177 | } 178 | 179 | const coordinates = document.querySelectorAll('.coordinates *'); 180 | function flipBoard() { 181 | boardLayout.classList.toggle('flipped'); 182 | flipCoordinates(); 183 | } 184 | function flipCoordinates() { 185 | let texts = boardLayout.classList.contains('flipped') ? '12345678hgfedcba' : '87654321abcdefgh' 186 | for (let i = 0; i < coordinates.length; i++) { 187 | coordinates[i].textContent = texts[i]; 188 | } 189 | } 190 | 191 | 192 | 193 | 194 | 195 | uploadPgnContainer.addEventListener('dragover', (e) => { 196 | uploadPgnContainer.classList.add('file-hover'); 197 | e.preventDefault(); 198 | }); 199 | 200 | uploadPgnContainer.addEventListener('dragleave', () => { 201 | uploadPgnContainer.classList.remove('file-hover'); 202 | }); 203 | uploadPgnContainer.addEventListener('drop', (e) => { 204 | e.preventDefault(); 205 | uploadPgnContainer.classList.remove('file-hover'); 206 | const file = e.dataTransfer.files[0]; 207 | resetProgressWidth(); 208 | readPgnFile(file); 209 | }); 210 | 211 | const fileInput = document.getElementById('file-input'); 212 | 213 | uploadPgnBtn.addEventListener('click', () => { 214 | fileInput.click(); 215 | }) 216 | 217 | uploadPgnInput.addEventListener('input', () => { 218 | uploadFenInput.value = ''; 219 | confirmBtn.textContent = (uploadPgnInput.value) ? 'load game' : 'new game'; 220 | }); 221 | uploadFenInput.addEventListener('input', () => { 222 | uploadPgnInput.value = ''; 223 | confirmBtn.textContent = (uploadFenInput.value) ? 'load game' : 'new game'; 224 | }); 225 | 226 | fileInput.addEventListener('change', (e) => { 227 | const file = e.target.files[0]; 228 | resetProgressWidth(); 229 | readPgnFile(file); 230 | }); 231 | 232 | function readPgnFile(file) { 233 | if (!file) return; 234 | 235 | confirmBtn.disabled = true; 236 | 237 | if (!file.name.endsWith('.pgn')) { 238 | alert('Please upload a valid PGN file.'); 239 | return; 240 | } 241 | 242 | const reader = new FileReader(); 243 | reader.addEventListener('load', (e) => { 244 | const pgn = e.target.result; 245 | uploadPgnInput.value = pgn; 246 | uploadFenInput.value = ''; 247 | 248 | confirmBtn.textContent = (uploadPgnInput.value) ? 'load game' : 'new game'; 249 | confirmBtn.disabled = false; 250 | }); 251 | 252 | reader.addEventListener('progress', (e) => { 253 | if (e.lengthComputable) { 254 | const progress = (e.loaded / e.total) * 100; 255 | uploadProgressBar.style.width = `${progress}%`; 256 | } 257 | }); 258 | 259 | reader.addEventListener('error', (e) => { 260 | console.error('File reading error:', e.target.error); 261 | alert(e.target.error); 262 | }); 263 | 264 | reader.readAsText(file); 265 | } 266 | 267 | function resetProgressWidth() { 268 | uploadProgressBar.style.transition = 'none'; 269 | uploadProgressBar.style.width = 0; 270 | void uploadProgressBar.offsetWidth; 271 | uploadProgressBar.style.transition = 'width .3s'; 272 | } 273 | 274 | // ====================================================== 275 | // ========== drop down search time controller ========== 276 | // ====================================================== 277 | const selections = document.querySelectorAll('.selection'); 278 | const options = document.querySelectorAll('.drop-menu .options'); 279 | 280 | function removeSelectFrom(option) { 281 | [...option.children].forEach(child => { 282 | child.classList.remove('select'); 283 | }); 284 | } 285 | 286 | options.forEach(option => { 287 | [...option.children].forEach(child => { 288 | child.addEventListener('click', () => { 289 | removeSelectFrom(option); 290 | child.classList.add('select'); 291 | if (option.id == 'white') { 292 | selections[1].textContent = child.textContent + 's'; 293 | engine.searchTime[Color.white] = +child.textContent; 294 | } 295 | else { 296 | selections[0].textContent = child.textContent + 's'; 297 | engine.searchTime[Color.black] = +child.textContent; 298 | } 299 | }) 300 | }) 301 | }) 302 | selections.forEach(selection => { 303 | selection.addEventListener('click', () => { 304 | removeDropDown(); 305 | selection.parentElement.classList.add('active'); 306 | }) 307 | }) 308 | 309 | function removeDropDown() { 310 | selections.forEach(selection => { 311 | selection.parentElement.classList.remove('active'); 312 | }) 313 | } 314 | document.addEventListener('click', (e) => { 315 | if (!e.target.classList.contains('selection')) { 316 | removeDropDown(); 317 | } 318 | }) 319 | 320 | window.addEventListener('keydown', (e) => { 321 | if (e.keyCode === 37) { 322 | gui.undoMove(); 323 | } 324 | else if (e.keyCode === 39) { 325 | moveForward(); 326 | } 327 | }) 328 | 329 | const uploadBookBtn = document.getElementById('upload-book-btn'); 330 | const bookInput = document.getElementById('book-input'); 331 | const bookUploadProgress = document.querySelector('.book .progress'); 332 | const bookUploadPercent = document.querySelector('.book .percent'); 333 | const library = document.querySelector('.library'); 334 | const bookLoader = document.querySelector('.book'); 335 | const uploadBookName = bookLoader.querySelector('.file-name'); 336 | const defaultBook = library.querySelector('.book'); 337 | 338 | defaultBook.addEventListener('click', selectBook(defaultBook)); 339 | function selectBook(book) { 340 | return () => { 341 | if(book.classList.contains("selected")) return; 342 | document.querySelectorAll('.book').forEach(b => { 343 | b.classList.remove('selected'); 344 | }) 345 | book.classList.add('selected'); 346 | if (book == defaultBook) { 347 | readPolyBook({ path: '../gm2600.bin' }); 348 | } 349 | else { 350 | const fileName = book.querySelector('.file-name').textContent; 351 | readBookFromIndexedDB(fileName); 352 | } 353 | } 354 | } 355 | uploadBookBtn.addEventListener('click', () => { 356 | bookInput.click(); 357 | }) 358 | 359 | bookInput.addEventListener('change', uploadBook); 360 | 361 | function uploadBook() { 362 | const file = bookInput.files[0]; 363 | 364 | if (!file) { 365 | alert('Please select a file!'); 366 | return; 367 | } 368 | bookLoader.style.display = 'block'; 369 | uploadBookName.textContent = file.name; 370 | const reader = new FileReader(); 371 | 372 | reader.onprogress = function (event) { 373 | if (event.lengthComputable) { 374 | const percentLoaded = Math.round((event.loaded / event.total) * 100); 375 | console.log(percentLoaded); 376 | bookUploadProgress.style.width = percentLoaded + '%'; 377 | bookUploadPercent.textContent = percentLoaded + '%'; 378 | } 379 | }; 380 | reader.onload = function (event) { 381 | const buffer = event.target.result; // This is the ArrayBuffer 382 | 383 | // Calculate file size 384 | const fileSize = file.size; 385 | let sizeText; 386 | if (fileSize >= 1024 * 1024) { 387 | sizeText = (fileSize / (1024 * 1024)).toFixed(2) + ' MB'; 388 | } else if (fileSize >= 1024) { 389 | sizeText = (fileSize / 1024).toFixed(2) + ' KB'; 390 | } else { 391 | sizeText = fileSize + ' B'; 392 | } 393 | addBookToIndexedDB(buffer, file.name, sizeText); 394 | addBook(file.name, sizeText); 395 | bookLoader.style.display = 'none'; 396 | } 397 | reader.readAsArrayBuffer(file); 398 | } 399 | 400 | 401 | function addBook(name, size, selected = false) { 402 | const book = document.createElement('div'); 403 | const img = document.createElement('img'); 404 | const detail = document.createElement('div'); 405 | const fileName = document.createElement('div'); 406 | const fileSize = document.createElement('div'); 407 | const del = document.createElement('img'); 408 | if (selected) { 409 | book.className.add('selected'); 410 | } 411 | img.classList.add('icon'); 412 | book.classList.add('book'); 413 | detail.classList.add('detail'); 414 | fileName.classList.add('file-name'); 415 | fileSize.classList.add('file-size'); 416 | del.classList.add('icon', 'delete'); 417 | del.addEventListener('click', () => { 418 | removeBookFromIndexedDB(fileName.textContent); 419 | if (book.classList.contains('selected')) { 420 | setTimeout(() => { 421 | defaultBook.click(); 422 | }, 0); 423 | } 424 | book.remove(); 425 | }) 426 | img.src = "./assets/icons/book.svg" 427 | del.src = "./assets/icons/delete.png" 428 | 429 | fileSize.textContent = size; 430 | fileName.textContent = name; 431 | 432 | 433 | detail.appendChild(fileName); 434 | detail.appendChild(fileSize); 435 | 436 | book.appendChild(img); 437 | book.appendChild(detail); 438 | book.appendChild(del); 439 | book.addEventListener('click', selectBook(book)); 440 | library.appendChild(book); 441 | } 442 | 443 | -------------------------------------------------------------------------------- /js/gui/pieces.js: -------------------------------------------------------------------------------- 1 | const pieces = document.querySelectorAll('.piece'); -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | (function main() { 2 | init(); 3 | parseFen(StartingFen); 4 | if (typeof window !== 'undefined') { 5 | gui.renderPieces(); 6 | } 7 | readPolyBook({path:'../gm2600.bin'}); 8 | loadBooksFromIndexedDB(); 9 | })(); 10 | 11 | function init() { 12 | init_SquareSwitch(); 13 | init_FileBitMask(); 14 | init_RankBitMask(); 15 | init_PassedPawnBitMask(); 16 | init_IsolatedBitMask(); 17 | } 18 | 19 | 20 | function init_FileBitMask() { 21 | for (let file = FileA; file <= FileH; ++file) { 22 | for (let rank = Rank1; rank <= Rank8; ++rank) { 23 | FileBitMask[file] |= (1n << BigInt(file)) << BigInt(8 * rank); 24 | } 25 | } 26 | } 27 | 28 | function init_RankBitMask() { 29 | for (let rank = Rank1; rank <= Rank8; ++rank) { 30 | RankBitMask[rank] |= 255n << BigInt(rank * 8); 31 | } 32 | } 33 | 34 | function init_IsolatedBitMask() { 35 | for (let sq = 0; sq < 64; sq++) { 36 | let file = fileOf(Sq64To120[sq]); 37 | if (file > FileA) { 38 | IsolatedBitMask[sq] |= FileBitMask[file - 1]; 39 | } 40 | if (file < FileH) { 41 | IsolatedBitMask[sq] |= FileBitMask[file + 1]; 42 | } 43 | } 44 | } 45 | 46 | function init_PassedPawnBitMask() { 47 | for (let i = 0; i < 2; ++i) { 48 | PassedPawnBitMask[i] = new Array(64).fill(0n); 49 | 50 | for (let sq = 0; sq < 64; sq++) { 51 | let file = fileOf(Sq64To120[sq]); 52 | let rank = rankOf(Sq64To120[sq]); 53 | 54 | PassedPawnBitMask[i][sq] |= FileBitMask[file]; 55 | if (file > FileA) { 56 | PassedPawnBitMask[i][sq] |= FileBitMask[file - 1]; 57 | } 58 | if (file < FileH) { 59 | PassedPawnBitMask[i][sq] |= FileBitMask[file + 1]; 60 | } 61 | 62 | if (i == Color.white) { 63 | while (rank >= Rank1) { 64 | PassedPawnBitMask[i][sq] &= ~RankBitMask[rank--]; 65 | } 66 | } 67 | else { 68 | while (rank <= Rank8) { 69 | PassedPawnBitMask[i][sq] &= ~RankBitMask[rank++]; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | function init_SquareSwitch() { 77 | for (let rank = Rank1; rank <= Rank8; ++rank) { 78 | for (let file = FileA; file <= FileH; ++file) { 79 | let sq120 = fileRank2Sq(file, rank); 80 | let sq64 = rank * 8 + file; 81 | Sq120To64[sq120] = sq64; 82 | Sq64To120[sq64] = sq120; 83 | } 84 | } 85 | } 86 | 87 | 88 | function printArr(arr, rows, cols) { 89 | for (let i = 0; i < rows; ++i) { 90 | let line = `${i} `; 91 | for (let j = 0; j < cols; ++j) { 92 | let val = arr[i * cols + j]; 93 | if (val >= 0 && val <= 9) { 94 | line += '0' + val + ' '; 95 | } 96 | else if (!val) { 97 | line += '.. ' 98 | } 99 | else { 100 | line += val + ' '; 101 | } 102 | } 103 | console.log(line); 104 | } 105 | } -------------------------------------------------------------------------------- /js/moveStructure.js: -------------------------------------------------------------------------------- 1 | //move structure set Mask 2 | // 0000 0000 0000 0000 0000 0111 1111 0x7f from 3 | // 0000 0000 0000 0011 1111 1000 0000 0x7f to 4 | // 0000 0000 0011 1100 0000 0000 0000 0xf capture piece 5 | // 0000 0011 1100 0000 0000 0000 0000 0xf promoted piece 6 | // 0000 0100 0000 0000 0000 0000 0000 0x40000 enpassant 7 | // 0000 1000 0000 0000 0000 0000 0000 0x80000 castle 8 | // 0001 0000 0000 0000 0000 0000 0000 0x1000000 pawnstart 9 | // 0010 0000 0000 0000 0000 0000 0000 0x2000000 from Book 10 | 11 | 12 | //flags 🚩 13 | const EnPassantFlag = 0x400000; 14 | const CastleFlag = 0x800000; 15 | const PawnStartFlag = 0x1000000; 16 | const FromBookFlag = 0x2000000; 17 | 18 | const CaptureFlag = 0x3c000; 19 | const PromotionFlag = 0x3c0000; 20 | 21 | function buildMove(from, to, capturedPiece, promotedPiece, flag) { 22 | return from | (to << 7) | (capturedPiece << 14) | (promotedPiece << 18) | flag; 23 | } 24 | 25 | 26 | function moveFrom(move) { 27 | return (move & 0x7f); 28 | } 29 | function moveTo(move) { 30 | return (move >> 7) & 0x7f; 31 | 32 | } 33 | function moveCapturePiece(move) { 34 | return (move >> 14) & 0xf; 35 | } 36 | function movePromotionPiece(move) { 37 | return (move >> 18) & 0xf; 38 | } 39 | 40 | function moveDetail(move) { 41 | if (!move) return; 42 | 43 | console.log('from :', SquaresChar[moveFrom(move)]); 44 | console.log('to :', SquaresChar[moveTo(move)]); 45 | console.log('capture :', PieceChar[moveCapturePiece(move)]); 46 | console.log('promotion :', PieceChar[movePromotionPiece(move)]); 47 | 48 | if (move & EnPassantFlag) console.log('flag : enPassant'); 49 | if (move & PawnStartFlag) console.log('flag : pawn start'); 50 | if (move & CastleFlag) console.log('flag : castle'); 51 | console.log(''); 52 | } 53 | 54 | function movePiece(from, to) { 55 | let piece = gameBoard.pieces[from]; 56 | 57 | hashPiece(from, piece); 58 | gameBoard.pieces[from] = Pieces.empty; 59 | 60 | hashPiece(to, piece); 61 | gameBoard.pieces[to] = piece; 62 | 63 | if(PieceType[piece] == 'p'){ 64 | PawnBitBoard.clearBit(PieceColor[piece], Sq120To64[from]); 65 | PawnBitBoard.setBit(PieceColor[piece], Sq120To64[to]); 66 | } 67 | 68 | for (let i = 0; i < gameBoard.pieceCount[piece]; ++i) { 69 | if (gameBoard.pieceList[piece][i] === from) { 70 | gameBoard.pieceList[piece][i] = to 71 | return; 72 | } 73 | } 74 | } 75 | function addPiece(sq, piece) { 76 | if (gameBoard.pieces[sq] !== Pieces.empty) { 77 | console.error('add piece error'); 78 | return; 79 | } 80 | hashPiece(sq, piece); 81 | gameBoard.pieces[sq] = piece; 82 | gameBoard.material[PieceColor[piece]] += PieceValue[piece]; 83 | gameBoard.pieceList[piece].push(sq); 84 | gameBoard.pieceCount[piece]++; 85 | if (PieceType[piece] == 'p') { 86 | PawnBitBoard.setBit(PieceColor[piece], Sq120To64[sq]) 87 | } 88 | } 89 | 90 | function removePiece(sq) { 91 | let piece = gameBoard.pieces[sq]; 92 | hashPiece(sq, piece); 93 | gameBoard.pieces[sq] = Pieces.empty; 94 | gameBoard.material[PieceColor[piece]] -= PieceValue[piece]; 95 | 96 | for (let i = 0; i < gameBoard.pieceCount[piece]; ++i) { 97 | if (gameBoard.pieceList[piece][i] === sq) { 98 | let lastIndex = gameBoard.pieceCount[piece] - 1; 99 | gameBoard.pieceList[piece][i] = gameBoard.pieceList[piece][lastIndex]; 100 | gameBoard.pieceList[piece].pop(); 101 | break; 102 | } 103 | } 104 | gameBoard.pieceCount[piece]--; 105 | if (PieceType[piece] == 'p') { 106 | PawnBitBoard.clearBit(PieceColor[piece], Sq120To64[sq]) 107 | } 108 | } 109 | 110 | 111 | function doMove(move) { 112 | if (!move) { 113 | return; 114 | } 115 | const from = moveFrom(move); 116 | const to = moveTo(move); 117 | const side = gameBoard.side; 118 | const piece = gameBoard.pieces[from]; 119 | gameBoard.history.push({ 120 | fiftyMove: gameBoard.fiftyMove, 121 | positionKey: gameBoard.positionKey, 122 | enPassantSq: gameBoard.enPassantSq, 123 | castlePermission: gameBoard.castlePermission, 124 | checkSq: gameBoard.checkSq, 125 | move, 126 | }); 127 | 128 | if (move & EnPassantFlag) { 129 | if (side === Color.white) { 130 | removePiece(to - 10); 131 | } else { 132 | removePiece(to + 10); 133 | } 134 | } 135 | else if (move & CastleFlag) { 136 | switch (to) { 137 | case Squares.g1: movePiece(Squares.h1, Squares.f1); break; 138 | case Squares.c1: movePiece(Squares.a1, Squares.d1); break; 139 | 140 | case Squares.g8: movePiece(Squares.h8, Squares.f8); break; 141 | case Squares.c8: movePiece(Squares.a8, Squares.d8); break; 142 | 143 | default: break; 144 | } 145 | } 146 | //hash out 147 | if (gameBoard.enPassantSq !== Squares.noSq) { 148 | hashEnPassant(); 149 | } 150 | hashCastle(); 151 | 152 | gameBoard.castlePermission &= CastlePermission[from]; 153 | gameBoard.castlePermission &= CastlePermission[to]; 154 | gameBoard.enPassantSq = Squares.noSq; 155 | 156 | //hash in 157 | hashCastle(); 158 | 159 | gameBoard.fiftyMove++; 160 | 161 | if (PieceType[piece] === 'p') { 162 | gameBoard.fiftyMove = 0; 163 | if (move & PawnStartFlag) { 164 | if (side === Color.white) { 165 | gameBoard.enPassantSq = from + 10; 166 | } 167 | else { 168 | gameBoard.enPassantSq = from - 10; 169 | } 170 | hashEnPassant(); 171 | } 172 | } 173 | 174 | if (move & CaptureFlag) { 175 | gameBoard.fiftyMove = 0; 176 | removePiece(to); 177 | } 178 | movePiece(from, to); 179 | 180 | if (move & PromotionFlag) { 181 | removePiece(to); 182 | addPiece(to, movePromotionPiece(move)); 183 | } 184 | 185 | gameBoard.side ^= 1; 186 | hashSide(); 187 | 188 | let kingOnSq = gameBoard.pieceList[Kings[side]][0]; 189 | let enemyKingOnSq = gameBoard.pieceList[Kings[gameBoard.side]][0]; 190 | 191 | if (isUnderAttack(kingOnSq, gameBoard.side)) { 192 | undoMove(); 193 | return false; 194 | } 195 | 196 | if (isUnderAttack(enemyKingOnSq, side)) { 197 | gameBoard.checkSq = enemyKingOnSq; 198 | } 199 | else { 200 | gameBoard.checkSq = Squares.noSq; 201 | } 202 | return true; 203 | } 204 | 205 | function undoMove() { 206 | if (gameBoard.history.length === 0) { 207 | return null; 208 | } 209 | 210 | const { 211 | fiftyMove, 212 | enPassantSq, 213 | castlePermission, 214 | checkSq, 215 | move, 216 | } = gameBoard.history.pop(); 217 | 218 | if (gameBoard.enPassantSq != Squares.noSq) { 219 | hashEnPassant(); 220 | } 221 | hashCastle(); 222 | 223 | gameBoard.fiftyMove = fiftyMove; 224 | gameBoard.enPassantSq = enPassantSq; 225 | gameBoard.castlePermission = castlePermission; 226 | gameBoard.checkSq = checkSq; 227 | 228 | if (gameBoard.enPassantSq != Squares.noSq) { 229 | hashEnPassant(); 230 | } 231 | hashCastle(); 232 | 233 | gameBoard.side ^= 1; 234 | hashSide(); 235 | 236 | const from = moveFrom(move); 237 | const to = moveTo(move); 238 | 239 | if (move & EnPassantFlag) { 240 | if (gameBoard.side === Color.white) { 241 | addPiece(to - 10, Pieces.bp); 242 | } 243 | else { 244 | addPiece(to + 10, Pieces.wp); 245 | } 246 | } 247 | else if (move & CastleFlag) { 248 | switch (to) { 249 | case Squares.g1: movePiece(Squares.f1, Squares.h1); break; 250 | case Squares.c1: movePiece(Squares.d1, Squares.a1); break; 251 | 252 | case Squares.g8: movePiece(Squares.f8, Squares.h8); break; 253 | case Squares.c8: movePiece(Squares.d8, Squares.a8); break; 254 | 255 | default: break; 256 | } 257 | } 258 | 259 | 260 | movePiece(to, from); 261 | if (move & CaptureFlag) { 262 | addPiece(to, moveCapturePiece(move)); 263 | } 264 | if (move & PromotionFlag) { 265 | removePiece(from); 266 | addPiece(from, PieceColor[movePromotionPiece(move)] === Color.white ? Pieces.wp : Pieces.bp); 267 | } 268 | 269 | return move; 270 | } 271 | 272 | 273 | // NULL MOVE 274 | function doNullMove() { 275 | if (gameBoard.checkSq != Squares.noSq) return; 276 | 277 | gameBoard.history.push({ 278 | fiftyMove: gameBoard.fiftyMove, 279 | positionKey: gameBoard.positionKey, 280 | enPassantSq: gameBoard.enPassantSq, 281 | castlePermission: gameBoard.castlePermission, 282 | checkSq: gameBoard.checkSq, 283 | move: null, 284 | }); 285 | 286 | gameBoard.enPassantSq = Squares.noSq; 287 | 288 | gameBoard.side ^= 1; 289 | hashSide(); 290 | } 291 | 292 | function undoNullMove() { 293 | if (gameBoard.history.length === 0) { 294 | return null; 295 | } 296 | 297 | const { 298 | fiftyMove, 299 | enPassantSq, 300 | castlePermission, 301 | checkSq, 302 | } = gameBoard.history.pop(); 303 | 304 | if (gameBoard.enPassantSq != Squares.noSq) { 305 | hashEnPassant(); 306 | } 307 | 308 | gameBoard.castlePermission = castlePermission; 309 | gameBoard.fiftyMove = fiftyMove; 310 | gameBoard.enPassantSq = enPassantSq; 311 | gameBoard.checkSq = checkSq; 312 | 313 | if (gameBoard.enPassantSq != Squares.noSq) { 314 | hashEnPassant(); 315 | } 316 | gameBoard.side ^= 1; 317 | hashSide(); 318 | 319 | return null; 320 | } 321 | 322 | function parseMove(fromSq, toSq, movelist) { 323 | fromSq = Squares[fromSq]; 324 | toSq = Squares[toSq]; 325 | if (!movelist) { 326 | movelist = generateMoves(); 327 | } 328 | 329 | 330 | let found = null; 331 | for (const { move } of movelist) { 332 | if (moveFrom(move) === fromSq && moveTo(move) === toSq) { 333 | found = move; 334 | break; 335 | } 336 | } 337 | 338 | if (found) { 339 | if (!doMove(found)) { 340 | IllegalSound.play(); 341 | return null; 342 | } 343 | undoMove(); 344 | } 345 | 346 | return found; 347 | } 348 | -------------------------------------------------------------------------------- /js/perft.js: -------------------------------------------------------------------------------- 1 | function perft(depth) { 2 | if (depth <= 0) { 3 | return 1; 4 | } 5 | let count = 0; 6 | for (const { move } of generateMoves()) { 7 | if (doMove(move) == false) { 8 | continue; 9 | } 10 | count += perft(depth - 1); 11 | undoMove(); 12 | } 13 | return count; 14 | } 15 | 16 | function perftTest(depth) { 17 | let moveCount = 0; 18 | let totalNodeSeared = 0; 19 | for (const { move } of generateMoves()) { 20 | if (doMove(move) == false) { 21 | continue; 22 | } 23 | 24 | let count = perft(depth - 1); 25 | undoMove(); 26 | 27 | let test = TestCases[gameBoard.positionKey.toString(16)][depth][moveStr(move)] == count ? '✅' : '❌'; 28 | if(!test) test = ''; 29 | 30 | totalNodeSeared += count; 31 | console.log(++moveCount, moveStr(move), count, test); 32 | } 33 | console.log('Total Node Seared', totalNodeSeared); 34 | } 35 | 36 | 37 | 38 | 39 | 40 | const TestCases = { 41 | '5300d2e1': { 42 | 1: { 43 | 'a2a3': 1, 44 | 'a2a4': 1, 45 | 'b2b3': 1, 46 | 'b2b4': 1, 47 | 'c2c3': 1, 48 | 'c2c4': 1, 49 | 'd2d3': 1, 50 | 'd2d4': 1, 51 | 'e2e3': 1, 52 | 'e2e4': 1, 53 | 'f2f3': 1, 54 | 'f2f4': 1, 55 | 'g2g3': 1, 56 | 'g2g4': 1, 57 | 'h2h3': 1, 58 | 'h2h4': 1, 59 | 'b1a3': 1, 60 | 'b1c3': 1, 61 | 'g1f3': 1, 62 | 'g1h3': 1, 63 | }, 64 | 2: { 65 | 'a2a3': 20, 66 | 'a2a4': 20, 67 | 'b2b3': 20, 68 | 'b2b4': 20, 69 | 'c2c3': 20, 70 | 'c2c4': 20, 71 | 'd2d3': 20, 72 | 'd2d4': 20, 73 | 'e2e3': 20, 74 | 'e2e4': 20, 75 | 'f2f3': 20, 76 | 'f2f4': 20, 77 | 'g2g3': 20, 78 | 'g2g4': 20, 79 | 'h2h3': 20, 80 | 'h2h4': 20, 81 | 'b1a3': 20, 82 | 'b1c3': 20, 83 | 'g1f3': 20, 84 | 'g1h3': 20, 85 | }, 86 | 3: { 87 | 'a2a3': 380, 88 | 'a2a4': 420, 89 | 'b2b3': 420, 90 | 'b2b4': 421, 91 | 'c2c3': 420, 92 | 'c2c4': 441, 93 | 'd2d3': 539, 94 | 'd2d4': 560, 95 | 'e2e3': 599, 96 | 'e2e4': 600, 97 | 'f2f3': 380, 98 | 'f2f4': 401, 99 | 'g2g3': 420, 100 | 'g2g4': 421, 101 | 'h2h3': 380, 102 | 'h2h4': 420, 103 | 'b1a3': 400, 104 | 'b1c3': 440, 105 | 'g1f3': 440, 106 | 'g1h3': 400, 107 | }, 108 | 4: { 109 | 'a2a3': 8457, 110 | 'a2a4': 9329, 111 | 'b2b3': 9345, 112 | 'b2b4': 9332, 113 | 'c2c3': 9272, 114 | 'c2c4': 9744, 115 | 'd2d3': 11959, 116 | 'd2d4': 12435, 117 | 'e2e3': 13134, 118 | 'e2e4': 13160, 119 | 'f2f3': 8457, 120 | 'f2f4': 8929, 121 | 'g2g3': 9345, 122 | 'g2g4': 9328, 123 | 'h2h3': 8457, 124 | 'h2h4': 9329, 125 | 'b1a3': 8885, 126 | 'b1c3': 9755, 127 | 'g1f3': 9748, 128 | 'g1h3': 8881, 129 | 130 | }, 131 | 5: { 132 | 'a2a3': 181046, 133 | 'a2a4': 217832, 134 | 'b2b3': 215255, 135 | 'b2b4': 216145, 136 | 'c2c3': 222861, 137 | 'c2c4': 240082, 138 | 'd2d3': 328511, 139 | 'd2d4': 361790, 140 | 'e2e3': 402988, 141 | 'e2e4': 405385, 142 | 'f2f3': 178889, 143 | 'f2f4': 198473, 144 | 'g2g3': 217210, 145 | 'g2g4': 214048, 146 | 'h2h3': 181044, 147 | 'h2h4': 218829, 148 | 'b1a3': 198572, 149 | 'b1c3': 234656, 150 | 'g1f3': 233491, 151 | 'g1h3': 198502, 152 | }, 153 | 6: { 154 | 'a2a3': 4463267, 155 | 'a2a4': 5363555, 156 | 'b2b3': 5310358, 157 | 'b2b4': 5293555, 158 | 'c2c3': 5417640, 159 | 'c2c4': 5866666, 160 | 'd2d3': 8073082, 161 | 'd2d4': 8879566, 162 | 'e2e3': 9726018, 163 | 'e2e4': 9771632, 164 | 'f2f3': 4404141, 165 | 'f2f4': 4890429, 166 | 'g2g3': 5346260, 167 | 'g2g4': 5239875, 168 | 'h2h3': 4463070, 169 | 'h2h4': 5385554, 170 | 'b1a3': 4856835, 171 | 'b1c3': 5708064, 172 | 'g1f3': 5723523, 173 | 'g1h3': 4877234, 174 | }, 175 | }, 176 | 177 | '6839c89f': { 178 | 1: { 179 | a2a3: 1, 180 | b2b3: 1, 181 | g2g3: 1, 182 | d5d6: 1, 183 | a2a4: 1, 184 | g2g4: 1, 185 | g2h3: 1, 186 | d5e6: 1, 187 | c3b1: 1, 188 | c3d1: 1, 189 | c3a4: 1, 190 | c3b5: 1, 191 | e5d3: 1, 192 | e5c4: 1, 193 | e5g4: 1, 194 | e5c6: 1, 195 | e5g6: 1, 196 | e5d7: 1, 197 | e5f7: 1, 198 | d2c1: 1, 199 | d2e3: 1, 200 | d2f4: 1, 201 | d2g5: 1, 202 | d2h6: 1, 203 | e2d1: 1, 204 | e2f1: 1, 205 | e2d3: 1, 206 | e2c4: 1, 207 | e2b5: 1, 208 | e2a6: 1, 209 | a1b1: 1, 210 | a1c1: 1, 211 | a1d1: 1, 212 | h1f1: 1, 213 | h1g1: 1, 214 | f3d3: 1, 215 | f3e3: 1, 216 | f3g3: 1, 217 | f3h3: 1, 218 | f3f4: 1, 219 | f3g4: 1, 220 | f3f5: 1, 221 | f3h5: 1, 222 | f3f6: 1, 223 | e1d1: 1, 224 | e1f1: 1, 225 | e1g1: 1, 226 | e1c1: 1, 227 | }, 228 | 2: { 229 | a2a3: 44, 230 | b2b3: 42, 231 | g2g3: 42, 232 | d5d6: 41, 233 | a2a4: 44, 234 | g2g4: 42, 235 | g2h3: 43, 236 | d5e6: 46, 237 | c3b1: 42, 238 | c3d1: 42, 239 | c3a4: 42, 240 | c3b5: 39, 241 | e5d3: 43, 242 | e5c4: 42, 243 | e5g4: 44, 244 | e5c6: 41, 245 | e5g6: 42, 246 | e5d7: 45, 247 | e5f7: 44, 248 | d2c1: 43, 249 | d2e3: 43, 250 | d2f4: 43, 251 | d2g5: 42, 252 | d2h6: 41, 253 | e2d1: 44, 254 | e2f1: 44, 255 | e2d3: 42, 256 | e2c4: 41, 257 | e2b5: 39, 258 | e2a6: 36, 259 | a1b1: 43, 260 | a1c1: 43, 261 | a1d1: 43, 262 | h1f1: 43, 263 | h1g1: 43, 264 | f3d3: 42, 265 | f3e3: 43, 266 | f3g3: 43, 267 | f3h3: 43, 268 | f3f4: 43, 269 | f3g4: 43, 270 | f3f5: 45, 271 | f3h5: 43, 272 | f3f6: 39, 273 | e1d1: 43, 274 | e1f1: 43, 275 | e1g1: 43, 276 | e1c1: 43, 277 | }, 278 | 3: { 279 | a2a3: 2186, 280 | b2b3: 1964, 281 | g2g3: 1882, 282 | d5d6: 1991, 283 | a2a4: 2149, 284 | g2g4: 1843, 285 | g2h3: 1970, 286 | d5e6: 2241, 287 | c3b1: 2038, 288 | c3d1: 2040, 289 | c3a4: 2203, 290 | c3b5: 2138, 291 | e5d3: 1803, 292 | e5c4: 1880, 293 | e5g4: 1878, 294 | e5c6: 2027, 295 | e5g6: 1997, 296 | e5d7: 2124, 297 | e5f7: 2080, 298 | d2c1: 1963, 299 | d2e3: 2136, 300 | d2f4: 2000, 301 | d2g5: 2134, 302 | d2h6: 2019, 303 | e2d1: 1733, 304 | e2f1: 2060, 305 | e2d3: 2050, 306 | e2c4: 2082, 307 | e2b5: 2057, 308 | e2a6: 1907, 309 | a1b1: 1969, 310 | a1c1: 1968, 311 | a1d1: 1885, 312 | h1f1: 1929, 313 | h1g1: 2013, 314 | f3d3: 2005, 315 | f3e3: 2174, 316 | f3g3: 2214, 317 | f3h3: 2360, 318 | f3f4: 2132, 319 | f3g4: 2169, 320 | f3f5: 2396, 321 | f3h5: 2267, 322 | f3f6: 2111, 323 | e1d1: 1894, 324 | e1f1: 1855, 325 | e1g1: 2059, 326 | e1c1: 1887, 327 | }, 328 | 4: { 329 | a2a3: 94405, 330 | b2b3: 81066, 331 | g2g3: 77468, 332 | d5d6: 79551, 333 | a2a4: 90978, 334 | g2g4: 75677, 335 | g2h3: 82759, 336 | d5e6: 97464, 337 | c3b1: 84773, 338 | c3d1: 84782, 339 | c3a4: 91447, 340 | c3b5: 81498, 341 | e5d3: 77431, 342 | e5c4: 77752, 343 | e5g4: 79912, 344 | e5c6: 83885, 345 | e5g6: 83866, 346 | e5d7: 93913, 347 | e5f7: 88799, 348 | d2c1: 83037, 349 | d2e3: 90274, 350 | d2f4: 84869, 351 | d2g5: 87951, 352 | d2h6: 82323, 353 | e2d1: 74963, 354 | e2f1: 88728, 355 | e2d3: 85119, 356 | e2c4: 84835, 357 | e2b5: 79739, 358 | e2a6: 69334, 359 | a1b1: 83348, 360 | a1c1: 83263, 361 | a1d1: 79695, 362 | h1f1: 81563, 363 | h1g1: 84876, 364 | f3d3: 83727, 365 | f3e3: 92505, 366 | f3g3: 94461, 367 | f3h3: 98524, 368 | f3f4: 90488, 369 | f3g4: 92037, 370 | f3f5: 104992, 371 | f3h5: 95034, 372 | f3f6: 77838, 373 | e1d1: 79989, 374 | e1f1: 77887, 375 | e1g1: 86975, 376 | e1c1: 79803, 377 | }, 378 | 5: { 379 | a2a3: 4627439, 380 | b2b3: 3768824, 381 | g2g3: 3472039, 382 | d5d6: 3835265, 383 | a2a4: 4387586, 384 | g2g4: 3338154, 385 | g2h3: 3819456, 386 | d5e6: 4727437, 387 | c3b1: 3996171, 388 | c3d1: 3995761, 389 | c3a4: 4628497, 390 | c3b5: 4317482, 391 | e5d3: 3288812, 392 | e5c4: 3494887, 393 | e5g4: 3415992, 394 | e5c6: 4083458, 395 | e5g6: 3949417, 396 | e5d7: 4404043, 397 | e5f7: 4164923, 398 | d2c1: 3793390, 399 | d2e3: 4407041, 400 | d2f4: 3941257, 401 | d2g5: 4370915, 402 | d2h6: 3967365, 403 | e2d1: 3074219, 404 | e2f1: 4095479, 405 | e2d3: 4066966, 406 | e2c4: 4182989, 407 | e2b5: 4032348, 408 | e2a6: 3553501, 409 | a1b1: 3827454, 410 | a1c1: 3814203, 411 | a1d1: 3568344, 412 | h1f1: 3685756, 413 | h1g1: 3989454, 414 | f3d3: 3949570, 415 | f3e3: 4477772, 416 | f3g3: 4669768, 417 | f3h3: 5067173, 418 | f3f4: 4327936, 419 | f3g4: 4514010, 420 | f3f5: 5271134, 421 | f3h5: 4743335, 422 | f3f6: 3975992, 423 | e1d1: 3559113, 424 | e1f1: 3377351, 425 | e1g1: 4119629, 426 | e1c1: 3551583, 427 | } 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /js/pgnCompiler.js: -------------------------------------------------------------------------------- 1 | 2 | // Example usage 3 | // let pgn = [ 4 | // '1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7 11. Nbd2 Bb7 12. Bc2 Re8 13. Nf1 Bf8 14. Ng3 g6 15. Bg5 h6 16. Bd2 Bg7 17. Qc1 Kh7 18. Nh2 c5 19. d5 c4 20. Ng4 Nxg4 21. hxg4 Nc5 22. f3 a5 23. Kf2 b4 24. Rh1 Qb6 25. Be3 Qc7 26. Bxh6 Bxh6 27. Qxh6+ Kg8 28. Qh8#', 5 | // '1. b3 d5 2. Nf3 Nf6 3. Bb2 Bf5 4. Nh4 Bd7 5. e3 c5 6. c4 e6 7. Be2 Be7 8. O-O O-O 9. Nf3 Nc6 10. d4 cxd4 11. exd4 Rc8 12. Nbd2 dxc4 13. bxc4 Re8 14. Nb3 Bf8 15. Qd2 Ne7 16. Ne5 g6 17. Bf3 b6 18. Rfe1 Bg7 19. Rad1 b5 20. Nxd7 Nxd7 21. cxb5 Nb6 22. Na5 Nc4 23. Nxc4 Rxc4 24. Qd3 Rc8 25. a4 Qa5 26. Qb3 Red8 27. g3 Nd5 28. Rc1 Nb6 29. d5 Bxb2 30. Qxb2 exd5 31. Qf6 Qxa4 32. Rc7 Rf8 33. Rxc8 Rxc8 34. Re7 Rf8 35. h4 h5 36. Kg2 Qc2 37. g4 hxg4 38. Bxg4 d4 39. Be6 Qe4+ 40. Kg3 Qxe6 41. Rxe6 fxe6 42. Qxg6+ Kh8 43. Qh6+ Kg8 44. Qxe6+ Kg7 45. Qe5+ Rf6 46. Qxd4 Kf7 47. h5 Nc8 48. Kg4 Ne7 49. f4 Rb6 50. Qc4+ Kg7 51. Qc5 Ng8 52. Kg5 Nh6 53. Qe5+ Kf8 54. Qh8+ Ng8 55. h6 Rxh6 56. Qxh6+ Nxh6 57. Kxh6 Ke7 58. Kg7 Ke6 59. Kg6 1-0', 6 | // ]; 7 | // setTimeout(() => { 8 | // const result = parsePGN(pgn[1]); 9 | // }, 200); 10 | 11 | function parsePGN(pgn) { 12 | engine.stop(); 13 | 14 | let result = []; 15 | const moves = extractMoves(pgn); 16 | console.log(moves); 17 | newGame(); 18 | for (const move of moves) { 19 | const match = isValidPgnMove(move); 20 | if (!match) { 21 | throw new Error(`${move} is a invalid pgn move`); 22 | } 23 | 24 | let { piece, fromFile, fromRank, toSq, capture } = match; 25 | let fromSq; 26 | 27 | if (move[0].toUpperCase() == 'O') { 28 | ({ fromSq, toSq } = getCastleSq(move)); 29 | } 30 | else if (piece == 'P') { 31 | fromSq = getPawnFromSq(match); 32 | } 33 | else { 34 | piece = Pieces[SideChar[gameBoard.side] + piece.toLowerCase()]; 35 | toSq = Squares[toSq]; 36 | if (fromFile !== '') fromFile = fromFile.charCodeAt(0) - 'a'.charCodeAt(0); 37 | if (fromRank !== '') fromRank -= 1; 38 | 39 | let moves = generateMoves().filter(({ move }) => { 40 | return ( 41 | gameBoard.pieces[moveFrom(move)] == piece && 42 | moveTo(move) == toSq 43 | ) 44 | }); 45 | 46 | //filtering illegal move 47 | moves = moves.filter(({ move }) => { 48 | if (!doMove(move)) return false; 49 | undoMove(); 50 | return true; 51 | }) 52 | if (moves.length == 1) { 53 | gui.doMove(moves[0].move, { audio: false }); 54 | } 55 | else { 56 | moves = moves.filter(({ move }) => { 57 | return fileOf(moveFrom(move)) === fromFile || rankOf(moveFrom(move)) === fromRank 58 | }) 59 | if (moves.length !== 1) { 60 | throw new Error(`${move} is a Illigal move`); 61 | } 62 | gui.doMove(moves[0].move, { audio: false }); 63 | } 64 | continue; 65 | } 66 | 67 | const parsedMove = parseMove(fromSq, toSq); 68 | if (!parsedMove) { 69 | throw new Error(`${move} is a Illigal move`); 70 | } 71 | // console.log(fromSq, toSq, parsedMove); 72 | gui.doMove(parsedMove, { audio: false }); 73 | } 74 | } 75 | 76 | function isValidPgnMove(move) { 77 | const movePattern = /^(O-O-O|O-O|([NBKRQ]?)([a-h]?)([1-8]?)(x?)([a-h][1-8])(=[NBKRQ])?)[+#]?$/; 78 | const match = move.match(movePattern); 79 | if (!match) return null; 80 | return { 81 | piece: match[2] || 'P', 82 | fromFile: match[3] || '', 83 | fromRank: match[4] || '', 84 | capture: match[5] === 'x', 85 | toSq: match[6] 86 | } 87 | } 88 | 89 | function getCastleSq(move) { 90 | let fromSq, toSq; 91 | if (gameBoard.side == Color.white) { 92 | fromSq = 'e1'; 93 | toSq = (move.toLowerCase() == 'o-o') ? 'g1' : 'c1'; 94 | } 95 | else { 96 | fromSq = 'e8'; 97 | toSq = (move.toLowerCase() == 'o-o') ? 'g8' : 'c8'; 98 | } 99 | return { fromSq, toSq }; 100 | } 101 | 102 | function getPawnFromSq({ fromFile, toSq, capture }) { 103 | toSq = Squares[toSq]; 104 | 105 | if (gameBoard.side == Color.white) { 106 | if (capture) return fromFile + (rankOf(toSq - 10) + 1); 107 | if (gameBoard.pieces[toSq - 10] != Pieces.empty) { 108 | return SquaresChar[toSq - 10]; 109 | } 110 | return SquaresChar[toSq - 20]; 111 | } 112 | else { 113 | if (capture) return fromFile + (rankOf(toSq + 10) + 1); 114 | if (gameBoard.pieces[toSq + 10] != Pieces.empty) { 115 | return SquaresChar[toSq + 10]; 116 | } 117 | return SquaresChar[toSq + 20]; 118 | } 119 | } 120 | 121 | 122 | 123 | 124 | function getPGN() { 125 | let pgn = ''; 126 | let ply = 0; 127 | for(let i = 0; i<=currMoveNode; ++i){ 128 | const node = nodeList[i]; 129 | if (i % 2 == 0) { 130 | pgn += ++ply + '. '; 131 | } 132 | pgn += node.textContent; 133 | pgn += ' '; 134 | } 135 | if (isGameOver()) { 136 | let result = resultTitle.textContent; 137 | switch (result) { 138 | case 'Draw': pgn += '1/2-1/2'; break; 139 | case 'White Won': pgn += '1-0'; break; 140 | case 'Black Won': pgn += '0-1'; break; 141 | } 142 | } 143 | return pgn; 144 | } 145 | 146 | function extractMoves(pgn) { 147 | const lines = pgn.split('\n'); 148 | let movesStart = false; 149 | let moves = []; 150 | 151 | for (let line of lines) { 152 | // If we encounter a line without square brackets, it indicates the start of moves 153 | if (!movesStart && !line.startsWith('[') && line.trim() !== '') { 154 | movesStart = true; 155 | } 156 | 157 | // If movesStart is true, we process the line for moves 158 | if (movesStart) { 159 | // Remove comments and metadata enclosed in {}, and also strip any evaluation comments 160 | line = line.replace(/\{[^}]*\}|\([^\)]*\)/g, '').trim(); 161 | 162 | const parts = line.split(/\s+/); 163 | 164 | for (let part of parts) { 165 | // Remove invalid moves (like move numbers, result, and annotations) 166 | if (!part.match(/^(\d+\.$|\d+\.\.\.$|1-0|0-1|1\/2-1\/2|\*|[\(\)]|\?\?|\!\!|\?|\!)/)) { 167 | moves.push(part); 168 | } 169 | } 170 | 171 | // Stop processing when we encounter the result of the game 172 | if (line.includes('1-0') || line.includes('0-1') || line.includes('1/2-1/2') || line.includes('*')) { 173 | break; 174 | } 175 | } 176 | } 177 | moves = moves.map(move=>move.replace(/[?()\s]+/g, '')); 178 | return moves; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /js/pvTable.js: -------------------------------------------------------------------------------- 1 | const PvTable = { 2 | entries: {}, 3 | }; 4 | 5 | PvTable.addMove = function (move) { 6 | this.entries[gameBoard.positionKey] = move; 7 | } 8 | PvTable.getMove = function () { 9 | let index = gameBoard.positionKey % transpositionTable.maxEntries; 10 | if(transpositionTable.entries[index]?.positionKey !== gameBoard.positionKey) return null; 11 | return transpositionTable.entries[index].move; 12 | } 13 | PvTable.clear = function () { 14 | this.entries = {}; 15 | } 16 | PvTable.length = function () { 17 | return Object.keys(this.entries).length; 18 | } 19 | PvTable.getLine = function (depth) { 20 | let move = this.getMove(); 21 | let movelist = []; 22 | let count = 0; 23 | 24 | while (move && count < depth) { 25 | if (isMoveExists(move)) { 26 | doMove(move); 27 | movelist.push(move); 28 | count++; 29 | } 30 | else { 31 | break; 32 | } 33 | move = this.getMove(); 34 | } 35 | while (count--) undoMove(); 36 | return movelist.map(move => moveStr(move)).join(' '); 37 | } 38 | 39 | function isMoveExists(arg) { 40 | for (const { move } of generateMoves()) { 41 | if (move == arg) { 42 | if (!doMove(move)) continue; 43 | undoMove(); 44 | return true; 45 | } 46 | } 47 | return false; 48 | } 49 | 50 | const transpositionTable = { 51 | maxEntries: 0x100000 * 16, 52 | entries: new Array(this.maxEntries), 53 | } 54 | const AlphaFlag = 0; 55 | const BetaFlag = 1; 56 | const ExactFlag = 2; 57 | 58 | transpositionTable.clear = function(){ 59 | this.entries = new Array(this.maxEntries); 60 | } 61 | 62 | transpositionTable.add = function (positionKey, move, score, flag, depth) { 63 | if (score > Mate) score += searchController.ply; 64 | if (score < -Mate) score -= searchController.ply; 65 | let index = positionKey % this.maxEntries; 66 | 67 | this.entries[index] = { 68 | positionKey, 69 | move, 70 | score, 71 | depth, 72 | flag 73 | } 74 | } 75 | transpositionTable.get = function (positionKey) { 76 | let index = positionKey % this.maxEntries; 77 | if(this.entries[index]?.positionKey !== positionKey) return null; 78 | return this.entries[index]; 79 | } 80 | transpositionTable.length = function(){ 81 | return Object.keys(this.entries).length; 82 | } 83 | 84 | 85 | // D:1 Best:d2d4 Score:30 nodes:21 Pv: d2d4 86 | // D:2 Best:d2d4 Score:0 nodes:207 Pv: d2d4 d7d5 87 | // D:3 Best:d2d4 Score:25 nodes:2680 Pv: d2d4 d7d5 c1e3 88 | // D:4 Best:d2d4 Score:0 nodes:17507 Pv: d2d4 d7d5 c1e3 c8e6 89 | // D:5 Best:e2e4 Score:25 nodes:217789 Pv: e2e4 e7e5 d2d4 d7d5 c1e3 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /js/search.js: -------------------------------------------------------------------------------- 1 | function getMoveFromBook() { 2 | const key = getPolyKey(); 3 | // no move present for current position 4 | if (!openingBook[key]) return false; 5 | 6 | // pick a random move from movelist 7 | const moves = openingBook[key]; 8 | let move = moves[Math.floor(Math.random() * moves.length)].move; 9 | move = extractPolyMove(move); 10 | if(!move) return false; 11 | 12 | searchController.thinking = false; 13 | searchController.bestMove = move | FromBookFlag; 14 | searchController.bestScore = 0; 15 | searchController.depthReached = 0; 16 | return true; 17 | } 18 | 19 | const searchController = {}; 20 | searchController.depthReached; 21 | searchController.nodes; 22 | searchController.fh; 23 | searchController.fhf; 24 | searchController.depth; 25 | searchController.time; 26 | searchController.start; 27 | searchController.stop; 28 | searchController.bestMove; 29 | searchController.bestScore; 30 | searchController.thinking; 31 | searchController.ply; 32 | searchController.killers = new Array(MaxDepth); //killer[ply][0/1]; 33 | searchController.history = new Array(13); // 34 | 35 | 36 | searchController.clear = function () { 37 | this.killers = Array.from({ length: MaxDepth }, () => [null, null]); 38 | this.history = Array.from({ length: 13 }, () => Array(120).fill(0)); 39 | this.ply = 0; 40 | this.depthReached = 0; 41 | this.nodes = 0; 42 | this.fh = 0; 43 | this.fhf = 0; 44 | this.start = Date.now(); 45 | this.stop = false; 46 | } 47 | 48 | searchController.clear(); 49 | 50 | function searchPosition(thinkingTime = 2) { 51 | let bestMove = null; 52 | let bestScore = -Infinite; 53 | let depth = 1; 54 | let ordering = 0; 55 | 56 | searchController.clear(); 57 | PvTable.clear(); 58 | 59 | if (getMoveFromBook()) return; 60 | 61 | searchController.depth = MaxDepth; 62 | searchController.start = Date.now(); 63 | searchController.time = thinkingTime * 1000; 64 | 65 | for (depth = 1; depth <= searchController.depth; ++depth) { 66 | bestScore = alphaBeta(-Infinite, Infinite, depth); 67 | 68 | if (searchController.stop) break; 69 | 70 | bestMove = PvTable.getMove(); 71 | 72 | if (depth != 1 && searchController.fh) { 73 | ordering = ((searchController.fhf / searchController.fh) * 100).toFixed(2); 74 | } 75 | if(ordering == 'NaN'){ 76 | console.log(searchController.fh, searchController.fhf); 77 | } 78 | 79 | self.postMessage({ 80 | command: 'searching', 81 | depth, 82 | pvLine: PvTable.getLine(depth), 83 | bestMove, 84 | bestScore, 85 | nodes: searchController.nodes, 86 | ordering, 87 | }) 88 | searchController.bestMove = bestMove; 89 | searchController.bestScore = bestScore; 90 | searchController.depthReached = depth; 91 | } 92 | 93 | searchController.thinking = false; 94 | } 95 | 96 | 97 | 98 | 99 | function alphaBeta(alpha, beta, depth, { doNull = true } = {}) { 100 | if (depth <= 0) { 101 | return quiescence(alpha, beta); 102 | } 103 | 104 | if ((searchController.nodes % 2048) == 0) { 105 | checkTimeUp(); 106 | } 107 | 108 | searchController.nodes++; 109 | 110 | if ((isRepetition() || gameBoard.fiftyMove >= 100) && searchController.ply != 0) { 111 | return 0; 112 | } 113 | 114 | if (searchController.ply >= MaxDepth) { 115 | return evalPosition(); 116 | } 117 | 118 | let inCheck = gameBoard.checkSq != Squares.noSq; 119 | if (inCheck) { 120 | ++depth; 121 | } 122 | 123 | let score = -Infinite; 124 | 125 | const ttEntry = transpositionTable.get(gameBoard.positionKey); 126 | let pvMove = ttEntry?.move; 127 | if (ttEntry) { 128 | if (ttEntry.depth >= depth) { 129 | score = ttEntry.score; 130 | if(score > Mate) score -= searchController.ply; 131 | else if(score < -Mate) score += searchController.ply; 132 | if(ttEntry.flag === AlphaFlag && score <= alpha) return alpha; 133 | if(ttEntry.flag === BetaFlag && score >= beta) return beta; 134 | if(ttEntry.flag === ExactFlag) return score; 135 | } 136 | } 137 | 138 | //NULL Move Pruning 139 | if (doNull && !inCheck && searchController.ply && depth >= 4) { 140 | doNullMove(); 141 | score = -alphaBeta(-beta, -beta + 1, depth - 4, { doNull: false }); 142 | undoNullMove(); 143 | if (searchController.stop) return 0; 144 | if (score >= beta) { 145 | return beta; 146 | } 147 | } 148 | 149 | const moves = generateMoves(); 150 | let legalMove = 0; 151 | const prevAlpha = alpha; 152 | let bestMove = null; 153 | 154 | if (pvMove) { 155 | for (const moveObj of moves) { 156 | if (moveObj.move == pvMove) { 157 | moveObj.score = 2000000; 158 | break; 159 | } 160 | } 161 | } 162 | 163 | for (let i = 0; i < moves.length; ++i) { 164 | swapWithBest(i, moves); 165 | const move = moves[i].move; 166 | 167 | if (!doMove(move)) continue; 168 | legalMove++; 169 | searchController.ply++; 170 | score = -alphaBeta(-beta, -alpha, depth - 1); 171 | 172 | undoMove(); 173 | searchController.ply--; 174 | 175 | if (searchController.stop) return 0; 176 | 177 | if (score > alpha) { 178 | bestMove = move;; 179 | 180 | if (score >= beta) { 181 | if (legalMove === 1) searchController.fhf++; 182 | searchController.fh++; 183 | if (!(move & CaptureFlag)) { 184 | searchController.killers[searchController.ply][1] = searchController.killers[searchController.ply][0]; 185 | searchController.killers[searchController.ply][0] = move; 186 | } 187 | 188 | transpositionTable.add(gameBoard.positionKey, move, beta, BetaFlag, depth); 189 | return beta; 190 | } 191 | 192 | alpha = score; 193 | if (!(move & CaptureFlag)) { 194 | let piece = gameBoard.pieces[moveFrom(move)]; 195 | let toSq = moveTo(move); 196 | searchController.history[piece][toSq] += depth * depth; 197 | } 198 | } 199 | 200 | } 201 | 202 | if (legalMove == 0) { 203 | if (inCheck) { 204 | return -Infinite + searchController.ply; 205 | } else { 206 | return 0; 207 | } 208 | } 209 | 210 | if (alpha != prevAlpha) { 211 | transpositionTable.add(gameBoard.positionKey, bestMove, score, ExactFlag, depth); 212 | } 213 | else { 214 | transpositionTable.add(gameBoard.positionKey, bestMove, alpha, AlphaFlag, depth); 215 | } 216 | 217 | return alpha; 218 | } 219 | 220 | 221 | function quiescence(alpha, beta) { 222 | 223 | if ((searchController.nodes % 2048) == 0) { 224 | checkTimeUp(); 225 | } 226 | 227 | searchController.nodes++; 228 | 229 | if ((isRepetition() || gameBoard.fiftyMove >= 100) && searchController.ply != 0) { 230 | return 0; 231 | } 232 | 233 | if (searchController.ply >= MaxDepth) { 234 | return evalPosition(); 235 | } 236 | 237 | let score = evalPosition(); 238 | 239 | if (score >= beta) return beta; 240 | if (score > alpha) alpha = score; 241 | 242 | 243 | let legalMove = 0; 244 | let moves = generateCaptureMoves(); 245 | 246 | 247 | for (let i = 0; i < moves.length; ++i) { 248 | swapWithBest(i, moves); 249 | const move = moves[i].move; 250 | 251 | if (!doMove(move)) continue; 252 | legalMove++; 253 | searchController.ply++; 254 | 255 | score = -quiescence(-beta, -alpha); 256 | 257 | undoMove(); 258 | searchController.ply--; 259 | 260 | if (searchController.stop) return 0; 261 | 262 | if (score > alpha) { 263 | if (score >= beta) { 264 | if (legalMove == 1) { 265 | searchController.fhf++; 266 | } 267 | searchController.fh++; 268 | return beta; 269 | } 270 | alpha = score; 271 | } 272 | } 273 | 274 | 275 | return alpha; 276 | 277 | } 278 | 279 | function swapWithBest(i, moves) { 280 | let bestIndex = i; 281 | for (let j = i + 1; j < moves.length; ++j) { 282 | if (moves[j].score > moves[bestIndex].score) { 283 | bestIndex = j; 284 | } 285 | } 286 | if (bestIndex !== i) { 287 | [moves[i], moves[bestIndex]] = [moves[bestIndex], moves[i]]; 288 | } 289 | } 290 | 291 | 292 | function checkTimeUp() { 293 | if ((Date.now() - searchController.start) > searchController.time) { 294 | searchController.stop = true; 295 | } 296 | } 297 | 298 | function isRepetition() { 299 | let hisPly = gameBoard.history.length; 300 | for (let i = hisPly - gameBoard.fiftyMove; i < hisPly - 1; ++i) { 301 | if (gameBoard.positionKey == gameBoard.history[i].positionKey) { 302 | return true; 303 | } 304 | } 305 | 306 | return false; 307 | } 308 | 309 | -------------------------------------------------------------------------------- /js/searchWorker.js: -------------------------------------------------------------------------------- 1 | importScripts( 2 | 'defs.js', 3 | 'bitBoard.js', 4 | 'board.js', 5 | 'moveStructure.js', 6 | 'generateMove.js', 7 | 'evaluation.js', 8 | 'pvTable.js', 9 | 'search.js', 10 | 'polyglot.js', 11 | 'main.js', 12 | ); 13 | 14 | self.onmessage = function(e){ 15 | const {command, searchTime, board} = e.data; 16 | gameBoard = board; 17 | if(command == 'search'){ 18 | let depth = searchPosition(searchTime); 19 | self.postMessage({ 20 | command: 'searchFinished', 21 | bestMove: searchController.bestMove, 22 | bestScore: searchController.bestScore, 23 | depth : searchController.depthReached, 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /js/sounds.js: -------------------------------------------------------------------------------- 1 | const CaptureSound = new Audio('./assets/sounds/capture.mp3'); 2 | const CastleSound = new Audio('./assets/sounds/castle.mp3'); 3 | const GameEndSound = new Audio('./assets/sounds/game-end.mp3'); 4 | const GameStartSound = new Audio('./assets/sounds/game-start.mp3'); 5 | const IllegalSound = new Audio('./assets/sounds/illegal.mp3'); 6 | const CheckSound = new Audio('./assets/sounds/move-check.mp3'); 7 | const EnemyMoveSound = new Audio('./assets/sounds/move-opponent.mp3'); 8 | const SelfMoveSound = new Audio('./assets/sounds/move-self.mp3'); 9 | const notifySound = new Audio('./assets/sounds/notify.mp3'); 10 | const PremoveSound = new Audio('./assets/sounds/premove.mp3'); 11 | const PromoteSound = new Audio('./assets/sounds/promote.mp3'); 12 | const TenSecondsSound = new Audio('./assets/sounds/tenseconds.mp3'); 13 | --------------------------------------------------------------------------------