Add AI tile count and total tile counts, other UI tweaks
This commit is contained in:
parent
e7c46c8efd
commit
0d30ac0b46
6 changed files with 162 additions and 67 deletions
17
src/game.rs
17
src/game.rs
|
@ -361,6 +361,23 @@ impl Game {
|
|||
|
||||
}
|
||||
|
||||
pub fn get_remaining_tiles(&self) -> usize {
|
||||
self.tile_pool.len()
|
||||
}
|
||||
|
||||
pub fn get_player_tile_count(&self, player: &str) -> Result<usize, String> {
|
||||
let tray = match self.player_states.get_tray(&player) {
|
||||
None => {return Err(format!("Player {} not found", player))}
|
||||
Some(x) => {x}
|
||||
};
|
||||
|
||||
Ok(
|
||||
tray.letters.iter()
|
||||
.filter(|l| l.is_some())
|
||||
.count()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Tsify, Debug)]
|
||||
|
|
21
src/wasm.rs
21
src/wasm.rs
|
@ -155,4 +155,25 @@ impl GameWasm {
|
|||
self.0.current_player_name()
|
||||
}
|
||||
|
||||
pub fn get_remaining_tiles(&self) -> usize {
|
||||
self.0.get_remaining_tiles()
|
||||
}
|
||||
|
||||
pub fn get_player_tile_count(&self, player: &str) -> Result<JsValue, Error> {
|
||||
match self.0.get_player_tile_count(player) {
|
||||
Ok(count) => {
|
||||
Ok(serde_wasm_bindgen::to_value(&MyResult{
|
||||
response_type: ResponseType::OK,
|
||||
value: count,
|
||||
})?)
|
||||
},
|
||||
Err(msg) => {
|
||||
Ok(serde_wasm_bindgen::to_value(&MyResult{
|
||||
response_type: ResponseType::OK,
|
||||
value: msg,
|
||||
})?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
145
ui/src/Game.tsx
145
ui/src/Game.tsx
|
@ -108,7 +108,7 @@ export function Game(props: {
|
|||
function runAI() {
|
||||
const result: TurnAdvanceResult = props.wasm.advance_turn();
|
||||
if(result.type == "AIMove"){
|
||||
handlePlayerAction(result.action, "AI");
|
||||
handlePlayerAction(result.action, props.settings.aiName);
|
||||
} else {
|
||||
// this would be quite surprising
|
||||
console.error({result});
|
||||
|
@ -179,6 +179,21 @@ export function Game(props: {
|
|||
}
|
||||
}, [logInfo]);
|
||||
|
||||
const remainingTiles = useMemo(() => {
|
||||
return props.wasm.get_remaining_tiles();
|
||||
}, [turnCount]);
|
||||
|
||||
const remainingAITiles = useMemo(() => {
|
||||
let result = props.wasm.get_player_tile_count(props.settings.aiName) as MyResult<number | String>;
|
||||
if(result.response_type == "OK") {
|
||||
return result.value as number;
|
||||
} else {
|
||||
console.error(result.value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
}, [turnCount]);
|
||||
|
||||
function handlePlayerAction(action: TurnAction, playerName: string) {
|
||||
|
||||
if (action.type == "PlayTiles"){
|
||||
|
@ -213,73 +228,85 @@ export function Game(props: {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tray-row">
|
||||
<div>
|
||||
<div>
|
||||
{remainingTiles} letters remaining
|
||||
</div>
|
||||
<div>
|
||||
{props.settings.aiName} has {remainingAITiles} tiles
|
||||
</div>
|
||||
<button onClick={() => {
|
||||
trayDispatch({action: TileDispatchActionType.RETURN}); // want all tiles back on tray for tile exchange
|
||||
setIsTileExchangeOpen(true);
|
||||
}}>Open Tile Exchange</button>
|
||||
</div>
|
||||
|
||||
<TileTray letters={playerLetters} trayLength={props.settings.trayLength} dispatch={trayDispatch}/>
|
||||
<div className="player-controls">
|
||||
<button className="check" onClick={() => {
|
||||
const playedTiles = playerLetters.map((i) => {
|
||||
if (i === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
<TileTray letters={playerLetters} trayLength={props.settings.trayLength} dispatch={trayDispatch}/>
|
||||
<button onClick={() => {
|
||||
const playedTiles = playerLetters.map((i) => {
|
||||
if (i === undefined) {
|
||||
return null;
|
||||
}
|
||||
if (i.location === LocationType.GRID) {
|
||||
let result: PlayedTile = {
|
||||
index: i.index,
|
||||
character: undefined
|
||||
};
|
||||
if (i.is_blank) {
|
||||
result.character = i.text;
|
||||
}
|
||||
|
||||
if (i.location === LocationType.GRID) {
|
||||
let result: PlayedTile = {
|
||||
index: i.index,
|
||||
character: undefined
|
||||
};
|
||||
if (i.is_blank) {
|
||||
result.character = i.text;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const result: MyResult<{ type: "PlayTiles"; result: ScoreResult } | string> = props.wasm.receive_play(playedTiles, confirmedScorePoints > -1);
|
||||
console.log({result});
|
||||
|
||||
if(result.response_type === "ERR") {
|
||||
const message = result.value as string;
|
||||
if (message.endsWith("is not a valid word")) {
|
||||
// extract out word
|
||||
const word = message.split(" ")[0];
|
||||
logDispatch(<div><em>{message}</em><AddWordButton word={word} addWordFn={addWordFn} /></div>);
|
||||
} else {
|
||||
logDispatch(<div><em>{message}</em></div>);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const score_result = (result.value as { type: "PlayTiles"; result: ScoreResult }).result;
|
||||
const total_points = score_result.total;
|
||||
|
||||
|
||||
if (confirmedScorePoints > -1) {
|
||||
handlePlayerAction({type: "PlayTiles", result: score_result}, props.settings.playerName);
|
||||
setTurnCount(turnCount + 1);
|
||||
}
|
||||
|
||||
setConfirmedScorePoints(total_points);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const result: MyResult<{ type: "PlayTiles"; result: ScoreResult } | string> = props.wasm.receive_play(playedTiles, confirmedScorePoints > -1);
|
||||
console.log({result});
|
||||
|
||||
if(result.response_type === "ERR") {
|
||||
const message = result.value as string;
|
||||
if (message.endsWith("is not a valid word")) {
|
||||
// extract out word
|
||||
const word = message.split(" ")[0];
|
||||
logDispatch(<div><em>{message}</em><AddWordButton word={word} addWordFn={addWordFn} /></div>);
|
||||
} else {
|
||||
logDispatch(<div><em>{message}</em></div>);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const score_result = (result.value as { type: "PlayTiles"; result: ScoreResult }).result;
|
||||
const total_points = score_result.total;
|
||||
|
||||
|
||||
if (confirmedScorePoints > -1) {
|
||||
handlePlayerAction({type: "PlayTiles", result: score_result}, props.settings.playerName);
|
||||
}}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points` : "Check"}</button>
|
||||
<button className="return" onClick={() => {
|
||||
trayDispatch({action: TileDispatchActionType.RETURN});
|
||||
}}>Return Tiles</button>
|
||||
<button className="pass" onClick={() => {
|
||||
props.wasm.skip_turn();
|
||||
handlePlayerAction({type: "Pass"}, props.settings.playerName);
|
||||
setTurnCount(turnCount + 1);
|
||||
}
|
||||
|
||||
setConfirmedScorePoints(total_points);
|
||||
}
|
||||
}}>Pass</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
}}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points ✅` : "Check"}</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
trayDispatch({action: TileDispatchActionType.RETURN}); // want all tiles back on tray for tile exchange
|
||||
setIsTileExchangeOpen(true);
|
||||
}}
|
||||
>Open Tile Exchange</button>
|
||||
<button onClick={() => {
|
||||
trayDispatch({action: TileDispatchActionType.RETURN});
|
||||
}}>Return Tiles</button>
|
||||
<button onClick={() => {
|
||||
props.wasm.skip_turn();
|
||||
handlePlayerAction({type: "Pass"}, props.settings.playerName);
|
||||
setTurnCount(turnCount + 1);
|
||||
}}>Skip Turn</button>
|
||||
</>;
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ async function run() {
|
|||
root.render(<Menu dictionary_text={dictionary_text} settings={{
|
||||
trayLength: 7,
|
||||
playerName: 'Player',
|
||||
aiName: 'AI',
|
||||
|
||||
}}/>);
|
||||
|
||||
|
|
|
@ -3,16 +3,44 @@
|
|||
@board-length: 15;
|
||||
@tile-star-size: 45px;
|
||||
|
||||
.tray {
|
||||
.tray-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, @tile-width);
|
||||
grid-gap: 5px;
|
||||
height: @tile-width;
|
||||
width: fit-content;
|
||||
background-color: #bbb59d;
|
||||
margin: 10px;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
width: @board-length*@tile-width;
|
||||
|
||||
.tray {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, @tile-width);
|
||||
grid-gap: 5px;
|
||||
height: @tile-width;
|
||||
width: fit-content;
|
||||
background-color: #bbb59d;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.player-controls {
|
||||
display: grid;
|
||||
grid-template-areas: "check return"
|
||||
"check pass";
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
.check {
|
||||
grid-area: check;
|
||||
}
|
||||
|
||||
.return {
|
||||
grid-area: return;
|
||||
}
|
||||
|
||||
.pass {
|
||||
grid-area: pass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.board-grid {
|
||||
//grid-area: grid;
|
||||
display: grid;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Letter as LetterData, Letter} from "word_grid";
|
||||
import {Letter as LetterData, Letter} from "../../pkg/word_grid";
|
||||
import * as React from "react";
|
||||
|
||||
export enum CellType {
|
||||
|
@ -14,6 +14,7 @@ export enum CellType {
|
|||
export interface Settings {
|
||||
trayLength: number;
|
||||
playerName: string;
|
||||
aiName: string;
|
||||
}
|
||||
|
||||
export enum LocationType {
|
||||
|
|
Loading…
Reference in a new issue