正在写预测棋子的走向
This commit is contained in:
parent
0f8dd04918
commit
cdb9cc29f4
@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.95"
|
||||
wasm-bindgen-futures = "0.4.45"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
29
web/chess/package.json
Normal file
29
web/chess/package.json
Normal 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"
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -82,3 +82,13 @@
|
||||
bottom: 5%;
|
||||
cursor: pointer;
|
||||
}
|
||||
#chessBoardCanvas{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
border: 5px solid #BF9970;
|
||||
border-radius: 10px;
|
||||
}
|
@ -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<boolean>(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<roundDuration||(matchDuration!=0&&roundDuration===0)) {
|
||||
alert("回合时长不能比对局时长长")
|
||||
}
|
||||
const chessGame=ChessGame.new(current,roundDuration,matchDuration,mode)
|
||||
setChessGame(chessGame)
|
||||
setGameState(false);
|
||||
}
|
||||
else{
|
||||
alert("信息不完整")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
configureState ? (
|
||||
<div className="configure">
|
||||
<span className="title">配置菜单</span>
|
||||
<span className="x" onClick={()=>{setConfigureState(false)}}>✕</span>
|
||||
<span className="x" onClick={() => {
|
||||
setConfigureState(false)
|
||||
}}>✕</span>
|
||||
<div className="option">
|
||||
<span>模式</span>
|
||||
<select>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<select id={"mode"}>
|
||||
<option value={"local"}>本地对战</option>
|
||||
<option value={"online"}>在线对战</option>
|
||||
<option value={"robot"}>人机对战</option>
|
||||
</select>
|
||||
<span>每轮时间</span>
|
||||
<span id={"roundDuration"}>每轮时间</span>
|
||||
<select>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option value={0}>无限制</option>
|
||||
<option value={30}>30秒</option>
|
||||
<option value={60}>60秒</option>
|
||||
<option value={90}>90秒</option>
|
||||
<option value={120}>120秒</option>
|
||||
</select>
|
||||
<span>每位玩家分钟数</span>
|
||||
<select>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<span>总时长</span>
|
||||
<select id={"matchDuration"}>
|
||||
<option value={0}>无限制</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>
|
||||
<span>谁先玩</span>
|
||||
<select>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<select id={"current"}>
|
||||
<option value={"black"}>黑棋</option>
|
||||
<option value={"white"}>白棋</option>
|
||||
<option value={"random"}>随机</option>
|
||||
</select>
|
||||
</div>
|
||||
<span className="start">开始</span>
|
||||
<span className="start" onClick={dataSubmit}>开始</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="homeMenu chessBoard">
|
||||
<div className="homeMenu">
|
||||
<h1>国际象棋</h1>
|
||||
<ul>
|
||||
<li onClick={handleClick}>创建对局</li>
|
||||
@ -96,8 +129,85 @@ const Home: React.FC = () => {
|
||||
)
|
||||
};
|
||||
|
||||
const Game: React.FC = () => {
|
||||
return (<div>游戏</div>)
|
||||
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<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;
|
@ -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<Camp, Player>,
|
||||
chessboard: Vec<Chessman>,
|
||||
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, "♔")?;
|
||||
}
|
||||
Grade::Queen => {
|
||||
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::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 }
|
||||
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 {
|
||||
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();
|
||||
players.insert(Camp::Black, Player { username: String::from("黑色玩家"), score: 0 });
|
||||
players.insert(Camp::White, Player { username: String::from("白色玩家"), score: 0 });
|
||||
|
||||
ChessGame { current: Camp::from_string(current), players, chessboard: chess_game }
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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<JsValue> {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user