From cdb9cc29f43d98402a21dbadb3c1e6dad650f339 Mon Sep 17 00:00:00 2001 From: lsy Date: Tue, 5 Nov 2024 16:35:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AD=A3=E5=9C=A8=E5=86=99=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E6=A3=8B=E5=AD=90=E7=9A=84=E8=B5=B0=E5=90=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/chess/Cargo.toml | 1 + web/chess/package.json | 29 +++++ web/chess/src/App.css | 14 ++- web/chess/src/App.tsx | 148 ++++++++++++++++++++--- web/chess/src/lib.rs | 262 ++++++++++++++++++++++++++++++++--------- 5 files changed, 377 insertions(+), 77 deletions(-) create mode 100644 web/chess/package.json diff --git a/web/chess/Cargo.toml b/web/chess/Cargo.toml index 3ff2042..82dc460 100644 --- a/web/chess/Cargo.toml +++ b/web/chess/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] wasm-bindgen = "0.2.95" +wasm-bindgen-futures = "0.4.45" [lib] crate-type = ["cdylib", "rlib"] diff --git a/web/chess/package.json b/web/chess/package.json new file mode 100644 index 0000000..10138e8 --- /dev/null +++ b/web/chess/package.json @@ -0,0 +1,29 @@ +{ + "name": "chess", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", + "eslint": "^9.13.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.13", + "globals": "^15.11.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.10.0", + "vite": "^5.4.9" + } +} diff --git a/web/chess/src/App.css b/web/chess/src/App.css index b231d3c..78bdcdc 100644 --- a/web/chess/src/App.css +++ b/web/chess/src/App.css @@ -1,11 +1,11 @@ -.chessBoard{ +.homeMenu{ position: absolute; left: 0; top: 0; right: 0; bottom: 0; margin: auto; - width: 400px; + width: 480px; height: 480px; border: 5px solid #BF9970; border-radius: 10px; @@ -81,4 +81,14 @@ right: 5%; bottom: 5%; cursor: pointer; +} +#chessBoardCanvas{ + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + margin: auto; + border: 5px solid #BF9970; + border-radius: 10px; } \ No newline at end of file diff --git a/web/chess/src/App.tsx b/web/chess/src/App.tsx index 32635b9..7559f20 100644 --- a/web/chess/src/App.tsx +++ b/web/chess/src/App.tsx @@ -1,5 +1,5 @@ import init, {ChessGame} from '../pkg'; -import React, {useContext, useEffect, useState, createContext, useRef} from "react"; // 确保路径正确 +import React, {createContext, useContext, useEffect, useState} from "react"; // 确保路径正确 import "./App.css" const chessGameContext = createContext<{ @@ -52,41 +52,74 @@ const App: React.FC = () => { const Home: React.FC = () => { const [configureState, setConfigureState] = useState(false); const {setGameState} = useContext(gameStateContext); + const {setChessGame} = useContext(chessGameContext); const handleClick = () => { setConfigureState(true); }; + const dataSubmit = () => { + const modeElemet=document.getElementById('mode') as HTMLSelectElement; + const roundDurationElemet=document.getElementById('roundDuration') as HTMLSelectElement; + const matchDurationElemet=document.getElementById('matchDuration') as HTMLSelectElement; + const currentElment=document.getElementById('current') as HTMLSelectElement; + if (modeElemet && roundDurationElemet && matchDurationElemet && currentElment ) { + const mode=modeElemet.value; + const current=currentElment.value; + const roundDuration=Number(roundDurationElemet); + const matchDuration=Number(roundDurationElemet.value); + if(matchDuration!=0&&matchDuration 配置菜单 - {setConfigureState(false)}}>✕ + { + setConfigureState(false) + }}>✕
模式 - + + + - 每轮时间 + 每轮时间 - 每位玩家分钟数 - + + + + + 谁先玩 - + + +
- 开始 + 开始 ) : ( -
+

国际象棋

  • 创建对局
  • @@ -96,8 +129,85 @@ const Home: React.FC = () => { ) }; -const Game: React.FC = () => { - return (
    游戏
    ) +function drawChess(chessGame:ChessGame|undefined) { + const canvas = document.getElementById('chessBoardCanvas') as HTMLCanvasElement; + const chessWidth=60 + const chessHeight=60 + canvas.width = chessWidth*8; + canvas.height = chessHeight*8; + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + ctx.beginPath(); + ctx.strokeStyle="#BF9970"; + for (let i = 1; i < 8; i++) { + ctx.moveTo(i * chessWidth, 0); + ctx.lineTo(i * chessWidth, canvas.height); + } + for (let i = 1; i < 8; i++) { + ctx.moveTo(0, i * chessHeight); + ctx.lineTo(canvas.width,i * chessHeight); + } + const loadImagePromises: Promise[] = []; + const chess_all= chessGame?.get_chessboard() as Array>; ; + chess_all.forEach((chess,index) => { + const row=Math.floor(index/8); + const line=index-row*8; + let color; + const depth="#F0D9B5"; + const deep="#B58863"; + if (row%2==0){ + color=line%2==0?depth:deep; + }else{ + color=line%2==1?depth:deep; + } + + ctx.fillStyle = color; + ctx.fillRect(chessWidth*line, row*chessHeight, chessWidth, chessHeight); + if (chess[0]!="none"){ + const img = new Image(); + img.src = `/chess/${chess[1]}_${chess[0]}.svg`; + + const imgPromise=new Promise(resolve => { + img.onload = () => { + ctx.drawImage(img, chessWidth*line, row*chessHeight, chessWidth, chessHeight); + resolve(); + }; + }) + loadImagePromises.push(imgPromise); + } + }) + Promise.all(loadImagePromises).then(() => { + ctx.stroke(); + }) + } + +const Game: React.FC = () => { + const {chessGame} = useContext(chessGameContext); + const [movestate,setMoveState] = useState(false); + const [movechess,setMoveChess] = useState(Array); + + useEffect(()=>{ + drawChess(chessGame); + },[]) + function chessBoardClick(e: React.MouseEvent) { + const client=e.currentTarget.getBoundingClientRect(); + const clickX = e.clientX-client.x; + const clickY = e.clientY-client.y; + const row=Math.floor(clickX/60); + const line=Math.floor(clickY/60); + const index=row+line*8; + console.log(row,line,chessGame?.get_chess_action(index)); + + // if (result){ + // + // }else{ + // const result=chessGame?.get_chess(index); + // } + + } + return () +} + + export default App; \ No newline at end of file diff --git a/web/chess/src/lib.rs b/web/chess/src/lib.rs index 06186a3..4564740 100644 --- a/web/chess/src/lib.rs +++ b/web/chess/src/lib.rs @@ -1,7 +1,10 @@ -use std::fmt; use std::collections::HashMap; +use std::fmt; use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::js_sys; +const CHESS_LINE: u32 = 8; +const CHESS_ROW: u32 = 8; enum Grade { King, @@ -14,15 +17,15 @@ enum Grade { } impl Grade { - fn as_str(&self) -> &'static str { + fn to_string(&self) -> String { match self { - Grade::King => "king", - Grade::Queen => "queen", - Grade::Rook => "rook", - Grade::Bishop => "bishop", - Grade::Knight => "knight", - Grade::Pawn => "pawn", - Grade::None => "None" + Grade::King => String::from("king"), + Grade::Queen => String::from("queen"), + Grade::Rook => String::from("rook"), + Grade::Bishop => String::from("bishop"), + Grade::Knight => String::from("knight"), + Grade::Pawn => String::from("pawn"), + Grade::None => String::from("none"), } } } @@ -31,24 +34,26 @@ impl Grade { enum Camp { Black, White, + None, } impl Camp { - fn as_str(&self) -> &'static str { + fn to_string(&self) -> String { match self { - Camp::Black => "black", - Camp::White => "white", + Camp::Black => String::from("black"), + Camp::White => String::from("white"), + Camp::None => String::from("none"), } } fn from_string(s: String) -> Camp { + let s = s.to_lowercase(); match s.as_str() { "black" => Camp::Black, "white" => Camp::White, - _ => Camp::White, + _ => Camp::None, } } } - struct Chessman { grade: Grade, camp: Camp, @@ -56,44 +61,102 @@ struct Chessman { struct Player { username: String, - score: u32, + time: u32, } +enum Mode { + Local, + Online, + Robot, +} +impl Mode { + pub fn as_string(&self) -> String { + match self { + Mode::Local => "local".to_string(), + Mode::Online => "online".to_string(), + Mode::Robot => "robot".to_string(), + } + } + pub fn from_string(s: String) -> Mode { + let s = s.to_lowercase(); + match s.as_str() { + "local" => Mode::Local, + "online" => Mode::Online, + "robot" => Mode::Robot, + _ => Mode::Local, + } + } +} +#[wasm_bindgen] +pub struct Info { + current: Camp, + winner: Camp, + round_duration: u32, + match_duration: u32, + mode: Mode, +} #[wasm_bindgen] pub struct ChessGame { players: HashMap, chessboard: Vec, - current: Camp, + info: Info, } impl fmt::Display for ChessGame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for line in self.chessboard.as_slice().chunks(8) { + for line in self.chessboard.as_slice().chunks(CHESS_ROW as usize) { for square in line { write!(f, " ")?; match square.camp { - Camp::White => { - match &square.grade { - Grade::King => { write!(f, "♔")?; } - Grade::Queen => { write!(f, "♕")?; } - Grade::Rook => { write!(f, "♖")?; } - Grade::Bishop => { write!(f, "♗")?; } - Grade::Knight => { write!(f, "♘")?; } - Grade::Pawn => { write!(f, "♙")?; } - Grade::None => { write!(f, "※")?; } + Camp::White => match &square.grade { + Grade::King => { + write!(f, "♔")?; } - } - Camp::Black => { - match &square.grade { - Grade::King => { write!(f, "♚")?; } - Grade::Queen => { write!(f, "♛")?; } - Grade::Rook => { write!(f, "♜")?; } - Grade::Bishop => { write!(f, "♝")?; } - Grade::Knight => { write!(f, "♞")?; } - Grade::Pawn => { write!(f, "♟")?; } - Grade::None => { write!(f, "※")?; } + Grade::Queen => { + write!(f, "♕")?; } + Grade::Rook => { + write!(f, "♖")?; + } + Grade::Bishop => { + write!(f, "♗")?; + } + Grade::Knight => { + write!(f, "♘")?; + } + Grade::Pawn => { + write!(f, "♙")?; + } + Grade::None => { + write!(f, "※")?; + } + }, + Camp::Black => match &square.grade { + Grade::King => { + write!(f, "♚")?; + } + Grade::Queen => { + write!(f, "♛")?; + } + Grade::Rook => { + write!(f, "♜")?; + } + Grade::Bishop => { + write!(f, "♝")?; + } + Grade::Knight => { + write!(f, "♞")?; + } + Grade::Pawn => { + write!(f, "♟")?; + } + Grade::None => { + write!(f, "※")?; + } + }, + Camp::None => { + write!(f, "※")?; } } } @@ -105,7 +168,12 @@ impl fmt::Display for ChessGame { #[wasm_bindgen] impl ChessGame { - pub fn new(current: String) -> ChessGame { + pub fn new( + current: String, + round_duration: u32, + match_duration: u32, + mode: String, + ) -> ChessGame { fn level_position(index: u8) -> Grade { match index { 0 | 7 => Grade::Rook, @@ -113,27 +181,79 @@ impl ChessGame { 2 | 5 => Grade::Bishop, 3 => Grade::King, 4 => Grade::Queen, - _ => Grade::None + _ => Grade::None, } } - let chess_game = (0..64).map(|i| { - let row = i / 8; - let color = if row == 0 || row == 1 { Camp::Black } else { Camp::White }; - if row == 1 || row == 6 { - return Chessman { grade: Grade::Pawn, camp: color }; - } else if row == 0 || row == 7 { - return Chessman { grade: level_position(i % 8), camp: color } - } else { - Chessman { grade: Grade::None, camp: color } - } - }).collect(); + let chess_game = (0..CHESS_LINE*CHESS_ROW) + .map(|i| { + let row = i / CHESS_LINE; + let color = if row == 0 || row == 1 { + Camp::White + } else if row == 6 || row == 7 { + Camp::Black + } else { + Camp::None + }; + if row == 1 || row == 6 { + return Chessman { + grade: Grade::Pawn, + camp: color, + }; + } else if row == 0 || row == 7 { + return Chessman { + grade: level_position((i % CHESS_ROW) as u8), + camp: color, + }; + } else { + Chessman { + grade: Grade::None, + camp: color, + } + } + }) + .collect(); let mut players = HashMap::new(); - players.insert(Camp::Black, Player { username: String::from("黑色玩家"), score: 0 }); - players.insert(Camp::White, Player { username: String::from("白色玩家"), score: 0 }); + players.insert( + Camp::Black, + Player { + username: String::from("黑色玩家"), + time: 0, + }, + ); + players.insert( + Camp::White, + Player { + username: String::from("白色玩家"), + time: 0, + }, + ); + let mut current_convert: Camp; + if current.to_string() == "black" || current.to_string() == "white" { + current_convert = Camp::from_string(current); + } else { + if js_sys::Math::random() < 0.5 { + current_convert = Camp::Black; + } else { + current_convert = Camp::White; + } + } - ChessGame { current: Camp::from_string(current), players, chessboard: chess_game } + let info = Info { + current: current_convert, + winner: Camp::None, + round_duration, + match_duration, + mode: Mode::from_string(mode), + }; + ChessGame { + players, + chessboard: chess_game, + info, + } + } + pub fn render(&self) -> String { + self.to_string() } - pub fn render(&self) -> String { self.to_string() } } #[wasm_bindgen] @@ -141,9 +261,39 @@ impl ChessGame { pub fn get_chessboard(&self) -> Vec { let mut chessboard = Vec::new(); for i in self.chessboard.iter() { - let chess = vec![i.grade.as_str().to_string(), i.camp.as_str().to_string()]; + let chess = vec![i.grade.to_string(), i.camp.to_string()]; chessboard.push(JsValue::from(chess)); - }; + } chessboard } -} \ No newline at end of file + + pub fn get_chess_action(&self, index: u32) -> Vec { + let un_index: usize = index as usize; + if index >= self.chessboard.len() as u32 { + return vec![JsValue::from(vec!["999", "点击的范围超出的棋盘"])]; + } + let chess: &Chessman = &self.chessboard[un_index]; + if chess.camp != self.info.current { + return vec![JsValue::from(vec!["998", "不是该阵营移动"])]; + } + let line: u32 = index / CHESS_ROW ; + let row: u32 = index - line * CHESS_LINE ; + let mut available_locations: Vec = Vec::new(); + match chess.grade { + Grade::King => {} + Grade::Queen => {} + Grade::Rook => {} + Grade::Bishop => {} + Grade::Knight => {} + Grade::Pawn => { + if chess.camp == Camp::Black && line != 0 { + + } + }, + Grade::None => {} + } + + available_locations + } + +}