Add AI tile count and total tile counts, other UI tweaks

This commit is contained in:
Joel Therrien 2023-09-16 21:08:10 -07:00
parent e7c46c8efd
commit 0d30ac0b46
6 changed files with 162 additions and 67 deletions

View file

@ -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)]

View file

@ -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,
})?)
}
}
}
}

View file

@ -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>
</>;
}

View file

@ -51,6 +51,7 @@ async function run() {
root.render(<Menu dictionary_text={dictionary_text} settings={{
trayLength: 7,
playerName: 'Player',
aiName: 'AI',
}}/>);

View file

@ -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;

View file

@ -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 {