Add turn counting and score persistence
This commit is contained in:
parent
c7399b2b99
commit
fd7cf2d6af
5 changed files with 117 additions and 43 deletions
12
src/board.rs
12
src/board.rs
|
@ -1,11 +1,13 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Formatter, Write};
|
use std::fmt::{Formatter, Write};
|
||||||
|
use std::borrow::BorrowMut;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tsify::Tsify;
|
use tsify::Tsify;
|
||||||
use crate::constants::{ALL_LETTERS_BONUS, GRID_LENGTH, TRAY_LENGTH};
|
use crate::constants::{ALL_LETTERS_BONUS, GRID_LENGTH, TRAY_LENGTH};
|
||||||
use crate::dictionary::DictionaryImpl;
|
use crate::dictionary::DictionaryImpl;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum Direction {
|
enum Direction {
|
||||||
Row, Column
|
Row, Column
|
||||||
|
@ -458,6 +460,16 @@ impl Board {
|
||||||
Ok(())
|
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 {
|
impl fmt::Display for Board {
|
||||||
|
|
38
src/game.rs
38
src/game.rs
|
@ -28,11 +28,11 @@ impl Player {
|
||||||
pub struct PlayerState {
|
pub struct PlayerState {
|
||||||
pub player: Player,
|
pub player: Player,
|
||||||
pub score: u32,
|
pub score: u32,
|
||||||
tray: Tray
|
pub tray: Tray
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Game{
|
pub struct Game{
|
||||||
tile_pool: Vec<Letter>,
|
pub tile_pool: Vec<Letter>,
|
||||||
board: Board,
|
board: Board,
|
||||||
pub player_states: Vec<PlayerState>,
|
pub player_states: Vec<PlayerState>,
|
||||||
dictionary: DictionaryImpl,
|
dictionary: DictionaryImpl,
|
||||||
|
@ -75,19 +75,29 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tray(&self, name: &str) -> Option<&Tray> {
|
pub fn get_player_state(&self, name: &str) -> Option<&PlayerState> {
|
||||||
let player = self.player_states.iter()
|
self.player_states.iter()
|
||||||
.filter(|state| state.player.get_name().eq(name))
|
.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)
|
Some(&player.tray)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tray_mut(&mut self, name: &str) -> Option<&mut Tray> {
|
pub fn get_tray_mut(&mut self, name: &str) -> Option<&mut Tray> {
|
||||||
let player = self.player_states.iter_mut()
|
let player = self.get_player_state_mut(name)?;
|
||||||
.filter(|state| state.player.get_name().eq(name))
|
|
||||||
.nth(0)?;
|
|
||||||
|
|
||||||
Some(&mut player.tray)
|
Some(&mut player.tray)
|
||||||
|
|
||||||
|
@ -95,8 +105,15 @@ impl Game {
|
||||||
|
|
||||||
pub fn get_board(&self) -> &Board {&self.board}
|
pub fn get_board(&self) -> &Board {&self.board}
|
||||||
|
|
||||||
pub fn get_board_mut(&mut self) -> &mut Board {
|
pub fn set_board(&mut self, new_board: Board) {
|
||||||
&mut self.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 {
|
pub fn get_dictionary(&self) -> &DictionaryImpl {
|
||||||
|
@ -104,5 +121,4 @@ impl Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ use crate::board::Letter;
|
||||||
|
|
||||||
pub mod ai;
|
pub mod ai;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Tsify)]
|
#[derive(Debug, Serialize, Deserialize, Tsify, Clone)]
|
||||||
#[tsify(from_wasm_abi)]
|
#[tsify(from_wasm_abi)]
|
||||||
pub struct Tray {
|
pub struct Tray {
|
||||||
pub letters: Vec<Option<Letter>>
|
pub letters: Vec<Option<Letter>>
|
||||||
|
|
14
src/wasm.rs
14
src/wasm.rs
|
@ -58,13 +58,14 @@ impl GameWasm {
|
||||||
let tray_tile_locations: Vec<Option<PlayedTile>> = serde_wasm_bindgen::from_value(tray_tile_locations)?;
|
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 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();
|
let mut played_letters: Vec<(Letter, Coordinates)> = Vec::new();
|
||||||
for (i, played_tile) in tray_tile_locations.iter().enumerate() {
|
for (i, played_tile) in tray_tile_locations.iter().enumerate() {
|
||||||
if played_tile.is_some() {
|
if played_tile.is_some() {
|
||||||
let played_tile = played_tile.unwrap();
|
let played_tile = played_tile.unwrap();
|
||||||
let mut letter: Letter = tray.letters.get(i).unwrap().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);
|
let coord = Coordinates::new_from_index(played_tile.index);
|
||||||
if letter.is_blank {
|
if letter.is_blank {
|
||||||
|
@ -113,7 +114,16 @@ impl GameWasm {
|
||||||
|
|
||||||
})
|
})
|
||||||
.collect();
|
.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(
|
serde_wasm_bindgen::to_value(
|
||||||
&MyResult {
|
&MyResult {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {GameWasm, Letter as LetterData, MyResult, PlayedTile, PlayerAndScore, Tray, WordResult} from "word_grid";
|
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 {
|
export enum LocationType {
|
||||||
GRID,
|
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) {
|
function addLogInfo(existingLog: React.JSX.Element[], newItem: React.JSX.Element) {
|
||||||
newItem.key = existingLog.length;
|
newItem = React.cloneElement(newItem, { key: existingLog.length })
|
||||||
existingLog.push(newItem);
|
existingLog.push(newItem);
|
||||||
return existingLog.slice();
|
return existingLog.slice();
|
||||||
}
|
}
|
||||||
|
@ -86,9 +91,21 @@ export function Game(props: {wasm: GameWasm}) {
|
||||||
return props.wasm.get_board_cell_types();
|
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 startIndex = matchCoordinate(playerLetters, update.start);
|
||||||
let endIndex = matchCoordinate(playerLetters, update.end);
|
let endIndex = matchCoordinate(playerLetters, update.end);
|
||||||
|
@ -105,12 +122,17 @@ export function Game(props: {wasm: GameWasm}) {
|
||||||
endLetter.index = update.start.index;
|
endLetter.index = update.start.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCheckPerformed(false);
|
setConfirmedScorePoints(-1);
|
||||||
|
|
||||||
return playerLetters.slice();
|
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");
|
let tray: Tray = props.wasm.get_tray("Player");
|
||||||
|
|
||||||
// initial state
|
// initial state
|
||||||
|
@ -127,14 +149,22 @@ export function Game(props: {wasm: GameWasm}) {
|
||||||
|
|
||||||
const [logInfo, logDispatch] = useReducer(addLogInfo, []);
|
const [logInfo, logDispatch] = useReducer(addLogInfo, []);
|
||||||
|
|
||||||
const [turnCount, setTurnCount] = useState<number>(0);
|
const [turnCount, setTurnCount] = useState<number>(1);
|
||||||
const playerAndScores: PlayerAndScore[] = useMemo(() => {
|
const playerAndScores: PlayerAndScore[] = useMemo(() => {
|
||||||
return props.wasm.get_scores();
|
return props.wasm.get_scores();
|
||||||
}, [turnCount])
|
}, [turnCount])
|
||||||
|
useEffect(() => {
|
||||||
|
logDispatch(<h4>Turn {turnCount}</h4>);
|
||||||
|
setConfirmedScorePoints(-1);
|
||||||
|
trayDispatch({action: TileDispatchActionType.RETRIEVE});
|
||||||
|
|
||||||
|
}, [turnCount]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="board-log">
|
<div className="board-log">
|
||||||
<Grid cellTypes={cellTypes} playerLetters={playerLetters} dispatch={dispatch}/>
|
<Grid cellTypes={cellTypes} playerLetters={playerLetters} dispatch={trayDispatch}/>
|
||||||
<div className="message-log">
|
<div className="message-log">
|
||||||
<Scores playerScores={playerAndScores}/>
|
<Scores playerScores={playerAndScores}/>
|
||||||
<div className="log">
|
<div className="log">
|
||||||
|
@ -143,7 +173,7 @@ export function Game(props: {wasm: GameWasm}) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TileTray letters={playerLetters} trayLength={7} dispatch={dispatch}/>
|
<TileTray letters={playerLetters} trayLength={7} dispatch={trayDispatch}/>
|
||||||
<button onClick={(e) => {
|
<button onClick={(e) => {
|
||||||
const playedTiles = playerLetters.map((i) => {
|
const playedTiles = playerLetters.map((i) => {
|
||||||
if (i === undefined) {
|
if (i === undefined) {
|
||||||
|
@ -165,7 +195,7 @@ export function Game(props: {wasm: GameWasm}) {
|
||||||
return null;
|
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});
|
console.log({result});
|
||||||
|
|
||||||
if(result.response_type === "ERR") {
|
if(result.response_type === "ERR") {
|
||||||
|
@ -177,15 +207,21 @@ export function Game(props: {wasm: GameWasm}) {
|
||||||
total_points += word_result.score;
|
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>);
|
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 startLocation: CoordinateData = JSON.parse(e.dataTransfer.getData("wordGrid/coords"));
|
||||||
const thisLocation = props.location;
|
const thisLocation = props.location;
|
||||||
|
|
||||||
props.dispatch({start: startLocation, end: thisLocation});
|
props.dispatch({action: TileDispatchActionType.MOVE, start: startLocation, end: thisLocation});
|
||||||
}
|
}
|
||||||
|
|
||||||
let className = "tileSpot";
|
let className = "tileSpot";
|
||||||
|
|
Loading…
Reference in a new issue