Add turn counting and score persistence

This commit is contained in:
Joel Therrien 2023-08-16 19:37:32 -07:00
parent c7399b2b99
commit fd7cf2d6af
5 changed files with 117 additions and 43 deletions

View file

@ -1,11 +1,13 @@
use std::collections::HashSet;
use std::fmt;
use std::fmt::{Formatter, Write};
use std::borrow::BorrowMut;
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use crate::constants::{ALL_LETTERS_BONUS, GRID_LENGTH, TRAY_LENGTH};
use crate::dictionary::DictionaryImpl;
#[derive(Clone, Copy)]
enum Direction {
Row, Column
@ -458,6 +460,16 @@ impl Board {
Ok(())
}
pub fn fix_tiles(&mut self) {
for cell in self.cells.iter_mut() {
match cell.value.borrow_mut() {
None => {}
Some(x) => {
x.ephemeral = false;
}
}
}
}
}
impl fmt::Display for Board {

View file

@ -28,11 +28,11 @@ impl Player {
pub struct PlayerState {
pub player: Player,
pub score: u32,
tray: Tray
pub tray: Tray
}
pub struct Game{
tile_pool: Vec<Letter>,
pub tile_pool: Vec<Letter>,
board: Board,
pub player_states: Vec<PlayerState>,
dictionary: DictionaryImpl,
@ -75,19 +75,29 @@ impl Game {
}
}
pub fn get_tray(&self, name: &str) -> Option<&Tray> {
let player = self.player_states.iter()
pub fn get_player_state(&self, name: &str) -> Option<&PlayerState> {
self.player_states.iter()
.filter(|state| state.player.get_name().eq(name))
.nth(0)?;
.nth(0)
}
pub fn get_player_state_mut(&mut self, name: &str) -> Option<&mut PlayerState> {
self.player_states.iter_mut()
.filter(|state| state.player.get_name().eq(name))
.nth(0)
}
pub fn get_tray(&self, name: &str) -> Option<&Tray> {
let player = self.get_player_state(name)?;
Some(&player.tray)
}
pub fn get_tray_mut(&mut self, name: &str) -> Option<&mut Tray> {
let player = self.player_states.iter_mut()
.filter(|state| state.player.get_name().eq(name))
.nth(0)?;
let player = self.get_player_state_mut(name)?;
Some(&mut player.tray)
@ -95,8 +105,15 @@ impl Game {
pub fn get_board(&self) -> &Board {&self.board}
pub fn get_board_mut(&mut self) -> &mut Board {
&mut self.board
pub fn set_board(&mut self, new_board: Board) {
self.board = new_board;
}
pub fn fill_trays(&mut self){
for state in self.player_states.iter_mut() {
let tray = &mut state.tray;
tray.fill(&mut self.tile_pool);
}
}
pub fn get_dictionary(&self) -> &DictionaryImpl {
@ -104,5 +121,4 @@ impl Game {
}
}

View file

@ -5,7 +5,7 @@ use crate::board::Letter;
pub mod ai;
#[derive(Debug, Serialize, Deserialize, Tsify)]
#[derive(Debug, Serialize, Deserialize, Tsify, Clone)]
#[tsify(from_wasm_abi)]
pub struct Tray {
pub letters: Vec<Option<Letter>>

View file

@ -58,13 +58,14 @@ impl GameWasm {
let tray_tile_locations: Vec<Option<PlayedTile>> = serde_wasm_bindgen::from_value(tray_tile_locations)?;
let mut board_instance = self.0.get_board().clone();
let tray = self.0.get_tray(player).unwrap().clone();
let mut tray = self.0.get_tray(player).unwrap().clone();
let mut played_letters: Vec<(Letter, Coordinates)> = Vec::new();
for (i, played_tile) in tray_tile_locations.iter().enumerate() {
if played_tile.is_some() {
let played_tile = played_tile.unwrap();
let mut letter: Letter = tray.letters.get(i).unwrap().unwrap();
*tray.letters.get_mut(i).unwrap() = None;
let coord = Coordinates::new_from_index(played_tile.index);
if letter.is_blank {
@ -113,7 +114,16 @@ impl GameWasm {
})
.collect();
//let total_score = x.1;
if commit_move {
let mut player_state = self.0.get_player_state_mut(player).unwrap();
player_state.score += x.1;
player_state.tray = tray;
board_instance.fix_tiles();
self.0.set_board(board_instance);
self.0.fill_trays()
}
serde_wasm_bindgen::to_value(
&MyResult {

View file

@ -1,6 +1,6 @@
import * as React from "react";
import {GameWasm, Letter as LetterData, MyResult, PlayedTile, PlayerAndScore, Tray, WordResult} from "word_grid";
import {useMemo, useReducer, useState} from "react";
import {useEffect, useMemo, useReducer, useState} from "react";
export enum LocationType {
GRID,
@ -72,10 +72,15 @@ function matchCoordinate(playerLetters: PlayableLetterData[], coords: Coordinate
}
type TileDispatch = React.Dispatch<{start: CoordinateData, end: CoordinateData}>;
enum TileDispatchActionType {
MOVE,
RETRIEVE,
}
type TileDispatchAction = {action: TileDispatchActionType, start?: CoordinateData, end?: CoordinateData};
type TileDispatch = React.Dispatch<TileDispatchAction>;
function addLogInfo(existingLog: React.JSX.Element[], newItem: React.JSX.Element) {
newItem.key = existingLog.length;
newItem = React.cloneElement(newItem, { key: existingLog.length })
existingLog.push(newItem);
return existingLog.slice();
}
@ -86,9 +91,21 @@ export function Game(props: {wasm: GameWasm}) {
return props.wasm.get_board_cell_types();
}, []);
const [checkPerformed, setCheckPerformed] = useState<boolean>(false);
const [confirmedScorePoints, setConfirmedScorePoints] = useState<number>(-1);
function movePlayableLetters(playerLetters: PlayableLetterData[], update: {start: CoordinateData, end: CoordinateData}) {
function movePlayableLetters(playerLetters: PlayableLetterData[], update: TileDispatchAction) {
if(update.action === TileDispatchActionType.RETRIEVE) {
let tray: Tray = props.wasm.get_tray("Player");
// initial state
let letters: PlayableLetterData[] = tray.letters.map((ld, i) => {
ld["location"] = LocationType.TRAY;
ld["index"] = i;
return ld as PlayableLetterData;
})
return letters;
} else if (update.action === TileDispatchActionType.MOVE) {
let startIndex = matchCoordinate(playerLetters, update.start);
let endIndex = matchCoordinate(playerLetters, update.end);
@ -105,12 +122,17 @@ export function Game(props: {wasm: GameWasm}) {
endLetter.index = update.start.index;
}
setCheckPerformed(false);
setConfirmedScorePoints(-1);
return playerLetters.slice();
} else {
console.error("Unknown tray update");
console.error({update});
}
const [playerLetters, dispatch] = useReducer(movePlayableLetters, null, (_) => {
}
const [playerLetters, trayDispatch] = useReducer(movePlayableLetters, null, (_) => {
let tray: Tray = props.wasm.get_tray("Player");
// initial state
@ -127,14 +149,22 @@ export function Game(props: {wasm: GameWasm}) {
const [logInfo, logDispatch] = useReducer(addLogInfo, []);
const [turnCount, setTurnCount] = useState<number>(0);
const [turnCount, setTurnCount] = useState<number>(1);
const playerAndScores: PlayerAndScore[] = useMemo(() => {
return props.wasm.get_scores();
}, [turnCount])
useEffect(() => {
logDispatch(<h4>Turn {turnCount}</h4>);
setConfirmedScorePoints(-1);
trayDispatch({action: TileDispatchActionType.RETRIEVE});
}, [turnCount]);
return <>
<div className="board-log">
<Grid cellTypes={cellTypes} playerLetters={playerLetters} dispatch={dispatch}/>
<Grid cellTypes={cellTypes} playerLetters={playerLetters} dispatch={trayDispatch}/>
<div className="message-log">
<Scores playerScores={playerAndScores}/>
<div className="log">
@ -143,7 +173,7 @@ export function Game(props: {wasm: GameWasm}) {
</div>
</div>
<TileTray letters={playerLetters} trayLength={7} dispatch={dispatch}/>
<TileTray letters={playerLetters} trayLength={7} dispatch={trayDispatch}/>
<button onClick={(e) => {
const playedTiles = playerLetters.map((i) => {
if (i === undefined) {
@ -165,7 +195,7 @@ export function Game(props: {wasm: GameWasm}) {
return null;
});
const result: MyResult<Array<WordResult> | string> = props.wasm.receive_play("Player", playedTiles, false);
const result: MyResult<Array<WordResult> | string> = props.wasm.receive_play("Player", playedTiles, confirmedScorePoints > -1);
console.log({result});
if(result.response_type === "ERR") {
@ -177,15 +207,21 @@ export function Game(props: {wasm: GameWasm}) {
total_points += word_result.score;
}
const msg = `You would score ${total_points} points.`;
let msg: string;
if (confirmedScorePoints > -1) {
msg = `You scored ${total_points} points.`;
setTurnCount(turnCount + 1);
}
logDispatch(<div><em>{msg}</em></div>);
setCheckPerformed(true);
setConfirmedScorePoints(total_points);
}
}}>{checkPerformed ? "Submit ✅" : "Check"}</button>
}}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points ✅` : "Check"}</button>
</>;
@ -205,7 +241,7 @@ export function TileSlot(props: { tile: React.JSX.Element | undefined, location:
const startLocation: CoordinateData = JSON.parse(e.dataTransfer.getData("wordGrid/coords"));
const thisLocation = props.location;
props.dispatch({start: startLocation, end: thisLocation});
props.dispatch({action: TileDispatchActionType.MOVE, start: startLocation, end: thisLocation});
}
let className = "tileSpot";