├── protochess-front ├── src │ ├── pages │ │ ├── wasmtest.js │ │ ├── _PlayButtons.svelte │ │ ├── _fallback.svelte │ │ ├── chess │ │ │ ├── _WebChat.svelte │ │ │ ├── [roomId].svelte │ │ │ └── _WebChess.svelte │ │ ├── about.svelte │ │ ├── _ProtochessInfo.svelte │ │ ├── faq.svelte │ │ ├── _layout.svelte │ │ └── singleplayer.svelte │ ├── components │ │ ├── MovementPatternDisplay │ │ │ ├── DisplayMode.js │ │ │ └── MovementPatternDisplay.svelte │ │ ├── Chess │ │ │ ├── ColorConstants.js │ │ │ ├── Tile.svelte │ │ │ └── Piece.svelte │ │ ├── MediaQuery.svelte │ │ ├── PlayersList │ │ │ └── PlayersList.svelte │ │ ├── RoomList │ │ │ └── RoomList.svelte │ │ ├── ChessEditor │ │ │ ├── PieceLeftControl.svelte │ │ │ ├── ChessLeftControl.svelte │ │ │ ├── ChessRightControl.svelte │ │ │ └── PieceRightControl.svelte │ │ ├── CreateRoomDialog │ │ │ └── CreateRoomDialog.svelte │ │ ├── Chat │ │ │ └── Chat.svelte │ │ ├── MovementPatternDisplayBar │ │ │ └── MovementPatternDisplayBar.svelte │ │ └── Modal.svelte │ ├── main.js │ └── App.svelte ├── scripts │ ├── now │ │ ├── .gitignore │ │ ├── now.json │ │ ├── package.json │ │ ├── build.js │ │ └── api │ │ │ └── ssr.js │ ├── netlify │ │ ├── .netlify │ │ │ └── state.json │ │ ├── .gitignore │ │ ├── api │ │ │ └── ssr │ │ │ │ ├── ssr.js │ │ │ │ └── package.json │ │ ├── netlify.toml │ │ ├── package.json │ │ └── build.js │ └── docker │ │ ├── docker-compose.yml │ │ ├── Dockerfile │ │ └── docker-build.sh ├── sandbox.config.json ├── static │ ├── favicon.png │ ├── click_sound.wav │ ├── protochess.png │ ├── images │ │ ├── zebra.png │ │ ├── heart_bear.png │ │ └── chess_pieces │ │ │ ├── black │ │ │ ├── w.svg │ │ │ ├── p.svg │ │ │ ├── y.svg │ │ │ ├── r.svg │ │ │ ├── b.svg │ │ │ ├── a.svg │ │ │ ├── k.svg │ │ │ ├── n.svg │ │ │ ├── u.svg │ │ │ ├── q.svg │ │ │ ├── g.svg │ │ │ ├── j.svg │ │ │ ├── d.svg │ │ │ ├── f.svg │ │ │ ├── s.svg │ │ │ ├── i.svg │ │ │ ├── e.svg │ │ │ ├── h.svg │ │ │ ├── v.svg │ │ │ ├── l.svg │ │ │ ├── z.svg │ │ │ └── t.svg │ │ │ ├── white │ │ │ ├── w.svg │ │ │ ├── p.svg │ │ │ ├── y.svg │ │ │ ├── r.svg │ │ │ ├── n.svg │ │ │ ├── u.svg │ │ │ ├── b.svg │ │ │ ├── k.svg │ │ │ ├── q.svg │ │ │ ├── a.svg │ │ │ ├── g.svg │ │ │ ├── j.svg │ │ │ ├── d.svg │ │ │ ├── f.svg │ │ │ ├── s.svg │ │ │ ├── i.svg │ │ │ ├── e.svg │ │ │ ├── h.svg │ │ │ ├── v.svg │ │ │ ├── l.svg │ │ │ ├── z.svg │ │ │ ├── t.svg │ │ │ └── o.svg │ │ │ ├── attribution.txt │ │ │ ├── garrow.svg │ │ │ ├── rarrow.svg │ │ │ ├── sarrow.svg │ │ │ ├── grarrow.svg │ │ │ ├── srarrow.svg │ │ │ ├── sgarrow.svg │ │ │ └── sgrarrow.svg │ ├── __index.html │ └── global.css ├── .idea │ └── .gitignore ├── README.md ├── package.json └── rollup.config.js ├── protochess.gif ├── protochess-common ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── protochess-engine-rs ├── src │ ├── constants │ │ ├── mod.rs │ │ └── fen.rs │ ├── rankfile.rs │ ├── types │ │ ├── bitboard.rs │ │ ├── mod.rs │ │ └── chess_move.rs │ ├── position │ │ ├── position_properties.rs │ │ ├── piece.rs │ │ ├── castle_rights.rs │ │ ├── piece_set.rs │ │ ├── zobrist_table.rs │ │ └── movement_pattern.rs │ ├── main.rs │ ├── move_generator │ │ └── bitboard_moves.rs │ └── transposition_table │ │ └── mod.rs ├── Cargo.toml ├── README.md └── tests │ ├── fen.rs │ └── custom_pieces.rs ├── protochess-engine-wasm ├── README.md ├── src │ ├── utils.rs │ └── lib.rs └── Cargo.toml ├── protochess-server-rs ├── README.md ├── src │ ├── room_message.rs │ ├── client.rs │ ├── main.rs │ └── client_message.rs └── Cargo.toml ├── .dockerignore ├── Dockerfile └── README.md /protochess-front/src/pages/wasmtest.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/scripts/now/.gitignore: -------------------------------------------------------------------------------- 1 | public/ 2 | node_modules/ 3 | .now -------------------------------------------------------------------------------- /protochess.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raytran/protochess/HEAD/protochess.gif -------------------------------------------------------------------------------- /protochess-front/sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "container": { 3 | "port": 5000 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /protochess-common/README.md: -------------------------------------------------------------------------------- 1 | Shared data structures between Javascript/Websocket server, serialized with Serde 2 | -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/.netlify/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "siteId": "a5b639b1-6d59-4150-98aa-c776e525bb0a" 3 | } -------------------------------------------------------------------------------- /protochess-front/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raytran/protochess/HEAD/protochess-front/static/favicon.png -------------------------------------------------------------------------------- /protochess-engine-rs/src/constants/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fen; 2 | pub const DEFAULT_WIDTH:u8 = 8; 3 | pub const DEFAULT_HEIGHT:u8 = 8; 4 | -------------------------------------------------------------------------------- /protochess-front/static/click_sound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raytran/protochess/HEAD/protochess-front/static/click_sound.wav -------------------------------------------------------------------------------- /protochess-front/static/protochess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raytran/protochess/HEAD/protochess-front/static/protochess.png -------------------------------------------------------------------------------- /protochess-front/static/images/zebra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raytran/protochess/HEAD/protochess-front/static/images/zebra.png -------------------------------------------------------------------------------- /protochess-front/static/images/heart_bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raytran/protochess/HEAD/protochess-front/static/images/heart_bear.png -------------------------------------------------------------------------------- /protochess-engine-wasm/README.md: -------------------------------------------------------------------------------- 1 | ## Rust -> WASM Bindings for Protochess 2 | This crate uses wasm-bindgen to compile the protochess-engine to web assembly. 3 | -------------------------------------------------------------------------------- /protochess-front/src/components/MovementPatternDisplay/DisplayMode.js: -------------------------------------------------------------------------------- 1 | export const DisplayMode = { 2 | ALL: 0, 3 | TRANSLATE: 1, 4 | ATTACK: 2 5 | } 6 | -------------------------------------------------------------------------------- /protochess-server-rs/README.md: -------------------------------------------------------------------------------- 1 | # Protochess server backend 2 | 3 | Protochess.com uses Warp to host the static Svelte SPA and the websocket server for multiplayer. 4 | 5 | -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/.gitignore: -------------------------------------------------------------------------------- 1 | public/ 2 | node_modules/ 3 | api/ssr/bundle.json 4 | .netlify/functions 5 | package-lock.json 6 | # Local Netlify folder 7 | .netlify -------------------------------------------------------------------------------- /protochess-front/scripts/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | routify-starter: 5 | image: "routify-starter" 6 | ports: 7 | - "5000:5000" 8 | - "5005:5005" -------------------------------------------------------------------------------- /protochess-front/src/main.js: -------------------------------------------------------------------------------- 1 | import HMR from '@sveltech/routify/hmr' 2 | import App from './App.svelte'; 3 | 4 | const app = HMR(App, { target: document.body }, 'routify-app') 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/constants/fen.rs: -------------------------------------------------------------------------------- 1 | // various FEN string constants 2 | pub const STARTING_POS:&str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 3 | pub const EMPTY:&str = "8/8/8/8/8/8/8/8 w KQkq - 0 1"; 4 | -------------------------------------------------------------------------------- /protochess-front/src/App.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /protochess-front/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /protochess-front/scripts/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine 2 | RUN apk add --no-cache git 3 | WORKDIR /app 4 | RUN npm init -y && npm install spassr@1.0.2 5 | COPY ./dist ./dist 6 | EXPOSE 5000 5005 7 | ENTRYPOINT ["npx", "spassr", "--serve-spa", "--serve-ssr"] 8 | -------------------------------------------------------------------------------- /protochess-front/scripts/now/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "routes": [ 4 | { 5 | "handle": "filesystem" 6 | }, 7 | { 8 | "src": "/.*", 9 | "dest": "/api/ssr.js" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /protochess-server-rs/src/room_message.rs: -------------------------------------------------------------------------------- 1 | use crate::client::Client; 2 | use uuid::Uuid; 3 | use crate::client_message::{ClientRequest}; 4 | use std::sync::{ Arc }; 5 | 6 | pub enum RoomMessage { 7 | AddClient(Arc), 8 | RemoveClient(Uuid), 9 | External(Uuid, ClientRequest) 10 | } -------------------------------------------------------------------------------- /protochess-front/scripts/now/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "echo \"dummy build\"", 4 | "build:app": "cd ../.. && npm run build", 5 | "deploy": "node build && npx now" 6 | }, 7 | "dependencies": { 8 | "@sveltech/ssr": "^0.0.9", 9 | "fs-extra": "^8.1.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /protochess-front/scripts/now/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const { execSync } = require('child_process'); 3 | 4 | if (!fs.pathExistsSync('../../dist')) { 5 | console.log('Building app...') 6 | execSync('npm run build:app', {stdio: 'inherit'}) 7 | } 8 | 9 | fs.removeSync('public') 10 | fs.copySync('../../dist', 'public') -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/api/ssr/ssr.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const { ssr } = require('@sveltech/ssr') 3 | const { script, template } = require('./bundle.json') 4 | 5 | exports.handler = async (event, context) => { 6 | const body = await ssr(template, script, event.path) 7 | return { statusCode: 200, body: body + '\n' } 8 | } -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/api/ssr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ssr", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "ssr.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@sveltech/ssr": "0.0.9" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/netlify.toml: -------------------------------------------------------------------------------- 1 | 2 | [build] 3 | publish = "public/" 4 | functions = "api/" 5 | ignore = "git diff --quiet HEAD^ HEAD ../../" 6 | 7 | [[redirects]] 8 | # SSR and SPA 9 | #from = "/*" 10 | #to = "/.netlify/functions/ssr" 11 | #status = 200 12 | 13 | # SPA only 14 | from = "/*" 15 | to = "/__app.html" 16 | status = 200 -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/w.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npm run build:app && npm run build:netlify", 4 | "build:app": "cd ../.. && npm i && npm run build", 5 | "build:netlify": "node build && cd api/ssr && npm i", 6 | "deploy": "npm run build:netlify && netlify deploy" 7 | }, 8 | "dependencies": { 9 | "fs-extra": "^8.1.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/w.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/README.md: -------------------------------------------------------------------------------- 1 | # protochess-front 2 | 3 | Frontend website for protochess.com. Uses [Routify](https://github.com/sveltech/routify) for SPA routing (with SSR/pre-rendering disabled). 4 | 5 | ### Get started 6 | 7 | To run a dev server 8 | ``` 9 | npm install 10 | npm run dev 11 | ``` 12 | 13 | ## Building 14 | 15 | ``` 16 | npm run build 17 | ``` 18 | This builds the SPA into /dist 19 | -------------------------------------------------------------------------------- /protochess-front/scripts/docker/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | docker build . -f scripts/docker/Dockerfile -t routify-starter 5 | echo "__________" 6 | echo "you can run this example from your project root with" 7 | echo " docker-compose -f scripts/docker/docker-compose.yml up -d" 8 | echo "" 9 | echo "or " 10 | echo " docker run -d -p 5000:5000 -p 5005:5005 routify-starter" 11 | echo "" -------------------------------------------------------------------------------- /protochess-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "protochess-common" 3 | version = "0.1.0" 4 | authors = ["raymond "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | serde = {version = "1.0", features = ["derive"] } 11 | serde_json = "1.0" 12 | protochess-engine-rs = {path = "../protochess-engine-rs"} 13 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/attribution.txt: -------------------------------------------------------------------------------- 1 | Ship 2 | https://game-icons.net/1x1/delapouite/drakkar.html 3 | Whale 4 | https://thenounproject.com/search/?q=animal&i=1951432 5 | Monkey 6 | https://www.svgrepo.com/svg/176703/monkey-animals 7 | Dog 8 | https://www.flaticon.com/free-icon/dog_2965101 9 | 10 | Other icons from : 11 | https://game-icons.net/ 12 | 13 | 14 | Click sound: 15 | https://freesound.org/people/zippi1/sounds/17892/ -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/p.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/scripts/now/api/ssr.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const { ssr } = require('@sveltech/ssr') 3 | 4 | const script = fs.readFileSync(require.resolve('../public/build/bundle.js'), 'utf8') 5 | const template = fs.readFileSync(require.resolve('../public/__app.html'), 'utf8') 6 | 7 | module.exports = async (req, res) => { 8 | const html = await ssr(template, script, req.url) 9 | res.send(html + '\n') 10 | } 11 | 12 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/p.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/components/Chess/ColorConstants.js: -------------------------------------------------------------------------------- 1 | export default { 2 | DARK_SQUARE: '#a97d5d', 3 | LIGHT_SQUARE: '#f7dcb4', 4 | DISABLED: 'rgb(87,87,87)', 5 | FROM_HIGHLIGHT_COLOR: 'rgba(92,147,94,0.7)', 6 | TO_HIGHLIGHT_COLOR: 'rgba(153,186,130,0.7)', 7 | POSSIBLE_FROM_HIGHLIGHT_COLOR: 'rgba(213,197,87,0.7)', 8 | POSSIBLE_TO_HIGHLIGHT_COLOR: 'rgba(217,191,119, 0.6)', 9 | IN_CHECK_HIGHLIGHT_COLOR: "rgba(255,0,0,0.5)" 10 | }; -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | #Common 2 | /protochess-common/target 3 | /protochess-common/.idea 4 | 5 | #Engine 6 | /protochess-engine-rs/target 7 | /protochess-engine-rs/.idea 8 | /protochess-engine-wasm/target 9 | /protochess-engine-rs/.idea 10 | 11 | #Server 12 | /protochess-server-rs/.idea 13 | /protochess-server-rs/dist 14 | /protochess-server-rs/target 15 | 16 | #Front 17 | /protochess-front/node_modules 18 | /protochess-front/scripts 19 | /protochess-front/package-lock.json 20 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/y.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/garrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/rarrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/y.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-wasm/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | // When the `console_error_panic_hook` feature is enabled, we can call the 3 | // `set_panic_hook` function at least once during initialization, and then 4 | // we will get better error messages if our code ever panics. 5 | // 6 | // For more details see 7 | // https://github.com/rustwasm/console_error_panic_hook#readme 8 | #[cfg(feature = "console_error_panic_hook")] 9 | console_error_panic_hook::set_once(); 10 | } 11 | -------------------------------------------------------------------------------- /protochess-front/src/pages/_PlayButtons.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | Play against the Computer 9 | 10 |
11 | 14 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/sarrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/scripts/netlify/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | 3 | // Copy the public folder to netlify's working folder 4 | fs.removeSync('public') 5 | fs.copySync('../../dist', 'public') 6 | 7 | // For SSR we need to copy the template and bundle to our SSR function 8 | const bundle = { 9 | date: new Date, 10 | script: fs.readFileSync('public/build/bundle.js', 'utf8'), 11 | template: fs.readFileSync('public/__app.html', 'utf8') 12 | } 13 | fs.writeFileSync('api/ssr/bundle.json', JSON.stringify(bundle, 0, 2)) -------------------------------------------------------------------------------- /protochess-front/src/components/MediaQuery.svelte: -------------------------------------------------------------------------------- 1 | 17 |
18 | 19 |
20 |
21 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /protochess-front/static/__index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Protochess 8 | 9 | 10 | 11 | 12 | 13 | 14 | __SCRIPT__ 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /protochess-front/src/components/PlayersList/PlayersList.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 |
    10 | {#each playersList.names as player, i} 11 |
  • 12 | {i === 0 ? "⭐ " : "- "}{playersList.you === player ? player + " (You)" : player} 13 |
  • 14 | {/each} 15 |
16 |
17 | -------------------------------------------------------------------------------- /protochess-front/src/pages/_fallback.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 |
19 |
404
20 |
Page not found. 21 | 22 | Go home 23 |
24 |
25 | -------------------------------------------------------------------------------- /protochess-server-rs/src/client.rs: -------------------------------------------------------------------------------- 1 | use uuid::Uuid; 2 | use tokio::sync::mpsc; 3 | use crate::client_message::ClientResponse; 4 | 5 | pub struct Client { 6 | pub(crate) name: String, 7 | pub(crate) id: Uuid, 8 | pub(crate) sender: mpsc::UnboundedSender> 9 | } 10 | 11 | impl Client { 12 | pub fn try_send(&self, cr: ClientResponse){ 13 | if let Ok(text) = serde_json::to_string(&cr){ 14 | let msg = Ok(warp::ws::Message::text(text)); 15 | if let Err(_) = self.sender.send(msg){ 16 | eprintln!("client send error????"); 17 | } 18 | } 19 | } 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/rankfile.rs: -------------------------------------------------------------------------------- 1 | //TODO 2 | /* 3 | fn to_xy(rank_file:String) -> (u8, u8) { 4 | let file = rank_file.chars()[0]; 5 | let rank = rank_file.chars().skip(0).take(rank_file.len()).collect(); 6 | ((file.to_digit(10) - 65).unwrap(), rank.parse::().unwrap() - 1) 7 | } 8 | */ 9 | 10 | /// Converts an (x, y) location to chess rank-file notation 11 | /// Ex: to_rank_file(0, 1) = A2 12 | pub(crate) fn to_rank_file(x:u8, y:u8) -> String{ 13 | let mut return_string = String::new(); 14 | return_string.push(std::char::from_u32((x+65) as u32).unwrap()); 15 | return_string.push_str(format!("{}", (y + 1)).as_ref()); 16 | return_string 17 | } 18 | 19 | -------------------------------------------------------------------------------- /protochess-engine-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "protochess-engine-rs" 3 | version = "0.1.0" 4 | authors = ["raytr "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [profile.dev] 9 | opt-level = 3 10 | 11 | [profile.test] 12 | opt-level = 3 13 | 14 | [features] 15 | stdweb = [ "instant/stdweb" ] 16 | wasm-bindgen = [ "instant/wasm-bindgen" ] 17 | 18 | [dependencies] 19 | instant = { version = "0.1", features = [ "now", "wasm-bindgen" ] } 20 | scan-rules = "0.2.0" 21 | rand = "0.7.3" 22 | arrayvec = "0.5.1" 23 | numext-fixed-uint = "0.1.4" 24 | ahash = "0.3.5" 25 | lazy_static = "1.4.0" 26 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/r.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/r.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/pages/chess/_WebChat.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/n.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/u.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/grarrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/b.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Rust 2 | FROM rust as builder 3 | WORKDIR /usr/src/protochess 4 | RUN rustup target add x86_64-unknown-linux-musl 5 | COPY . . 6 | RUN cargo install --target x86_64-unknown-linux-musl --path ./protochess-server-rs 7 | 8 | # Install npm 9 | RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - 10 | RUN apt-get install -y nodejs 11 | 12 | #Build frontend SPA 13 | WORKDIR /usr/src/protochess/protochess-front 14 | RUN npm install 15 | RUN npm run build 16 | 17 | # Bundle Stage 18 | FROM scratch 19 | # Copy in warp 20 | COPY --from=builder /usr/local/cargo/bin/protochess-server-rs . 21 | # Copy in built frontend SPA 22 | COPY --from=builder /usr/src/protochess/protochess-front/dist ./dist 23 | USER 1000 24 | EXPOSE 3030 25 | CMD ["./protochess-server-rs"] 26 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/b.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/types/bitboard.rs: -------------------------------------------------------------------------------- 1 | 2 | pub type Bitboard = numext_fixed_uint::U256; 3 | 4 | pub fn to_index(x:u8, y:u8) -> usize{ 5 | (16 * y + x) as usize 6 | } 7 | 8 | pub fn from_index(index:usize) -> (u8, u8) { 9 | (index as u8 % 16 , index as u8 / 16 ) 10 | } 11 | 12 | pub fn to_string(bitboard:&Bitboard) -> String { 13 | let mut return_str = String::new(); 14 | for y in (0..16).rev() { 15 | for x in 0..16 { 16 | if bitboard.bit(to_index(x, y)).unwrap(){ 17 | return_str.push('1'); 18 | }else{ 19 | return_str.push('.'); 20 | } 21 | return_str.push(' '); 22 | } 23 | return_str.push('\n'); 24 | } 25 | return_str 26 | } 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/k.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-server-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "protochess-server-rs" 3 | version = "0.1.0" 4 | authors = ["raymond "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tokio = { version = "0.2.21", features = ["full"] } 11 | warp = "0.2" 12 | serde = {version = "1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | futures = { version = "0.3", default-features = false } 15 | uuid = { version = "0.4", features = ["serde", "v4"] } 16 | pretty_env_logger = "0.4.0" 17 | lazy_static = "1.4.0" 18 | adjective_adjective_animal = "0.1.0" 19 | 20 | protochess-engine-rs = {path = "../protochess-engine-rs"} 21 | protochess-common = {path = "../protochess-common"} 22 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bitboard; 2 | pub mod chess_move; 3 | 4 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] 5 | pub enum PieceType { 6 | King, 7 | Queen, 8 | Rook, 9 | Bishop, 10 | Knight, 11 | Pawn, 12 | Custom(char), 13 | } 14 | 15 | impl PieceType { 16 | pub fn from_char(c:char) -> PieceType { 17 | match c.to_ascii_lowercase() { 18 | 'k' =>{PieceType::King} 19 | 'q' =>{PieceType::Queen} 20 | 'r' =>{PieceType::Rook} 21 | 'b' =>{PieceType::Bishop} 22 | 'n' =>{PieceType::Knight} 23 | 'p' =>{PieceType::Pawn} 24 | _ => {PieceType::Custom(c)} 25 | } 26 | } 27 | } 28 | pub struct Dimensions { 29 | pub width:u8, 30 | pub height:u8, 31 | } 32 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/a.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/q.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/a.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/k.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/n.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/u.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/q.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/pages/about.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Protochess by Raymond Tran

4 |

5 | 6 | It's on Github 7 | 8 |

9 | The frontend is written in Svelte with routing from Routify and styling with the Bulma CSS framework. 10 |
11 | All the chess logic is written in Rust, and compiled to WebAssembly to run singleplayer. The multiplayer websocket server uses Warp. 12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/g.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/g.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/j.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/j.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/pages/_ProtochessInfo.svelte: -------------------------------------------------------------------------------- 1 |

Protochess

2 |

Chess with custom pieces and boards

3 | 4 |
5 | 6 |
7 |
8 | Are you a chess grandmaster? Or maybe you barely know the rules of chess? Either way, Protochess allows you to customize chess to your heart's content. 9 | Create your own boards/pieces using the built-in editor and then play against your friends (or the computer if you don't have friends). 10 | Here are some ideas to get you started: 11 |
    12 |
  • A piece that attacks and moves like a knight + queen.
  • 13 |
  • A piece that moves north and south but attacks like a knight.
  • 14 |
  • A 6x5 board with only pawns and kings.
  • 15 |
  • A piece that can only move but not attack.
  • 16 |
  • A piece that does nothing but die.
  • 17 |
  • ....Or anything you want!
  • 18 |
19 |
20 | -------------------------------------------------------------------------------- /protochess-front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "run-p routify rollup", 6 | "build": "npm run routify -- -b && rollup -c", 7 | "serve": "spassr --serve-spa", 8 | "rollup": "rollup -cw", 9 | "routify": "routify -D" 10 | }, 11 | "devDependencies": { 12 | "@rollup/plugin-commonjs": "^12.0.0", 13 | "@rollup/plugin-node-resolve": "^8.0.0", 14 | "@wasm-tool/rollup-plugin-rust": "^1.0.3", 15 | "bulma": "^0.9.0", 16 | "clipboard": "^2.0.6", 17 | "del": "^5.1.0", 18 | "npm-run-all": "^4.1.5", 19 | "reconnecting-websocket": "^4.4.0", 20 | "rollup": "^2.3.4", 21 | "rollup-plugin-copy": "^3.3.0", 22 | "rollup-plugin-livereload": "^1.0.0", 23 | "rollup-plugin-postcss": "^3.1.2", 24 | "rollup-plugin-svelte": "^6.1.1", 25 | "rollup-plugin-terser": "^5.1.2", 26 | "sass": "^1.26.9", 27 | "spassr": "^1.0.3", 28 | "svelma": "^0.3.2", 29 | "svelte-preprocess": "^3.9.10", 30 | "svelte-tabs": "^1.1.0" 31 | }, 32 | "dependencies": { 33 | "@sveltech/routify": "^1.7.13", 34 | "svelte": "^3.0.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /protochess-front/static/global.css: -------------------------------------------------------------------------------- 1 | * { box-sizing: border-box } 2 | 3 | html, body { 4 | position: relative; 5 | width: 100%; 6 | height: 100%; 7 | } 8 | /* 9 | body { 10 | color: #333; 11 | margin: 0; 12 | padding: 8px; 13 | box-sizing: border-box; 14 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 15 | } 16 | 17 | a { 18 | color: rgb(0,100,200); 19 | text-decoration: none; 20 | } 21 | 22 | a:hover { 23 | text-decoration: underline; 24 | } 25 | 26 | a:visited { 27 | color: rgb(0,80,160); 28 | } 29 | 30 | label { 31 | display: block; 32 | } 33 | 34 | input, button, select, textarea { 35 | font-family: inherit; 36 | font-size: inherit; 37 | padding: 0.4em; 38 | margin: 0 0 0.5em 0; 39 | box-sizing: border-box; 40 | border: 1px solid #ccc; 41 | border-radius: 2px; 42 | } 43 | 44 | input:disabled { 45 | color: #ccc; 46 | } 47 | 48 | input[type="range"] { 49 | height: 0; 50 | } 51 | 52 | button { 53 | color: #333; 54 | background-color: #f4f4f4; 55 | outline: none; 56 | } 57 | 58 | button:disabled { 59 | color: #999; 60 | } 61 | 62 | button:not(:disabled):active { 63 | background-color: #ddd; 64 | } 65 | 66 | button:focus { 67 | border-color: #666; 68 | } 69 | 70 | */ 71 | -------------------------------------------------------------------------------- /protochess-front/src/components/Chess/Tile.svelte: -------------------------------------------------------------------------------- 1 | 11 | 20 | 21 |
dispatch('tileMouseOver', tile)} 27 | on:mousedown|preventDefault={()=> dispatch('tileMouseDown', tile)} 28 | on:mouseup={()=> dispatch('tileMouseUp', tile)} 29 | on:click={()=> dispatch('tileClick', tile)}> 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /protochess-server-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | mod room; 2 | mod client; 3 | mod room_message; 4 | mod client_message; 5 | mod websockets; 6 | mod room_manager; 7 | use std::sync::{ Arc }; 8 | use tokio::sync::{RwLock}; 9 | use warp::Filter; 10 | use crate::websockets::user_connected; 11 | use crate::room_manager::{RoomManager}; 12 | 13 | // Clients 14 | pub type Rooms = Arc>; 15 | 16 | #[tokio::main] 17 | async fn main() { 18 | pretty_env_logger::init(); 19 | let rooms = Arc::new(RwLock::new(RoomManager::new())); 20 | let rooms = warp::any().map(move || rooms.clone()); 21 | 22 | // GET /wschess -> websocket upgrade 23 | let wschess = warp::path("wschess") 24 | .and(warp::ws()) 25 | .and(rooms.clone()) 26 | .and_then(|ws: warp::ws::Ws, rooms| async move { 27 | //Hack to get past compiler.... 28 | if false { return Err(warp::reject::not_found()); } 29 | Ok(ws.on_upgrade(move |socket| 30 | user_connected(socket, rooms))) 31 | }); 32 | 33 | let assets = warp::fs::dir("./dist"); 34 | let routes = wschess 35 | .or(assets) 36 | .or(warp::fs::file("./dist/__app.html")); 37 | let routes = warp::get().and(routes); 38 | warp::serve(routes).run(([0, 0, 0, 0], 3030)).await; 39 | } 40 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/d.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/d.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/components/RoomList/RoomList.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 23 |
24 |

Public Rooms

25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {#each roomList.sort((a, b) => a.num_clients > b.num_clients) as roomInfo} 35 | 36 | 37 | 38 | 39 | 40 | {/each} 41 |
Room IdEdits on?# Clients
{roomInfo.room_id}{roomInfo.editable ? 'Yes' : 'No'} {roomInfo.num_clients}
42 |
43 |
44 | -------------------------------------------------------------------------------- /protochess-engine-rs/README.md: -------------------------------------------------------------------------------- 1 | # Protochess Engine 2 | ## This crate contains all the actual chess/chess engine logic. 3 | 4 | The engine features: 5 | * Bitboard representation using 256 bit integers for up to 16x16 sized boards. 6 | * Kindergarden-based move generation using Rust iterators 7 | * Zobrist Hashing 8 | * Transposition Tables 9 | * Principal Variation Search in an Iterative-Deepening framework 10 | * Quiescence search 11 | * History Heuristic 12 | * Killer Heuristic 13 | * Null-move pruning 14 | * Late move reductions 15 | 16 | 17 | ## Differences from a standard engine 18 | Instead of standard piece-square-tables that are compile time constants, the engine dynamically generates piece square tables as well as material value for custom pieces. Custom pieces are assigned material values as a function of how many move directions they have. All pieces, custom or not, have their piece square tables generated dynamically simply by summing up all the possible moves from each square. 19 | 20 | This evaluation is not at all optimized for standard chess (factors like king safety, pawn structure are mostly ignored), but it still plays a standard game well enough to beat me every time (as a casual chess player). 21 | 22 | ## Future improvements 23 | * Multithreading with Lazy SMP (...might break WASM integration) 24 | * Better time management 25 | * UCI compliance for standard games 26 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/position/position_properties.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use crate::types::{PieceType}; 3 | 4 | use crate::position::castle_rights::CastleRights; 5 | use crate::types::chess_move::Move; 6 | 7 | /// Properties that are hard to recover from a Move 8 | #[derive(Clone)] 9 | pub struct PositionProperties { 10 | pub zobrist_key: u64, 11 | pub move_played: Option, 12 | //If the last move was a promotion, promote_from is the previous piecetype 13 | pub promote_from: Option, 14 | pub castling_rights: CastleRights, 15 | //EP square (square behind a double pawn push) 16 | pub ep_square: Option, 17 | //Tuple (owner, PieceType) of the last piece captured, if any 18 | pub captured_piece: Option<(u8, PieceType)>, 19 | pub prev_properties: Option>, 20 | } 21 | 22 | impl PositionProperties { 23 | pub fn default() -> PositionProperties { 24 | PositionProperties{ 25 | zobrist_key: 0, 26 | castling_rights: CastleRights::new(), 27 | move_played: None, 28 | prev_properties: None, 29 | promote_from: None, 30 | ep_square: None, 31 | captured_piece: None, 32 | } 33 | } 34 | 35 | pub fn get_prev(&self) -> Option> { 36 | self.prev_properties.as_ref().cloned() 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Protochess](protochess.gif) 2 | 3 | ![Protochess](https://i.imgur.com/5MYfcpe.png) 4 | 5 | ![Protochess](https://i.imgur.com/6jngcdV.png) 6 | 7 | 8 | # Protochess 9 | https://protochess.com/ 10 | 11 | Protochess is an online chess website designed to let you customize your own chess boards/pieces. Want a piece that can move like a knight + queen? Sure. Want to play on a 16x16 sized board? Impractical but you can do it! 12 | 13 | ## Built with 14 | Rust for the backend/chess logic/multiplayer server 15 | 16 | Svelte for the single page app frontend 17 | 18 | ## Project structure 19 | 20 | The frontend/static website is contained in protochess-front. 21 | 22 | The backend multiplayer websocket server is in protochess-server-rs. 23 | 24 | Common shared data structures between the front/backend are in protochess-common 25 | 26 | The actual chess logic/chess engine is in protochess-engine-rs, with bindings to WebAssembly contained in protochess-engine-wasm. 27 | 28 | ## Running locally with docker 29 | 30 | Included in the base directory is a Dockerfile that compiles the frontend and launches the webserver. 31 | 32 | ``` 33 | git clone https://github.com/raytran/protochess 34 | cd protochess 35 | docker build -t protochess . 36 | docker run -p 3030:3030 protochess 37 | ``` 38 | This will launch the chess page at localhost:3030 39 | 40 | ## Contact 41 | You can email me (Raymond Tran) here: raytran@mit.edu 42 | -------------------------------------------------------------------------------- /protochess-engine-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "protochess-engine-wasm" 3 | version = "0.1.0" 4 | authors = ["raymond "] 5 | edition = "2018" 6 | 7 | 8 | [package.metadata.wasm-pack.profile.release] 9 | wasm-opt = ["-O4", "--enable-mutable-globals"] 10 | 11 | [lib] 12 | crate-type = ["cdylib", "rlib"] 13 | 14 | [features] 15 | default = ["console_error_panic_hook"] 16 | 17 | [dependencies] 18 | wasm-bindgen = {version = "0.2.63", features = ["serde-serialize"]} 19 | protochess-engine-rs = {path = "../protochess-engine-rs"} 20 | protochess-common = {path = "../protochess-common"} 21 | 22 | 23 | # The `console_error_panic_hook` crate provides better debugging of panics by 24 | # logging them with `console.error`. This is great for development, but requires 25 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 26 | # code size when deploying. 27 | console_error_panic_hook = { version = "0.1.6", optional = true } 28 | 29 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size 30 | # compared to the default allocator's ~10K. It is slower than the default 31 | # allocator, however. 32 | # 33 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. 34 | wee_alloc = { version = "0.4.5", optional = true } 35 | 36 | [dev-dependencies] 37 | wasm-bindgen-test = "0.3.13" 38 | 39 | [profile.release] 40 | # Tell `rustc` to optimize for small code size. 41 | opt-level = "s" 42 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/f.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/f.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/s.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate scan_rules; 2 | 3 | pub fn main() { 4 | let mut engine = protochess_engine_rs::Engine::default(); 5 | //let mut engine = protochess_engine_rs::Engine::from_fen("rnbqkbnr/nnnnnnnn/rrrrrrrr/8/8/8/QQQQQQQQ/RNBQKBNR w KQkq - 0 1".parse().unwrap()); 6 | //let mut engine = protochess_engine_rs::Engine::from_fen(("rnbqkbnr/pp4pp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").parse().unwrap()); 7 | //let mut engine = protochess_engine_rs::Engine::from_fen("r1b3nr/ppqk1Bbp/2pp4/4P1B1/3n4/3P4/PPP2QPP/R4RK1 w - - 1 0".parse().unwrap()); 8 | //let mut engine = protochess_engine_rs::Engine::from_fen("1Q6/5pk1/2p3p1/1pbbN2p/4n2P/8/r5P1/5K2 b - - 0 1".parse().unwrap()); 9 | //let mut engine = protochess_engine_rs::Engine::from_fen("rnbqkbnr/pppppppp/8/8/8/8/8/RNBQKBNR w KQkq - 0 1".parse().unwrap()); 10 | println!("{}", engine.to_string()); 11 | 12 | let mut ply = 0; 13 | loop { 14 | 15 | if !engine.play_best_move_timeout(4).0 { 16 | break; 17 | } 18 | ply += 1; 19 | println!("PLY: {} Engine plays: \n", ply); 20 | println!("{}", engine.to_string()); 21 | println!("========================================"); 22 | 23 | 24 | 25 | /* 26 | readln! { 27 | // Space-separated ints 28 | (let x1: u8, let y1: u8, let x2: u8, let y2: u8) => { 29 | println!("x1 y1 x2 y2: {} {} {} {}", x1, y1, x2, y2); 30 | engine.make_move(x1, y1, x2, y2); 31 | println!("{}", engine.to_string()); 32 | } 33 | } 34 | 35 | */ 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/srarrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/s.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/i.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/e.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/i.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/components/Chess/Piece.svelte: -------------------------------------------------------------------------------- 1 | 16 | 45 | 46 | 47 | {#if piece.x >= 0 && piece.y >= 0 && piece.x < gameWidth && piece.y < gameHeight} 48 |
50 | 51 | 52 | {#if piece.piece_text} 53 | 54 | {piece.piece_text} 55 | 56 | {/if} 57 |
58 | {/if} 59 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/e.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/components/ChessEditor/PieceLeftControl.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {#if showSaveChanges} 13 | 14 | 15 | {/if} 16 |
17 |
18 | 22 |
23 |
24 | 25 |
26 | 30 |
31 | 32 | 33 | 34 |
35 | 39 |
40 | 41 |
42 | 46 |
47 | 48 |
49 | 53 |
54 |
55 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/h.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/h.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/components/CreateRoomDialog/CreateRoomDialog.svelte: -------------------------------------------------------------------------------- 1 | 9 | 38 | 39 |
40 |

Create a new room

41 |
42 |
43 | 47 | 51 |
52 | 56 | 57 | 58 | 59 | 60 | 61 |
62 |
-------------------------------------------------------------------------------- /protochess-front/src/components/Chat/Chat.svelte: -------------------------------------------------------------------------------- 1 | 30 | 52 | 53 |
54 |

Chat

55 |
56 | {#each comments as comment} 57 |
58 | 59 | { 60 | comment.from === name ? comment.from + " (You)" : comment.from 61 | } 62 |
63 |      64 | {comment.content} 65 |
66 |
67 | {/each} 68 |
69 | 70 |
-------------------------------------------------------------------------------- /protochess-front/src/components/ChessEditor/ChessLeftControl.svelte: -------------------------------------------------------------------------------- 1 | 11 | 17 |
18 | {#if showSaveChanges} 19 | 20 | 21 | {/if} 22 |
23 | 27 |
28 | 29 |
30 | 34 |
35 | 36 |
37 | 41 |
42 | 43 | 44 | 47 | 48 |
49 | Registered Movement Patterns: 50 |
51 | 52 |
53 | -------------------------------------------------------------------------------- /protochess-front/src/components/MovementPatternDisplayBar/MovementPatternDisplayBar.svelte: -------------------------------------------------------------------------------- 1 | 38 | 43 | 44 |
45 | {#each customPieces as {pieceType, movementPattern, size}} 46 |
47 | 54 |
55 | {/each} 56 |
57 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/position/piece.rs: -------------------------------------------------------------------------------- 1 | use crate::types::PieceType; 2 | use crate::types::bitboard::Bitboard; 3 | 4 | pub struct Piece { 5 | pub char_rep: char, 6 | //Player num for the owner of this piece 7 | pub player_num: u8, 8 | pub piece_type: PieceType, 9 | pub bitboard: Bitboard 10 | } 11 | 12 | impl Piece { 13 | pub fn blank_custom(player_num:u8, char_rep: char) -> Piece { 14 | Piece { 15 | player_num, 16 | char_rep, 17 | piece_type: PieceType::Custom(char_rep), 18 | bitboard: Bitboard::zero() 19 | } 20 | } 21 | 22 | pub fn blank_pawn(player_num:u8) -> Piece{ 23 | Piece { 24 | player_num, 25 | char_rep: 'p', 26 | piece_type: PieceType::Pawn, 27 | bitboard: Bitboard::zero() 28 | } 29 | } 30 | 31 | pub fn blank_knight(player_num:u8) -> Piece{ 32 | Piece { 33 | player_num, 34 | char_rep: 'n', 35 | piece_type: PieceType::Knight, 36 | bitboard: Bitboard::zero() 37 | } 38 | } 39 | 40 | pub fn blank_king(player_num:u8) -> Piece{ 41 | Piece { 42 | player_num, 43 | char_rep: 'k', 44 | piece_type: PieceType::King, 45 | bitboard: Bitboard::zero() 46 | } 47 | } 48 | 49 | pub fn blank_rook(player_num:u8) -> Piece{ 50 | Piece { 51 | player_num, 52 | char_rep: 'r', 53 | piece_type: PieceType::Rook, 54 | bitboard: Bitboard::zero() 55 | } 56 | } 57 | 58 | pub fn blank_bishop(player_num:u8) -> Piece{ 59 | Piece { 60 | player_num, 61 | char_rep: 'b', 62 | piece_type: PieceType::Bishop, 63 | bitboard: Bitboard::zero() 64 | } 65 | } 66 | 67 | pub fn blank_queen(player_num:u8) -> Piece{ 68 | Piece { 69 | player_num, 70 | char_rep: 'q', 71 | piece_type: PieceType::Queen, 72 | bitboard: Bitboard::zero() 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /protochess-engine-rs/src/position/castle_rights.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct CastleRights(u8, u8, u8); 3 | 4 | /// Castling rights for up to 8 players 5 | /// CastleRights.0 -- kingside rights 6 | /// CastleRights.1 -- Queenside rights 7 | /// CastleRights.2 -- 1 if the player actually castled 8 | /// Where each bit in the u8 represents the castling right for the player at that index 9 | /// Ex if CastleRights.0 == 1u8 then the 0th player can castle kingside 10 | impl CastleRights { 11 | pub fn new() -> CastleRights { 12 | CastleRights(255u8, 255u8, 0u8) 13 | } 14 | 15 | pub fn can_player_castle_kingside(&self, playernum: u8) -> bool { 16 | (self.0 >> playernum) & 1u8 != 0 17 | } 18 | 19 | pub fn can_player_castle_queenside(&self, playernum: u8) -> bool { 20 | (self.1 >> playernum) & 1u8 != 0 21 | } 22 | 23 | pub fn can_player_castle(&self, playernum:u8) -> bool { 24 | self.can_player_castle_kingside(playernum) || 25 | self.can_player_castle_queenside(playernum) 26 | } 27 | 28 | pub fn did_player_castle(&self, playernum:u8) -> bool { 29 | (self.2 >> playernum) & 1u8 != 0 30 | } 31 | 32 | pub fn set_player_castled(&mut self, playernum:u8) { 33 | self.2 |= 1u8 << playernum 34 | } 35 | 36 | pub fn disable_kingside_castle(&mut self, playernum: u8) { 37 | self.0 &= !(1u8 << playernum) 38 | } 39 | 40 | pub fn disable_queenside_castle(&mut self, playernum: u8) { 41 | self.1 &= !(1u8 << playernum) 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use crate::position::castle_rights::CastleRights; 48 | 49 | #[test] 50 | fn test() { 51 | let mut test_rights = CastleRights::new(); 52 | println!("{}",test_rights.can_player_castle_queenside(0)); 53 | test_rights.disable_queenside_castle(0); 54 | println!("{}",test_rights.can_player_castle_queenside(0)); 55 | println!("{}",test_rights.can_player_castle_kingside(0)); 56 | test_rights.disable_kingside_castle(0); 57 | println!("{}",test_rights.can_player_castle_kingside(0)); 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/v.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/pages/chess/[roomId].svelte: -------------------------------------------------------------------------------- 1 | 34 | 39 | 40 |
41 |
42 | 45 | {#if $Connected } 46 | ✓ Connected 47 | {:else} 48 | ✖ Disconnected 49 | {/if} 50 |
51 | 52 |
53 |
54 | 55 | 56 | 57 | 58 |
59 | 60 |
61 |
62 | 63 | {#if $GameInfo.editable === true} 64 | 65 |
66 | requestEdits(e)} /> 67 |
68 |
69 | {/if} 70 |
71 |
72 | -------------------------------------------------------------------------------- /protochess-front/src/pages/faq.svelte: -------------------------------------------------------------------------------- 1 | 25 |
26 |
27 |

FAQ

28 |

Frequently Asked Questions

29 | {#each q_and_a as qa} 30 |
31 | {qa.question} 32 |

{qa.answer}

33 |
34 | {/each} 35 |
36 |
37 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/v.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/components/Modal.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 76 | 77 |
78 |
79 | 85 |
86 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/sgarrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/l.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/l.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-rs/tests/fen.rs: -------------------------------------------------------------------------------- 1 | extern crate protochess_engine_rs; 2 | 3 | #[cfg(test)] 4 | mod fen { 5 | #[test] 6 | fn starting_pos() { 7 | let mut engine = protochess_engine_rs::Engine::default(); 8 | assert_eq!(engine.perft(1), 20); 9 | assert_eq!(engine.perft(2), 400); 10 | assert_eq!(engine.perft(3), 8902); 11 | assert_eq!(engine.perft(4), 197281); 12 | assert_eq!(engine.perft(5), 4865609); 13 | } 14 | 15 | #[test] 16 | fn kiwipete(){ 17 | let mut engine = protochess_engine_rs::Engine::from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ".parse().unwrap()); 18 | assert_eq!(engine.perft(1), 48); 19 | assert_eq!(engine.perft(2), 2039); 20 | assert_eq!(engine.perft(3), 97862); 21 | assert_eq!(engine.perft(4), 4085603); 22 | assert_eq!(engine.perft(5), 193690690); 23 | } 24 | 25 | #[test] 26 | fn pos3(){ 27 | let mut engine = protochess_engine_rs::Engine::from_fen("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - ".parse().unwrap()); 28 | assert_eq!(engine.perft(1), 14); 29 | assert_eq!(engine.perft(2), 191); 30 | assert_eq!(engine.perft(3), 2812); 31 | assert_eq!(engine.perft(4), 43238); 32 | assert_eq!(engine.perft(5), 674624); 33 | } 34 | 35 | #[test] 36 | fn pos4(){ 37 | let mut engine = protochess_engine_rs::Engine::from_fen("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1".parse().unwrap()); 38 | assert_eq!(engine.perft(1), 6); 39 | assert_eq!(engine.perft(2), 264); 40 | assert_eq!(engine.perft(3), 9467); 41 | assert_eq!(engine.perft(4), 422333); 42 | assert_eq!(engine.perft(5), 15833292); 43 | } 44 | 45 | #[test] 46 | fn pos5(){ 47 | let mut engine = protochess_engine_rs::Engine::from_fen("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8 ".parse().unwrap()); 48 | assert_eq!(engine.perft(1), 44); 49 | assert_eq!(engine.perft(2), 1486); 50 | assert_eq!(engine.perft(3), 62379); 51 | assert_eq!(engine.perft(4), 2103487); 52 | assert_eq!(engine.perft(5), 89941194); 53 | } 54 | 55 | #[test] 56 | fn pos6(){ 57 | let mut engine = protochess_engine_rs::Engine::from_fen("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10 ".parse().unwrap()); 58 | assert_eq!(engine.perft(1), 46); 59 | assert_eq!(engine.perft(2), 2079); 60 | assert_eq!(engine.perft(3), 89890); 61 | assert_eq!(engine.perft(4), 3894594); 62 | assert_eq!(engine.perft(5), 164075551); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/z.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use protochess_engine_rs::Engine; 4 | use protochess_common::{GameState, serialize_game_state, validate_gamestate_request}; 5 | use wasm_bindgen::prelude::*; 6 | use wasm_bindgen::__rt::std::rc::Rc; 7 | 8 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global 9 | // allocator. 10 | #[cfg(feature = "wee_alloc")] 11 | #[global_allocator] 12 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 13 | 14 | #[wasm_bindgen] 15 | extern { 16 | fn alert(s: &str); 17 | } 18 | 19 | #[wasm_bindgen] 20 | pub fn greet() { 21 | alert("Hello, protochess-engine-wasm!"); 22 | } 23 | 24 | #[wasm_bindgen] 25 | pub struct Protochess { 26 | engine: Engine 27 | } 28 | 29 | #[wasm_bindgen] 30 | impl Protochess { 31 | #[wasm_bindgen(constructor)] 32 | pub fn new() -> Protochess { 33 | Protochess{ 34 | engine: Engine::default() 35 | } 36 | } 37 | 38 | pub fn to_string(&mut self) -> String { 39 | self.engine.to_string() 40 | } 41 | 42 | pub fn play_best_move(&mut self, depth: u8) -> bool{ 43 | self.engine.play_best_move(depth) 44 | } 45 | 46 | pub fn make_move(&mut self, x1: u8, y1: u8, x2: u8, y2: u8) -> bool { 47 | self.engine.make_move(x1,y1,x2,y2) 48 | } 49 | 50 | pub fn play_best_move_timeout(&mut self, time: usize) -> i8 { 51 | let (success, search_depth) = self.engine.play_best_move_timeout(time as u64); 52 | if !success { 53 | return -1; 54 | }else{ 55 | return search_depth as i8; 56 | } 57 | } 58 | 59 | pub fn get_best_move_timeout(&mut self, time:usize) -> JsValue { 60 | let best = self.engine.get_best_move_timeout(time as u64); 61 | JsValue::from_serde(&best).unwrap() 62 | } 63 | 64 | pub fn get_state(&self) -> JsValue { 65 | let game_state = serialize_game_state(&self.engine.current_position); 66 | JsValue::from_serde(&game_state).unwrap() 67 | } 68 | 69 | pub fn to_move_in_check(&mut self) -> bool { 70 | self.engine.to_move_in_check() 71 | } 72 | 73 | ///True on succcess 74 | pub fn set_state(&mut self, val: &JsValue) -> bool { 75 | let request_game_state: GameState = val.into_serde().unwrap(); 76 | if let Some((movements, valid_squares, valid_pieces)) = 77 | validate_gamestate_request(request_game_state.tiles, 78 | request_game_state.pieces, 79 | request_game_state.movement_patterns){ 80 | self.engine.set_state(movements, 81 | valid_squares, 82 | valid_pieces); 83 | return true; 84 | } 85 | false 86 | } 87 | 88 | pub fn moves_from(&mut self, x:u8, y:u8) -> JsValue{ 89 | let moves = self.engine.moves_from(x, y); 90 | JsValue::from_serde(&moves).unwrap() 91 | } 92 | } -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/sgrarrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/position/piece_set.rs: -------------------------------------------------------------------------------- 1 | //Pieces that a player has 2 | use crate::types::bitboard::Bitboard; 3 | use crate::position::piece::Piece; 4 | 5 | /// Represents a set of pieces for a player 6 | /// custom is a vec of custom piece 7 | pub struct PieceSet { 8 | pub occupied: Bitboard, 9 | pub king: Piece, 10 | pub queen: Piece, 11 | pub bishop: Piece, 12 | pub knight: Piece, 13 | pub rook: Piece, 14 | pub pawn: Piece, 15 | pub custom: Vec, 16 | pub player_num: u8 17 | } 18 | 19 | impl PieceSet { 20 | pub fn new(player_num:u8) -> PieceSet { 21 | PieceSet { 22 | occupied: Bitboard::zero(), 23 | king: Piece::blank_king(player_num), 24 | queen: Piece::blank_queen(player_num), 25 | bishop: Piece::blank_bishop(player_num), 26 | knight: Piece::blank_knight(player_num), 27 | rook: Piece::blank_rook(player_num), 28 | pawn: Piece::blank_pawn(player_num), 29 | custom: Vec::new(), 30 | player_num 31 | } 32 | } 33 | 34 | pub fn piece_at(&mut self, index:usize) -> Option<&mut Piece> { 35 | if self.king.bitboard.bit(index).unwrap(){ 36 | Some(&mut self.king) 37 | }else if self.queen.bitboard.bit(index).unwrap(){ 38 | Some(&mut self.queen) 39 | }else if self.bishop.bitboard.bit(index).unwrap(){ 40 | Some(&mut self.bishop) 41 | }else if self.knight.bitboard.bit(index).unwrap(){ 42 | Some(&mut self.knight) 43 | }else if self.rook.bitboard.bit(index).unwrap(){ 44 | Some(&mut self.rook) 45 | }else if self.pawn.bitboard.bit(index).unwrap(){ 46 | Some(&mut self.pawn) 47 | }else{ 48 | for p in self.custom.iter_mut(){ 49 | if p.bitboard.bit(index).unwrap(){ 50 | return Some(p); 51 | } 52 | } 53 | None 54 | } 55 | } 56 | 57 | pub fn get_piece_refs(&self) -> Vec<&Piece> { 58 | let mut return_vec = Vec::with_capacity(6); 59 | return_vec.push(&self.king); 60 | return_vec.push(&self.queen); 61 | return_vec.push(&self.bishop); 62 | return_vec.push(&self.knight); 63 | return_vec.push(&self.rook); 64 | return_vec.push(&self.pawn); 65 | for p in &self.custom { 66 | return_vec.push(p); 67 | } 68 | return_vec 69 | } 70 | 71 | //Recomputes occupied bb 72 | pub fn update_occupied(&mut self){ 73 | self.occupied = Bitboard::zero(); 74 | self.occupied |= &self.king.bitboard; 75 | self.occupied |= &self.queen.bitboard; 76 | self.occupied |= &self.bishop.bitboard; 77 | self.occupied |= &self.knight.bitboard; 78 | self.occupied |= &self.rook.bitboard; 79 | self.occupied |= &self.pawn.bitboard; 80 | for p in &self.custom { 81 | self.occupied |= &p.bitboard; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /protochess-server-rs/src/client_message.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use crate::room_manager::RoomInfo; 3 | use protochess_common::{GameState, Piece, Turn}; 4 | 5 | /// Public facing API 6 | /// Message from the server to client 7 | #[derive(Clone, Serialize, Deserialize, Debug)] 8 | #[serde(tag = "type", content="content")] 9 | pub enum ClientResponse { 10 | RoomCreateSuccess(String), 11 | RemovedFromRoom, 12 | RoomList(Vec), 13 | CannotOverwriteRoom, 14 | NoRoomFound, 15 | ChatMessage { 16 | from: String, 17 | content: String 18 | }, 19 | GameInfo { 20 | editable: bool, 21 | winner: Option, 22 | to_move_in_check: bool, 23 | in_check_kings: Option>, 24 | last_turn: Option, 25 | state: GameState 26 | }, 27 | PlayerList{ 28 | player_num: u8, 29 | you: String, 30 | names: Vec 31 | }, 32 | MovesFrom{ 33 | from: (u8, u8), 34 | to: Vec<(u8, u8)> 35 | } 36 | } 37 | 38 | /// Message from client to server 39 | #[derive(Serialize, Deserialize, Debug)] 40 | #[serde(tag = "type", content ="content")] 41 | pub enum ClientRequest { 42 | ListRooms, 43 | CreateRoom{ 44 | allow_edits: bool, 45 | is_public: bool, 46 | init_game_state: GameState 47 | }, 48 | JoinRoom(String), 49 | LeaveRoom, 50 | ChatMessage(String), 51 | TakeTurn(Turn), 52 | //Moves from (x,y) 53 | MovesFrom(u8, u8), 54 | ListPlayers, 55 | SwitchLeader(u8), 56 | EditGameState(GameState), 57 | DisableEdits, 58 | GameState 59 | } 60 | 61 | 62 | #[cfg(test)] 63 | mod tests { 64 | use crate::client_message::{ClientRequest }; 65 | use crate::client_message::ClientResponse; 66 | use crate::client_message::Turn; 67 | use std::collections::HashMap; 68 | use serde_json::json; 69 | use uuid::Uuid; 70 | #[test] 71 | fn serde() { 72 | /* 73 | let lol = json!(ClientRequest::MovesFrom(0, 1)); 74 | println!("{}", lol); 75 | let lol = json!(ClientRequest::ChatMessage("lol".to_string())); 76 | println!("{}", lol); 77 | 78 | let lol = json!(ClientRequest::TakeTurn(Turn{ 79 | from: (0,0), 80 | to: (0,0), 81 | promote_to: None 82 | })); 83 | println!("{}", lol); 84 | 85 | let rgs = RequestGameState{ 86 | width: 0, 87 | height: 0, 88 | tiles: vec![], 89 | pieces: vec![], 90 | movement_patterns: HashMap::new() 91 | }; 92 | 93 | 94 | let lol = json!(ClientRequest::ListRooms); 95 | println!("{}", lol); 96 | 97 | 98 | let lol = json!(ClientRequest::JoinRoom("bruh".to_string())); 99 | println!("{}", lol); 100 | 101 | 102 | let eds = ClientRequest::LeaveRoom; 103 | let lol = json!(eds); 104 | 105 | println!("{}", lol); 106 | 107 | */ 108 | } 109 | } -------------------------------------------------------------------------------- /protochess-engine-rs/src/types/chess_move.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use crate::rankfile::to_rank_file; 3 | use crate::types::bitboard::from_index; 4 | 5 | #[derive(PartialEq)] 6 | pub enum MoveType { 7 | Quiet, 8 | Capture, 9 | QueensideCastle, 10 | KingsideCastle, 11 | Promotion, 12 | PromotionCapture, 13 | Null, 14 | } 15 | 16 | /// Stores a move in a u32 17 | ///0-7: from index:u8 18 | ///8-15: to index:u8 19 | ///16-23: target index:u8 20 | ///24-26 : movetype 21 | /// 000 = quiet 22 | /// 001 = capture 23 | /// 010 = castle 24 | /// 011 = promotion 25 | /// 100 = promotion-capture 26 | #[derive(PartialEq, Copy, Clone)] 27 | pub struct Move(u32, Option); 28 | 29 | impl Move { 30 | pub fn new(from:u8, to:u8, target_loc: Option, move_type:MoveType, promo:Option) -> Move{ 31 | Move( 32 | (from as u32) 33 | | (to as u32) << 8u32 34 | | 35 | { 36 | if let Some(tl) = target_loc { 37 | (tl as u32) << 16u32 38 | } else { 39 | 0 40 | } 41 | } 42 | | 43 | match move_type { 44 | MoveType::Quiet => {0} 45 | MoveType::Capture => {1u32 << 24} 46 | MoveType::KingsideCastle => {2u32 << 24} 47 | MoveType::QueensideCastle => {3u32 << 24} 48 | MoveType::Promotion => {4u32 << 24} 49 | MoveType::PromotionCapture => {5u32 << 24} 50 | MoveType::Null => {6u32 << 24} 51 | }, 52 | promo 53 | ) 54 | } 55 | 56 | pub fn null() -> Move { 57 | Move::new(0,0,None,MoveType::Null, None) 58 | } 59 | 60 | pub fn get_from(&self) -> u8{ 61 | (self.0 & 255u32) as u8 62 | } 63 | 64 | pub fn get_to(&self) -> u8{ 65 | ((self.0 >> 8) & 255u32) as u8 66 | } 67 | 68 | pub fn get_is_capture(&self) -> bool{ 69 | ((self.0 >> 24) & 1u32) != 0u32 70 | } 71 | 72 | pub fn get_move_type(&self) -> MoveType { 73 | match &self.0 >> 24 & 7u32 { 74 | 0 => { MoveType::Quiet } 75 | 1 => { MoveType::Capture } 76 | 2 => { MoveType::KingsideCastle } 77 | 3 => { MoveType::QueensideCastle } 78 | 4 => { MoveType::Promotion } 79 | 5 => { MoveType::PromotionCapture } 80 | 6 => { MoveType::Null } 81 | _ => { MoveType::Quiet } 82 | } 83 | } 84 | 85 | pub fn get_promotion_char(&self) -> Option { 86 | self.1 87 | } 88 | 89 | pub fn get_target(&self) -> u8 { 90 | ((self.0 >> 16) & 255u32) as u8 91 | } 92 | } 93 | 94 | impl fmt::Display for Move { 95 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 96 | let (x1, y1) = from_index(self.get_from() as usize); 97 | let (x2, y2) = from_index(self.get_to() as usize); 98 | write!(f, "(from: {}, to:{})", to_rank_file(x1, y1),to_rank_file(x2, y2)) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /protochess-front/src/pages/_layout.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 61 |
62 | 63 |
64 |
65 |
66 |
67 | 68 | 69 | 70 | 71 |

72 | Protochess by Raymond Tran. The source code is licensed 73 | GNU GPLv3. 74 |

75 |
76 |
-------------------------------------------------------------------------------- /protochess-engine-rs/src/move_generator/bitboard_moves.rs: -------------------------------------------------------------------------------- 1 | use crate::types::bitboard::{Bitboard}; 2 | use crate::types::chess_move::{Move, MoveType}; 3 | 4 | /// Iterator that converts a Bitboard of move possibilities to Moves 5 | pub struct BitboardMoves { 6 | pub(crate) enemies: Bitboard, //Enemies 7 | pub(crate) moves:Bitboard, //moveset for source piece 8 | pub(crate) source_index: u8, //Source piece index 9 | pub(crate) promotion_squares: Option, //Optional promotion squares for this piece 10 | pub(crate) promo_vals: Option>, //Optional promotable values for this piece 11 | current_promo_vals: Option>, //Internal; used as a copy of promovals for each sq 12 | } 13 | 14 | impl BitboardMoves { 15 | pub fn new(enemies:Bitboard, 16 | moves:Bitboard, 17 | source_index:u8, 18 | promotion_squares:Option, 19 | promo_vals:Option>) -> BitboardMoves{ 20 | BitboardMoves{ 21 | enemies, 22 | moves, 23 | source_index, 24 | promotion_squares, 25 | promo_vals, 26 | current_promo_vals:None, 27 | } 28 | } 29 | } 30 | 31 | impl Iterator for BitboardMoves { 32 | type Item = Move; 33 | 34 | fn next(&mut self) -> Option { 35 | if let Some(to) = self.moves.lowest_one() { 36 | let promo_here = { 37 | if let Some(promo_sqs) = &self.promotion_squares { 38 | promo_sqs.bit(to).unwrap() 39 | } else { 40 | false 41 | } 42 | }; 43 | let capture_here = { self.enemies.bit(to).unwrap() }; 44 | let move_type = { 45 | match (capture_here, promo_here) { 46 | (true, true) => { MoveType::PromotionCapture }, 47 | (true, false) => { MoveType::Capture }, 48 | (false, true) => { MoveType::Promotion }, 49 | (false, false) => { MoveType::Quiet }, 50 | } 51 | }; 52 | let target = {if capture_here {to} else {0}}; 53 | let promo_char = { 54 | if promo_here { 55 | //promotion, do not go next until we run out of promo options 56 | let promo_options = { 57 | if let Some(pv) = &mut self.current_promo_vals { 58 | pv 59 | }else{ 60 | self.current_promo_vals = (&self.promo_vals).to_owned(); 61 | self.current_promo_vals.as_mut().unwrap() 62 | } 63 | }; 64 | 65 | let next_char = promo_options.pop().unwrap(); 66 | //If we run out of promo options, we can go to the next square 67 | if promo_options.len() == 0 { 68 | //Reset current_promo_vals for the next time 69 | self.current_promo_vals = None; 70 | self.moves.set_bit(to, false); 71 | } 72 | //Unwrap intentionally here; want to panic if this goes wrong 73 | Some(next_char) 74 | }else{ 75 | //No promotion chars left, go to next after this 76 | self.moves.set_bit(to, false); 77 | None 78 | } 79 | }; 80 | Some(Move::new(self.source_index as u8, to as u8, Some(target as u8), move_type, promo_char)) 81 | } else { 82 | None 83 | } 84 | } 85 | } 86 | 87 | 88 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/z.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/rollup.config.js: -------------------------------------------------------------------------------- 1 | import copy from 'rollup-plugin-copy'; 2 | import autoPreprocess from 'svelte-preprocess'; 3 | import postcss from 'rollup-plugin-postcss'; 4 | import rust from '@wasm-tool/rollup-plugin-rust'; 5 | import svelte from 'rollup-plugin-svelte'; 6 | import resolve from '@rollup/plugin-node-resolve'; 7 | import commonjs from '@rollup/plugin-commonjs'; 8 | import livereload from 'rollup-plugin-livereload'; 9 | import { terser } from 'rollup-plugin-terser'; 10 | import del from 'del' 11 | 12 | const production = !process.env.ROLLUP_WATCH; 13 | const staticDir = 'static' 14 | const distDir = 'dist' 15 | const buildDir = `${distDir}/build` 16 | 17 | del.sync(distDir + '/**') 18 | const transform = bundledTransform; 19 | export default { 20 | input: 'src/main.js', 21 | output: { 22 | inlineDynamicImports: true, 23 | sourcemap: true, 24 | format: 'iife', 25 | name: 'app', 26 | file: `${buildDir}/bundle.js` 27 | }, 28 | plugins: [ 29 | copy({ 30 | targets: [ 31 | { src: [staticDir + "/*", "!*/(__index.html)"], dest: distDir }, 32 | { src: `${staticDir}/__index.html`, dest: distDir, rename: '__app.html', transform }, 33 | ], 34 | copyOnce: true, 35 | flatten: false 36 | }), 37 | svelte({ 38 | // enable run-time checks when not in production 39 | dev: !production, 40 | // we'll extract any component CSS out into 41 | // a separate file - better for performance 42 | css: css => { 43 | css.write(`${buildDir}/bundle.css`); 44 | }, 45 | preprocess: autoPreprocess(), 46 | emitCss: true 47 | }), 48 | 49 | // If you have external dependencies installed from 50 | // npm, you'll most likely need these plugins. In 51 | // some cases you'll need additional configuration - 52 | // consult the documentation for details: 53 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 54 | resolve({ 55 | browser: true, 56 | dedupe: ['svelte'] 57 | }), 58 | commonjs(), 59 | rust({debug: false, serverPath: "/build/"}), 60 | postcss({ 61 | extract: true, 62 | minimize: true, 63 | use: [ 64 | [ 65 | "sass", 66 | { 67 | includePaths: ["./node_modules", "./node_modules/bulma", "./src"], 68 | }, 69 | ], 70 | ], 71 | } 72 | ), 73 | 74 | // In dev mode, call `npm run start` once 75 | // the bundle has been generated 76 | !production && serve(), 77 | 78 | // Watch the `public` directory and refresh the 79 | // browser on changes when not in production 80 | !production && livereload(distDir), 81 | 82 | // If we're building for production (npm run build 83 | // instead of npm run dev), minify 84 | production && terser() 85 | ], 86 | watch: { 87 | clearScreen: false 88 | } 89 | }; 90 | 91 | function serve() { 92 | let started = false; 93 | return { 94 | writeBundle() { 95 | if (!started) { 96 | started = true; 97 | require('child_process').spawn('npm', ['run', 'serve'], { 98 | stdio: ['ignore', 'inherit', 'inherit'], 99 | shell: true 100 | }); 101 | } 102 | } 103 | }; 104 | } 105 | 106 | function bundledTransform(contents) { 107 | return contents.toString().replace('__SCRIPT__', ` 108 | 109 | `) 110 | } 111 | 112 | function dynamicTransform(contents) { 113 | return contents.toString().replace('__SCRIPT__', ` 114 | 115 | 116 | `) 117 | } 118 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/transposition_table/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::types::chess_move::Move; 2 | 3 | const TABLE_SIZE:usize = 1_500_000; 4 | const ENTRIES_PER_CLUSTER:usize = 4; 5 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 6 | pub enum EntryFlag{ 7 | ALPHA, 8 | EXACT, 9 | BETA, 10 | NULL, 11 | } 12 | 13 | #[derive(Clone, Copy, PartialEq)] 14 | pub struct Entry { 15 | pub key: u64, 16 | pub flag: EntryFlag, 17 | pub value: isize, 18 | pub move_: Move, 19 | pub depth: u8, 20 | pub ancient: bool 21 | } 22 | impl Entry { 23 | pub fn null() -> Entry { 24 | Entry { 25 | key: 0, 26 | flag: EntryFlag::NULL, 27 | value: 0, 28 | move_: Move::null(), 29 | depth: 0, 30 | ancient: true 31 | } 32 | } 33 | } 34 | 35 | pub struct Cluster { 36 | entries: [Entry; ENTRIES_PER_CLUSTER] 37 | } 38 | 39 | pub struct TranspositionTable { 40 | data: Vec 41 | } 42 | 43 | impl TranspositionTable { 44 | pub fn new() -> TranspositionTable { 45 | let mut data = Vec::with_capacity(TABLE_SIZE); 46 | for _ in 0..TABLE_SIZE { 47 | data.push(Cluster{ entries: [Entry::null(); ENTRIES_PER_CLUSTER] }) 48 | } 49 | TranspositionTable { 50 | data 51 | } 52 | } 53 | 54 | /// Sets all the entries in the table to ancient, allowing them to be rewritten 55 | /// before any new entries are rewritten 56 | pub fn set_ancient(&mut self) { 57 | for cluster in self.data.iter_mut() { 58 | for entry in cluster.entries.iter_mut() { 59 | entry.ancient = true; 60 | } 61 | } 62 | } 63 | 64 | /// Inserts a new Entry item into the transposition table 65 | pub fn insert(&mut self, zobrist_key:u64, entry: Entry){ 66 | let cluster = &mut self.data[zobrist_key as usize % TABLE_SIZE]; 67 | for i in 0..cluster.entries.len() { 68 | let tentry = cluster.entries[i]; 69 | if tentry.depth <= entry.depth && tentry.key == zobrist_key && tentry.flag != EntryFlag::NULL { 70 | //Replace existing 71 | cluster.entries[i] = entry; 72 | return 73 | } 74 | } 75 | 76 | let cluster:&mut Cluster = &mut self.data[zobrist_key as usize % TABLE_SIZE]; 77 | //Replace the entry with the lowest depth (prefer greater depth entries) 78 | let mut lowest_depth_and_ancient = u8::MAX; 79 | let mut lowest_depth_and_ancient_indx:i32 = -1; 80 | 81 | let mut lowest_depth = u8::MAX; 82 | let mut lowest_depth_index = 0; 83 | for i in 0..ENTRIES_PER_CLUSTER { 84 | if cluster.entries[i].ancient 85 | && cluster.entries[i].depth <= lowest_depth_and_ancient { 86 | lowest_depth_and_ancient = cluster.entries[i].depth; 87 | lowest_depth_and_ancient_indx = i as i32; 88 | } 89 | 90 | if cluster.entries[i].depth <= lowest_depth { 91 | lowest_depth = cluster.entries[i].depth; 92 | lowest_depth_index = i; 93 | } 94 | } 95 | 96 | if lowest_depth_and_ancient_indx != -1 { 97 | cluster.entries [lowest_depth_and_ancient_indx as usize] = entry; 98 | }else{ 99 | cluster.entries [lowest_depth_index] = entry; 100 | } 101 | } 102 | 103 | /// Returns a handle to an Entry in the table, if it exists 104 | pub fn retrieve(&mut self, zobrist_key:u64) -> Option<&Entry> { 105 | let cluster = &self.data[zobrist_key as usize % TABLE_SIZE]; 106 | for entry in &cluster.entries { 107 | if entry.key == zobrist_key && entry.flag != EntryFlag::NULL { 108 | return Some(entry) 109 | } 110 | } 111 | None 112 | } 113 | } -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/black/t.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/t.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-front/src/pages/singleplayer.svelte: -------------------------------------------------------------------------------- 1 | 71 | 79 | 80 | 81 | 82 |
83 |
84 | {engineStatus} 85 | 89 |
90 |
91 | 97 |
98 |
99 |
100 | 101 |
102 | requestEdits(e)} /> 103 |
104 |
105 |
106 | 107 | -------------------------------------------------------------------------------- /protochess-engine-rs/tests/custom_pieces.rs: -------------------------------------------------------------------------------- 1 | extern crate protochess_engine_rs; 2 | 3 | #[cfg(test)] 4 | mod custom_pieces { 5 | use protochess_engine_rs::{MovementPatternExternal, PieceType}; 6 | 7 | #[test] 8 | fn custom_pieces() { 9 | let mut pos = protochess_engine_rs::Position::default(); 10 | pos.register_piecetype('c',MovementPatternExternal { 11 | promotion_squares: None, 12 | promo_vals: None, 13 | attack_sliding_deltas: vec![vec![(1,1),(2,2)]], 14 | attack_jump_deltas: vec![], 15 | attack_north: false, 16 | attack_south: false, 17 | attack_east: false, 18 | attack_west: false, 19 | attack_northeast: false, 20 | attack_northwest: false, 21 | attack_southeast: false, 22 | attack_southwest: false, 23 | translate_jump_deltas: vec![], 24 | translate_sliding_deltas: vec![vec![(1,1),(2,2)]], 25 | translate_north: false, 26 | translate_south: false, 27 | translate_east: false, 28 | translate_west: false, 29 | translate_northeast: false, 30 | translate_northwest: false, 31 | translate_southeast: false, 32 | translate_southwest: false 33 | }); 34 | pos.register_piecetype('a',MovementPatternExternal { 35 | promotion_squares: None, 36 | promo_vals: None, 37 | attack_sliding_deltas: vec![vec![(1,1),(2,2)]], 38 | attack_jump_deltas: vec![], 39 | attack_north: false, 40 | attack_south: false, 41 | attack_east: false, 42 | attack_west: false, 43 | attack_northeast: false, 44 | attack_northwest: false, 45 | attack_southeast: false, 46 | attack_southwest: false, 47 | translate_jump_deltas: vec![], 48 | translate_sliding_deltas: vec![vec![(1,1),(2,2)]], 49 | translate_north: false, 50 | translate_south: false, 51 | translate_east: false, 52 | translate_west: false, 53 | translate_northeast: false, 54 | translate_northwest: false, 55 | translate_southeast: false, 56 | translate_southwest: false 57 | }); 58 | 59 | for thing in pos.get_char_movementpattern_map(){ 60 | println!("{:?}", thing); 61 | } 62 | 63 | 64 | let mut engine = protochess_engine_rs::Engine::default(); 65 | 66 | 67 | /* 68 | engine.register_piecetype('a',MovementPattern { 69 | promotion_squares: None, 70 | promo_vals: None, 71 | attack_sliding_deltas: vec![vec![(1,1),(2,2)]], 72 | attack_jump_deltas: vec![], 73 | attack_north: false, 74 | attack_south: false, 75 | attack_east: false, 76 | attack_west: false, 77 | attack_northeast: false, 78 | attack_northwest: false, 79 | attack_southeast: false, 80 | attack_southwest: false, 81 | translate_jump_deltas: vec![], 82 | translate_sliding_deltas: vec![vec![(1,1),(2,2)]], 83 | translate_north: false, 84 | translate_south: false, 85 | translate_east: false, 86 | translate_west: false, 87 | translate_northeast: false, 88 | translate_northwest: false, 89 | translate_southeast: false, 90 | translate_southwest: false 91 | }); 92 | 93 | */ 94 | println!("{}", engine.get_zobrist()); 95 | println!("BASE SCORE: {}", engine.get_score()); 96 | engine.add_piece(0, PieceType::Custom('a'), 0, 3); 97 | println!("NEW SCORE: {}", engine.get_score()); 98 | println!("{}", engine.to_string()); 99 | 100 | 101 | let mut ply = 0; 102 | engine.play_best_move(1); 103 | ply += 1; 104 | println!("PLY: {} Engine plays: \n", ply); 105 | println!("{}", engine.to_string()); 106 | println!("========================================"); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/position/zobrist_table.rs: -------------------------------------------------------------------------------- 1 | use crate::PieceType; 2 | use std::collections::HashMap; 3 | use rand::rngs::StdRng; 4 | use rand::{SeedableRng, Rng}; 5 | use crate::position::piece::Piece; 6 | 7 | //Holds the random numbers used in generating zobrist keys 8 | pub struct ZobristTable { 9 | //zobrist[player_num][piecetype][index] -> zobrist key 10 | //king = 0 11 | //queen = 1 12 | //rook = 2 13 | //bishop = 3 14 | //knight = 4 15 | //pawn = 5 16 | zobrist: Vec>>, 17 | //Map of piecetype -> Vec of length 256, one random # for each index for each playernum 18 | custom_zobrist: HashMap<(u8, PieceType), Vec>, 19 | ep_zobrist: Vec, 20 | white_to_move: u64, 21 | w_q_castle: u64, 22 | b_q_castle: u64, 23 | w_k_castle: u64, 24 | b_k_castle: u64, 25 | rng: StdRng 26 | } 27 | 28 | impl ZobristTable { 29 | pub fn new() -> ZobristTable { 30 | let mut rng = StdRng::seed_from_u64(5435651169991665628); 31 | let mut ep_zobrist = Vec::with_capacity(16); 32 | for _ in 0..=16 { 33 | ep_zobrist.push(rng.gen::()); 34 | } 35 | let mut zobrist = Vec::with_capacity(4); 36 | //TODO 2+ players(?) 37 | zobrist.push(Vec::new()); 38 | zobrist.push(Vec::new()); 39 | for i in 0..2 { 40 | for j in 0..6 { 41 | let mut randoms = Vec::with_capacity(256); 42 | for _ in 0..=255 { 43 | randoms.push(rng.gen::()); 44 | } 45 | zobrist[i].push(randoms) 46 | } 47 | } 48 | 49 | let table = ZobristTable{ 50 | zobrist, 51 | custom_zobrist: Default::default(), 52 | ep_zobrist, 53 | white_to_move: rng.gen::(), 54 | w_q_castle: rng.gen::(), 55 | b_q_castle: rng.gen::(), 56 | w_k_castle: rng.gen::(), 57 | b_k_castle: rng.gen::(), 58 | rng 59 | }; 60 | //Initialize the table with the default piece set in a repeatable way 61 | //Mostly for easier testing 62 | table 63 | } 64 | 65 | /// Zobrist for the player to move 66 | pub fn get_to_move_zobrist(&self, player_num: u8) -> u64 { 67 | self.white_to_move 68 | } 69 | 70 | pub fn get_castling_zobrist(&self, player_num: u8, kingside: bool) -> u64 { 71 | match (player_num, kingside) { 72 | (0, true) => {self.w_k_castle} 73 | (0, false) => {self.w_q_castle} 74 | (1, true) => {self.b_k_castle} 75 | (1, false) => {self.b_q_castle} 76 | _ => {0} 77 | } 78 | } 79 | 80 | pub fn get_zobrist_sq_from_pt(&self, pt: &PieceType, owner: u8, index: u8) -> u64 { 81 | match pt { 82 | PieceType::King => { 83 | self.zobrist[owner as usize][0][index as usize] 84 | } 85 | PieceType::Queen => { 86 | self.zobrist[owner as usize][1][index as usize] 87 | } 88 | PieceType::Rook => { 89 | self.zobrist[owner as usize][2][index as usize] 90 | } 91 | PieceType::Bishop => { 92 | self.zobrist[owner as usize][3][index as usize] 93 | } 94 | PieceType::Knight => { 95 | self.zobrist[owner as usize][4][index as usize] 96 | } 97 | PieceType::Pawn => { 98 | self.zobrist[owner as usize][5][index as usize] 99 | } 100 | PieceType::Custom(c) => { 101 | if !self.custom_zobrist.contains_key(&(owner, pt.to_owned())) { 102 | return 0; 103 | //self.register_piecetype(owner, pt); 104 | } 105 | self.custom_zobrist.get(&(owner, pt.to_owned())).unwrap()[index as usize] 106 | } 107 | } 108 | } 109 | 110 | pub fn get_zobrist_sq(&self, piece:&Piece, index:u8) -> u64 { 111 | self.get_zobrist_sq_from_pt(&piece.piece_type, piece.player_num, index) 112 | } 113 | 114 | pub fn get_ep_zobrist_file(&self, rank: u8) -> u64 { 115 | self.ep_zobrist[rank as usize] 116 | } 117 | 118 | //Registers a custom piece type 119 | pub fn register_piecetype(&mut self, player_num:u8, pt:&PieceType){ 120 | let randoms = self.make_randoms(); 121 | self.custom_zobrist.insert((player_num, pt.to_owned()), randoms); 122 | } 123 | 124 | fn make_randoms(&mut self) -> Vec { 125 | let mut randoms = Vec::with_capacity(256); 126 | for i in 0..=255 { 127 | randoms.push(self.rng.gen::()); 128 | } 129 | randoms 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /protochess-front/src/pages/chess/_WebChess.svelte: -------------------------------------------------------------------------------- 1 | 2 | 31 | 32 | 33 | 86 | 87 | 88 |
89 | {#if chatVisible} 90 |
91 | 92 |
93 | {/if} 94 | 95 |
96 |
97 | 103 | 104 |
105 | {#if $GameInfo.winner} 106 | 107 | 108 | 111 | {$GameInfo.winner} 112 | 113 | 114 | 117 | WINS! 118 | 119 | 120 | {/if} 121 |
122 | 123 | {#if mpVisible} 124 |
125 |
126 |

Movement Patterns

127 | 128 |
129 | 130 |
131 | {/if} 132 |
133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /protochess-front/src/components/ChessEditor/ChessRightControl.svelte: -------------------------------------------------------------------------------- 1 | 25 | 39 |
40 | 41 |
42 | 46 |
47 |
48 | 52 |
53 | {#if toolSelected === ToolType.PIECE} 54 |
55 | 56 |
57 | 61 |
62 |
63 | 67 |
68 |
69 |
70 | 74 |
75 |
76 | 80 |
81 |
82 | 86 |
87 |
88 | 92 |
93 |
94 | 98 |
99 |
100 | 104 |
105 |
106 | 110 |
111 | 112 | {#if pieceSelected === PieceType.CUSTOM} 113 |
114 | Custom Piece: 115 |
116 | {#each registeredChars as char} 117 |
customPieceCharSelected = char}> 120 |
Ready
121 |
122 | 123 |
124 |
125 | {/each} 126 | 127 | {#each unregisteredChars as char} 128 |
customPieceCharSelected = char}> 131 |
???
132 |
133 | 134 |
135 |
136 | {/each} 137 |
138 |
139 | 142 | {/if} 143 | {/if} 144 |
145 | -------------------------------------------------------------------------------- /protochess-front/static/images/chess_pieces/white/o.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /protochess-engine-rs/src/position/movement_pattern.rs: -------------------------------------------------------------------------------- 1 | use crate::types::bitboard::{Bitboard, to_index, from_index}; 2 | /// External variant of MovementPattern for public 3 | #[derive(Clone, Debug )] 4 | pub struct MovementPatternExternal { 5 | // Places where this piece can promote, as well as char codes for the promotion pieces 6 | pub promotion_squares: Option>, 7 | pub promo_vals: Option>, 8 | 9 | // Ways the piece can capture (but not move without capturing) 10 | pub attack_sliding_deltas: Vec>, 11 | pub attack_jump_deltas: Vec<(i8, i8)>, 12 | pub attack_north: bool, 13 | pub attack_south: bool, 14 | pub attack_east: bool, 15 | pub attack_west: bool, 16 | pub attack_northeast: bool, 17 | pub attack_northwest: bool, 18 | pub attack_southeast: bool, 19 | pub attack_southwest: bool, 20 | 21 | //Ways the piece can move (but not capture) 22 | pub translate_jump_deltas: Vec<(i8, i8)>, 23 | pub translate_sliding_deltas: Vec>, 24 | pub translate_north: bool, 25 | pub translate_south: bool, 26 | pub translate_east: bool, 27 | pub translate_west: bool, 28 | pub translate_northeast: bool, 29 | pub translate_northwest: bool, 30 | pub translate_southeast: bool, 31 | pub translate_southwest: bool, 32 | } 33 | /// MovementPattern describes how a custom piece can move 34 | /// Each sliding field expects a vec of vec, with the inner vec represnting a new "run" 35 | #[derive(Clone, Debug )] 36 | pub struct MovementPattern { 37 | // Places where this piece can promote, as well as char codes for the promotion pieces 38 | pub promotion_squares: Option, 39 | pub promo_vals: Option>, 40 | 41 | // Ways the piece can capture (but not move without capturing) 42 | pub attack_sliding_deltas: Vec>, 43 | pub attack_jump_deltas: Vec<(i8, i8)>, 44 | pub attack_north: bool, 45 | pub attack_south: bool, 46 | pub attack_east: bool, 47 | pub attack_west: bool, 48 | pub attack_northeast: bool, 49 | pub attack_northwest: bool, 50 | pub attack_southeast: bool, 51 | pub attack_southwest: bool, 52 | 53 | //Ways the piece can move (but not capture) 54 | pub translate_jump_deltas: Vec<(i8, i8)>, 55 | pub translate_sliding_deltas: Vec>, 56 | pub translate_north: bool, 57 | pub translate_south: bool, 58 | pub translate_east: bool, 59 | pub translate_west: bool, 60 | pub translate_northeast: bool, 61 | pub translate_northwest: bool, 62 | pub translate_southeast: bool, 63 | pub translate_southwest: bool, 64 | } 65 | 66 | impl MovementPattern { 67 | pub fn promotion_at(&self, index:usize) -> bool { 68 | if let Some(bb) = &self.promotion_squares { 69 | return bb.bit(index).unwrap() 70 | } 71 | false 72 | } 73 | } 74 | 75 | pub fn external_mp_to_internal(mpe: MovementPatternExternal) -> MovementPattern{ 76 | let promotion_squares = { 77 | if let Some(vec) = mpe.promotion_squares{ 78 | let mut bb = Bitboard::zero(); 79 | for (x, y) in vec { 80 | bb.set_bit(to_index(x, y), true); 81 | } 82 | Some(bb) 83 | }else{ 84 | None 85 | } 86 | }; 87 | MovementPattern{ 88 | promotion_squares, 89 | promo_vals: mpe.promo_vals, 90 | attack_sliding_deltas: mpe.attack_sliding_deltas, 91 | attack_jump_deltas: mpe.attack_jump_deltas, 92 | attack_north: mpe.attack_north, 93 | attack_south: mpe.attack_south, 94 | attack_east: mpe.attack_east, 95 | attack_west: mpe.attack_west, 96 | attack_northeast: mpe.attack_northeast, 97 | attack_northwest: mpe.attack_northwest, 98 | attack_southeast: mpe.attack_southeast, 99 | attack_southwest: mpe.attack_southwest, 100 | translate_jump_deltas: mpe.translate_jump_deltas, 101 | translate_sliding_deltas: mpe.translate_sliding_deltas, 102 | translate_north: mpe.translate_north, 103 | translate_south: mpe.translate_south, 104 | translate_east: mpe.translate_east, 105 | translate_west: mpe.translate_west, 106 | translate_northeast: mpe.translate_northeast, 107 | translate_northwest: mpe.translate_northwest, 108 | translate_southeast: mpe.translate_southeast, 109 | translate_southwest: mpe.translate_southwest 110 | } 111 | } 112 | 113 | pub fn internal_mp_to_external(mp: MovementPattern) -> MovementPatternExternal { 114 | let promotion_squares = { 115 | let mut sq = Vec::new(); 116 | if let Some(mut bb) = mp.promotion_squares { 117 | while bb.is_zero() { 118 | let index = bb.lowest_one().unwrap(); 119 | sq.push(from_index(index)); 120 | bb.set_bit(index, false); 121 | } 122 | if sq.len() != 0 { 123 | Some(sq) 124 | }else{ 125 | None 126 | } 127 | } else { 128 | None 129 | } 130 | }; 131 | MovementPatternExternal { 132 | promotion_squares, 133 | promo_vals: mp.promo_vals, 134 | attack_sliding_deltas: mp.attack_sliding_deltas, 135 | attack_jump_deltas: mp.attack_jump_deltas, 136 | attack_north: mp.attack_north, 137 | attack_south: mp.attack_south, 138 | attack_east: mp.attack_east, 139 | attack_west: mp.attack_west, 140 | attack_northeast: mp.attack_northeast, 141 | attack_northwest: mp.attack_northwest, 142 | attack_southeast: mp.attack_southeast, 143 | attack_southwest: mp.attack_southwest, 144 | translate_jump_deltas: mp.translate_jump_deltas, 145 | translate_sliding_deltas: mp.translate_sliding_deltas, 146 | translate_north: mp.translate_north, 147 | translate_south: mp.translate_south, 148 | translate_east: mp.translate_east, 149 | translate_west: mp.translate_west, 150 | translate_northeast: mp.translate_northeast, 151 | translate_northwest: mp.translate_northwest, 152 | translate_southeast: mp.translate_southeast, 153 | translate_southwest: mp.translate_southwest 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /protochess-front/src/components/ChessEditor/PieceRightControl.svelte: -------------------------------------------------------------------------------- 1 | 11 | 21 |
22 | 23 |
24 |
25 | Jumps: 26 | 30 | 31 | 35 |
36 |
37 | Slides: 38 | 42 | 43 | 47 | {#if toolSelected === ToolType.ATTACK_SLIDE} 48 | 49 | {/if} 50 | {#if toolSelected === ToolType.TRANSLATE_SLIDE} 51 | 52 | {/if} 53 |
54 |
55 |
56 |
57 |
58 | 59 |
60 | 64 | 68 |
69 |
70 |
71 | 72 |
73 | 77 | 81 |
82 |
83 | 84 |
85 | 86 |
87 | 91 | 95 |
96 |
97 | 98 |
99 | 100 |
101 | 105 | 109 |
110 |
111 |
112 | 113 |
114 | 118 | 122 |
123 |
124 | 125 |
126 | 127 |
128 | 132 | 136 |
137 |
138 | 139 |
140 | 141 |
142 | 146 | 150 |
151 |
152 | 153 |
154 | 155 |
156 | 160 | 164 |
165 |
166 |
167 | -------------------------------------------------------------------------------- /protochess-front/src/components/MovementPatternDisplay/MovementPatternDisplay.svelte: -------------------------------------------------------------------------------- 1 | 161 | 162 | 163 | 164 | 173 | -------------------------------------------------------------------------------- /protochess-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use protochess_engine_rs::{Position, MovementPatternExternal}; 3 | use std::collections::HashMap; 4 | 5 | #[derive(Clone, Serialize, Deserialize, Debug)] 6 | pub struct Slides { 7 | pub north: bool, 8 | pub east: bool, 9 | pub south: bool, 10 | pub west: bool, 11 | pub northeast: bool, 12 | pub northwest: bool, 13 | pub southeast: bool, 14 | pub southwest: bool, 15 | } 16 | 17 | #[derive(Clone, Serialize, Deserialize, Debug)] 18 | #[serde(rename_all = "camelCase")] 19 | pub struct MovementPattern { 20 | pub attack_slides: Slides, 21 | pub translate_slides: Slides, 22 | pub attack_jumps:Vec<(i8, i8)>, 23 | pub translate_jumps: Vec<(i8, i8)>, 24 | pub attack_slide_deltas: Vec>, 25 | pub translate_slide_deltas: Vec>, 26 | } 27 | 28 | #[derive(Clone, Serialize, Deserialize, Debug)] 29 | pub struct Turn { 30 | pub promote_to: Option, 31 | pub from: (u8,u8), 32 | pub to: (u8, u8) 33 | } 34 | 35 | #[derive(Clone, Serialize, Deserialize, Debug)] 36 | pub struct Tile { 37 | pub x: u8, 38 | pub y: u8, 39 | pub tile_type: char 40 | } 41 | 42 | #[derive(Clone, Serialize, Deserialize, Debug)] 43 | pub struct Piece { 44 | pub owner: u8, 45 | pub x: u8, 46 | pub y: u8, 47 | pub piece_type: char 48 | } 49 | 50 | #[derive(Clone, Serialize, Deserialize, Debug)] 51 | pub struct GameState { 52 | pub width: u8, 53 | pub height: u8, 54 | pub to_move: u8, 55 | pub tiles: Vec, 56 | pub pieces: Vec, 57 | pub movement_patterns: HashMap, 58 | } 59 | 60 | pub fn serialize_game_state(position: &Position) -> GameState { 61 | let pieces = position.pieces_as_tuples() 62 | .into_iter() 63 | .map(|(owner, x, y, piece_type)| { 64 | Piece{ 65 | owner, 66 | x, 67 | y, 68 | piece_type 69 | } 70 | }) 71 | .collect(); 72 | let tiles = position.tiles_as_tuples() 73 | .into_iter() 74 | .map(|(x, y, tile_type)|{ 75 | Tile{ 76 | x, 77 | y, 78 | tile_type 79 | } 80 | }) 81 | .collect(); 82 | let to_move = position.whos_turn; 83 | let movement_patterns = { 84 | let mut temp = HashMap::new(); 85 | for (k, v) in position.get_char_movementpattern_map() { 86 | temp.insert(k, MovementPattern{ 87 | attack_slides: Slides { 88 | north: v.attack_north, 89 | east: v.attack_east, 90 | south: v.attack_south, 91 | west: v.attack_west, 92 | northeast: v.attack_northeast, 93 | northwest: v.attack_northwest, 94 | southeast: v.attack_southeast, 95 | southwest: v.attack_southwest 96 | }, 97 | translate_slides: Slides { 98 | north: v.translate_north, 99 | east: v.translate_east, 100 | south: v.translate_south, 101 | west: v.translate_west, 102 | northeast: v.translate_northeast, 103 | northwest: v.translate_northwest, 104 | southeast: v.translate_southeast, 105 | southwest: v.translate_southwest 106 | }, 107 | attack_jumps: v.attack_jump_deltas, 108 | translate_jumps: v.translate_jump_deltas, 109 | attack_slide_deltas: v.attack_sliding_deltas, 110 | translate_slide_deltas:v.translate_sliding_deltas 111 | }); 112 | } 113 | temp 114 | }; 115 | 116 | GameState { 117 | width: position.dimensions.width, 118 | height: position.dimensions.height, 119 | to_move, 120 | tiles, 121 | pieces, 122 | movement_patterns 123 | } 124 | } 125 | 126 | pub fn validate_gamestate_request(tiles:Vec, pieces:Vec, movement_patterns:HashMap) 127 | -> Option<(HashMap, Vec<(u8, u8)>, Vec<(u8, u8, u8, char)>)>{ 128 | let mut w_has_king = false; 129 | let mut b_has_king = false; 130 | for pce in &pieces { 131 | if pce.piece_type == 'k' { 132 | if pce.owner == 0 { 133 | w_has_king = true; 134 | }else { 135 | b_has_king = true; 136 | } 137 | } 138 | } 139 | 140 | if w_has_king && b_has_king { 141 | let valid_squares = tiles 142 | .into_iter() 143 | .filter(|sq| sq.x < 16 && sq.y < 16 && (sq.tile_type == 'w' || sq.tile_type == 'b')) 144 | .map(|sq| (sq.x, sq.y)) 145 | .collect(); 146 | 147 | let mut movements = HashMap::new(); 148 | for (key, val) in movement_patterns { 149 | let piece_type_char = key.to_ascii_lowercase(); 150 | if piece_type_char.is_ascii_alphabetic(){ 151 | movements.insert(piece_type_char, protochess_engine_rs::MovementPatternExternal{ 152 | promotion_squares: None, 153 | promo_vals: None, 154 | attack_sliding_deltas: val.attack_slide_deltas, 155 | attack_jump_deltas: val.attack_jumps, 156 | attack_north: val.attack_slides.north, 157 | attack_south: val.attack_slides.south, 158 | attack_east: val.attack_slides.east, 159 | attack_west: val.attack_slides.west, 160 | attack_northeast: val.attack_slides.northeast, 161 | attack_northwest: val.attack_slides.northwest, 162 | attack_southeast: val.attack_slides.southeast, 163 | attack_southwest: val.attack_slides.southwest, 164 | translate_jump_deltas: val.translate_jumps, 165 | translate_sliding_deltas: val.translate_slide_deltas, 166 | translate_north: val.translate_slides.north, 167 | translate_south: val.translate_slides.south, 168 | translate_east: val.translate_slides.east, 169 | translate_west: val.translate_slides.west, 170 | translate_northeast: val.translate_slides.northeast, 171 | translate_northwest: val.translate_slides.northwest, 172 | translate_southeast: val.translate_slides.southeast, 173 | translate_southwest: val.translate_slides.southwest 174 | }); 175 | } 176 | } 177 | 178 | let pieces = pieces.into_iter().map(|pce| (pce.owner, pce.x, pce.y, pce.piece_type)).collect(); 179 | 180 | return Some((movements, valid_squares, pieces)); 181 | } 182 | None 183 | } 184 | 185 | #[cfg(test)] 186 | mod tests { 187 | use std::collections::HashMap; 188 | use serde_json::json; 189 | use crate::GameState; 190 | #[test] 191 | fn serde() { 192 | let lol = (json!(GameState{ 193 | width:8, 194 | height:8, 195 | to_move:0, 196 | pieces: vec![], 197 | tiles: vec![], 198 | movement_patterns: HashMap::new() 199 | })); 200 | println!("{}", lol); 201 | } 202 | } 203 | --------------------------------------------------------------------------------