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)]
|
#[derive(Serialize, Deserialize, Tsify, Debug)]
|
||||||
|
|
21
src/wasm.rs
21
src/wasm.rs
|
@ -155,4 +155,25 @@ impl GameWasm {
|
||||||
self.0.current_player_name()
|
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() {
|
function runAI() {
|
||||||
const result: TurnAdvanceResult = props.wasm.advance_turn();
|
const result: TurnAdvanceResult = props.wasm.advance_turn();
|
||||||
if(result.type == "AIMove"){
|
if(result.type == "AIMove"){
|
||||||
handlePlayerAction(result.action, "AI");
|
handlePlayerAction(result.action, props.settings.aiName);
|
||||||
} else {
|
} else {
|
||||||
// this would be quite surprising
|
// this would be quite surprising
|
||||||
console.error({result});
|
console.error({result});
|
||||||
|
@ -179,6 +179,21 @@ export function Game(props: {
|
||||||
}
|
}
|
||||||
}, [logInfo]);
|
}, [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) {
|
function handlePlayerAction(action: TurnAction, playerName: string) {
|
||||||
|
|
||||||
if (action.type == "PlayTiles"){
|
if (action.type == "PlayTiles"){
|
||||||
|
@ -213,73 +228,85 @@ export function Game(props: {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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}/>
|
<TileTray letters={playerLetters} trayLength={props.settings.trayLength} dispatch={trayDispatch}/>
|
||||||
<button onClick={() => {
|
<div className="player-controls">
|
||||||
const playedTiles = playerLetters.map((i) => {
|
<button className="check" onClick={() => {
|
||||||
if (i === undefined) {
|
const playedTiles = playerLetters.map((i) => {
|
||||||
return null;
|
if (i === undefined) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (i.location === LocationType.GRID) {
|
if (i.location === LocationType.GRID) {
|
||||||
let result: PlayedTile = {
|
let result: PlayedTile = {
|
||||||
index: i.index,
|
index: i.index,
|
||||||
character: undefined
|
character: undefined
|
||||||
};
|
};
|
||||||
if (i.is_blank) {
|
if (i.is_blank) {
|
||||||
result.character = i.text;
|
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) {
|
}}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points` : "Check"}</button>
|
||||||
handlePlayerAction({type: "PlayTiles", result: score_result}, props.settings.playerName);
|
<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);
|
setTurnCount(turnCount + 1);
|
||||||
}
|
}}>Pass</button>
|
||||||
|
</div>
|
||||||
setConfirmedScorePoints(total_points);
|
</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={{
|
root.render(<Menu dictionary_text={dictionary_text} settings={{
|
||||||
trayLength: 7,
|
trayLength: 7,
|
||||||
playerName: 'Player',
|
playerName: 'Player',
|
||||||
|
aiName: 'AI',
|
||||||
|
|
||||||
}}/>);
|
}}/>);
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,44 @@
|
||||||
@board-length: 15;
|
@board-length: 15;
|
||||||
@tile-star-size: 45px;
|
@tile-star-size: 45px;
|
||||||
|
|
||||||
.tray {
|
.tray-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(7, @tile-width);
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
grid-gap: 5px;
|
width: @board-length*@tile-width;
|
||||||
height: @tile-width;
|
|
||||||
width: fit-content;
|
.tray {
|
||||||
background-color: #bbb59d;
|
display: grid;
|
||||||
margin: 10px;
|
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 {
|
.board-grid {
|
||||||
//grid-area: grid;
|
//grid-area: grid;
|
||||||
display: 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";
|
import * as React from "react";
|
||||||
|
|
||||||
export enum CellType {
|
export enum CellType {
|
||||||
|
@ -14,6 +14,7 @@ export enum CellType {
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
trayLength: number;
|
trayLength: number;
|
||||||
playerName: string;
|
playerName: string;
|
||||||
|
aiName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum LocationType {
|
export enum LocationType {
|
||||||
|
|
Loading…
Reference in a new issue