正在写预测棋子的走向

This commit is contained in:
lsy 2024-11-05 16:35:05 +08:00
parent 0f8dd04918
commit cdb9cc29f4
5 changed files with 377 additions and 77 deletions

View File

@ -5,6 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
wasm-bindgen = "0.2.95" wasm-bindgen = "0.2.95"
wasm-bindgen-futures = "0.4.45"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]

29
web/chess/package.json Normal file
View File

@ -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"
}
}

View File

@ -1,11 +1,11 @@
.chessBoard{ .homeMenu{
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
margin: auto; margin: auto;
width: 400px; width: 480px;
height: 480px; height: 480px;
border: 5px solid #BF9970; border: 5px solid #BF9970;
border-radius: 10px; border-radius: 10px;
@ -82,3 +82,13 @@
bottom: 5%; bottom: 5%;
cursor: pointer; cursor: pointer;
} }
#chessBoardCanvas{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
border: 5px solid #BF9970;
border-radius: 10px;
}

View File

@ -1,5 +1,5 @@
import init, {ChessGame} from '../pkg'; 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" import "./App.css"
const chessGameContext = createContext<{ const chessGameContext = createContext<{
@ -52,41 +52,74 @@ const App: React.FC = () => {
const Home: React.FC = () => { const Home: React.FC = () => {
const [configureState, setConfigureState] = useState<boolean>(false); const [configureState, setConfigureState] = useState<boolean>(false);
const {setGameState} = useContext(gameStateContext); const {setGameState} = useContext(gameStateContext);
const {setChessGame} = useContext(chessGameContext);
const handleClick = () => { const handleClick = () => {
setConfigureState(true); 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<roundDuration||(matchDuration!=0&&roundDuration===0)) {
alert("回合时长不能比对局时长长")
}
const chessGame=ChessGame.new(current,roundDuration,matchDuration,mode)
setChessGame(chessGame)
setGameState(false);
}
else{
alert("信息不完整")
}
}
return ( return (
configureState ? ( configureState ? (
<div className="configure"> <div className="configure">
<span className="title"></span> <span className="title"></span>
<span className="x" onClick={()=>{setConfigureState(false)}}></span> <span className="x" onClick={() => {
setConfigureState(false)
}}></span>
<div className="option"> <div className="option">
<span></span> <span></span>
<select> <select id={"mode"}>
<option>1</option> <option value={"local"}></option>
<option>2</option> <option value={"online"}>线</option>
<option value={"robot"}></option>
</select> </select>
<span></span> <span id={"roundDuration"}></span>
<select> <select>
<option>1</option> <option value={0}></option>
<option>2</option> <option value={30}>30</option>
<option value={60}>60</option>
<option value={90}>90</option>
<option value={120}>120</option>
</select> </select>
<span></span> <span></span>
<select> <select id={"matchDuration"}>
<option>1</option> <option value={0}></option>
<option>2</option> <option value={5 * 60}>5</option>
<option value={10 * 60}>10</option>
<option value={30 * 60}>30</option>
<option value={60 * 60}>60</option>
</select> </select>
<span></span> <span></span>
<select> <select id={"current"}>
<option>1</option> <option value={"black"}></option>
<option>2</option> <option value={"white"}></option>
<option value={"random"}></option>
</select> </select>
</div> </div>
<span className="start"></span> <span className="start" onClick={dataSubmit}></span>
</div> </div>
) : ( ) : (
<div className="homeMenu chessBoard"> <div className="homeMenu">
<h1></h1> <h1></h1>
<ul> <ul>
<li onClick={handleClick}></li> <li onClick={handleClick}></li>
@ -96,8 +129,85 @@ const Home: React.FC = () => {
) )
}; };
const Game: React.FC = () => { function drawChess(chessGame:ChessGame|undefined) {
return (<div></div>) 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<void>[] = [];
const chess_all= chessGame?.get_chessboard() as Array<Array<string>>; ;
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<void>(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<HTMLCanvasElement>) {
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 (<canvas id={"chessBoardCanvas"} onClick={chessBoardClick}></canvas>)
}
export default App; export default App;

View File

@ -1,7 +1,10 @@
use std::fmt;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::js_sys;
const CHESS_LINE: u32 = 8;
const CHESS_ROW: u32 = 8;
enum Grade { enum Grade {
King, King,
@ -14,15 +17,15 @@ enum Grade {
} }
impl Grade { impl Grade {
fn as_str(&self) -> &'static str { fn to_string(&self) -> String {
match self { match self {
Grade::King => "king", Grade::King => String::from("king"),
Grade::Queen => "queen", Grade::Queen => String::from("queen"),
Grade::Rook => "rook", Grade::Rook => String::from("rook"),
Grade::Bishop => "bishop", Grade::Bishop => String::from("bishop"),
Grade::Knight => "knight", Grade::Knight => String::from("knight"),
Grade::Pawn => "pawn", Grade::Pawn => String::from("pawn"),
Grade::None => "None" Grade::None => String::from("none"),
} }
} }
} }
@ -31,24 +34,26 @@ impl Grade {
enum Camp { enum Camp {
Black, Black,
White, White,
None,
} }
impl Camp { impl Camp {
fn as_str(&self) -> &'static str { fn to_string(&self) -> String {
match self { match self {
Camp::Black => "black", Camp::Black => String::from("black"),
Camp::White => "white", Camp::White => String::from("white"),
Camp::None => String::from("none"),
} }
} }
fn from_string(s: String) -> Camp { fn from_string(s: String) -> Camp {
let s = s.to_lowercase();
match s.as_str() { match s.as_str() {
"black" => Camp::Black, "black" => Camp::Black,
"white" => Camp::White, "white" => Camp::White,
_ => Camp::White, _ => Camp::None,
} }
} }
} }
struct Chessman { struct Chessman {
grade: Grade, grade: Grade,
camp: Camp, camp: Camp,
@ -56,44 +61,102 @@ struct Chessman {
struct Player { struct Player {
username: String, 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] #[wasm_bindgen]
pub struct ChessGame { pub struct ChessGame {
players: HashMap<Camp, Player>, players: HashMap<Camp, Player>,
chessboard: Vec<Chessman>, chessboard: Vec<Chessman>,
current: Camp, info: Info,
} }
impl fmt::Display for ChessGame { impl fmt::Display for ChessGame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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 { for square in line {
write!(f, " ")?; write!(f, " ")?;
match square.camp { match square.camp {
Camp::White => { Camp::White => match &square.grade {
match &square.grade { Grade::King => {
Grade::King => { write!(f, "")?; } 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, "")?;
} }
Camp::Black => { Grade::Rook => {
match &square.grade { write!(f, "")?;
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::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] #[wasm_bindgen]
impl ChessGame { 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 { fn level_position(index: u8) -> Grade {
match index { match index {
0 | 7 => Grade::Rook, 0 | 7 => Grade::Rook,
@ -113,27 +181,79 @@ impl ChessGame {
2 | 5 => Grade::Bishop, 2 | 5 => Grade::Bishop,
3 => Grade::King, 3 => Grade::King,
4 => Grade::Queen, 4 => Grade::Queen,
_ => Grade::None _ => Grade::None,
} }
} }
let chess_game = (0..64).map(|i| { let chess_game = (0..CHESS_LINE*CHESS_ROW)
let row = i / 8; .map(|i| {
let color = if row == 0 || row == 1 { Camp::Black } else { Camp::White }; let row = i / CHESS_LINE;
if row == 1 || row == 6 { let color = if row == 0 || row == 1 {
return Chessman { grade: Grade::Pawn, camp: color }; Camp::White
} else if row == 0 || row == 7 { } else if row == 6 || row == 7 {
return Chessman { grade: level_position(i % 8), camp: color } Camp::Black
} else { } else {
Chessman { grade: Grade::None, camp: color } 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(); }
})
.collect();
let mut players = HashMap::new(); let mut players = HashMap::new();
players.insert(Camp::Black, Player { username: String::from("黑色玩家"), score: 0 }); players.insert(
players.insert(Camp::White, Player { username: String::from("白色玩家"), score: 0 }); Camp::Black,
Player {
ChessGame { current: Camp::from_string(current), players, chessboard: chess_game } 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;
}
}
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] #[wasm_bindgen]
@ -141,9 +261,39 @@ impl ChessGame {
pub fn get_chessboard(&self) -> Vec<JsValue> { pub fn get_chessboard(&self) -> Vec<JsValue> {
let mut chessboard = Vec::new(); let mut chessboard = Vec::new();
for i in self.chessboard.iter() { 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.push(JsValue::from(chess));
}; }
chessboard chessboard
} }
pub fn get_chess_action(&self, index: u32) -> Vec<JsValue> {
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<JsValue> = 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
}
} }