Add basic tile exchange support

Still need to support handling if the player reordered their tray
This commit is contained in:
Joel Therrien 2023-08-22 20:52:03 -07:00
parent 960e8c31fb
commit ca5ab097c5
3 changed files with 132 additions and 38 deletions

View file

@ -47,10 +47,42 @@ pub struct WordResult {
score: u32, score: u32,
} }
pub struct PlayerStates(pub Vec<PlayerState>);
impl PlayerStates {
pub fn get_player_state(&self, name: &str) -> Option<&PlayerState> {
self.0.iter()
.filter(|state| state.player.get_name().eq(name))
.nth(0)
}
pub fn get_player_state_mut(&mut self, name: &str) -> Option<&mut PlayerState> {
self.0.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.get_player_state_mut(name)?;
Some(&mut player.tray)
}
}
pub struct Game{ pub struct Game{
pub tile_pool: Vec<Letter>, pub tile_pool: Vec<Letter>,
rng: SmallRng,
board: Board, board: Board,
pub player_states: Vec<PlayerState>, pub player_states: PlayerStates,
dictionary: DictionaryImpl, dictionary: DictionaryImpl,
} }
@ -85,39 +117,14 @@ impl Game {
Game { Game {
tile_pool: letters, tile_pool: letters,
rng,
board: Board::new(), board: Board::new(),
player_states, player_states: PlayerStates(player_states),
dictionary: DictionaryImpl::create_from_str(dictionary_text) dictionary: DictionaryImpl::create_from_str(dictionary_text)
} }
} }
pub fn get_player_state(&self, name: &str) -> Option<&PlayerState> {
self.player_states.iter()
.filter(|state| state.player.get_name().eq(name))
.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.get_player_state_mut(name)?;
Some(&mut player.tray)
}
pub fn get_board(&self) -> &Board {&self.board} pub fn get_board(&self) -> &Board {&self.board}
@ -126,7 +133,7 @@ impl Game {
} }
pub fn fill_trays(&mut self){ pub fn fill_trays(&mut self){
for state in self.player_states.iter_mut() { for state in self.player_states.0.iter_mut() {
let tray = &mut state.tray; let tray = &mut state.tray;
tray.fill(&mut self.tile_pool); tray.fill(&mut self.tile_pool);
} }
@ -139,7 +146,7 @@ impl Game {
pub fn receive_play(&mut self, player: &str, tray_tile_locations: Vec<Option<PlayedTile>>, commit_move: bool) -> Result<Vec<WordResult>, String> { pub fn receive_play(&mut self, player: &str, tray_tile_locations: Vec<Option<PlayedTile>>, commit_move: bool) -> Result<Vec<WordResult>, String> {
let mut board_instance = self.get_board().clone(); let mut board_instance = self.get_board().clone();
let mut tray = self.get_tray(player).unwrap().clone(); let mut tray = self.player_states.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() {
@ -181,7 +188,7 @@ impl Game {
.collect(); .collect();
if commit_move { if commit_move {
let mut player_state = self.get_player_state_mut(player).unwrap(); let mut player_state = self.player_states.get_player_state_mut(player).unwrap();
player_state.score += x.1; player_state.score += x.1;
player_state.tray = tray; player_state.tray = tray;
@ -193,4 +200,33 @@ impl Game {
Ok(words) Ok(words)
} }
pub fn exchange_tiles(&mut self, player: &str, tray_tile_locations: Vec<bool>) -> Result<Tray, String> {
let tray = match self.player_states.get_tray_mut(player) {
None => {return Err(format!("Player {} not found", player))}
Some(x) => {x}
};
if tray.letters.len() != tray_tile_locations.len() {
return Err("Incoming tray and existing tray have different lengths".to_string());
}
let tile_pool = &mut self.tile_pool;
for (i, played_tile) in tray_tile_locations.iter().enumerate() {
if *played_tile {
let letter = tray.letters.get_mut(i).unwrap();
if letter.is_some() {
tile_pool.push(letter.unwrap().clone());
*letter = None;
}
}
}
tile_pool.shuffle(&mut self.rng);
tray.fill(&mut self.tile_pool);
Ok(tray.clone())
}
} }

View file

@ -34,7 +34,7 @@ impl GameWasm {
} }
pub fn get_tray(&self, name: &str) -> Result<JsValue, Error> { pub fn get_tray(&self, name: &str) -> Result<JsValue, Error> {
let tray = self.0.get_tray(name); let tray = self.0.player_states.get_tray(name);
serde_wasm_bindgen::to_value(&tray) serde_wasm_bindgen::to_value(&tray)
} }
@ -91,7 +91,7 @@ impl GameWasm {
score: u32, score: u32,
} }
let scores: Vec<PlayerAndScore> = self.0.player_states.iter() let scores: Vec<PlayerAndScore> = self.0.player_states.0.iter()
.map(|player_state| { .map(|player_state| {
PlayerAndScore { PlayerAndScore {
name: player_state.player.get_name().to_string(), name: player_state.player.get_name().to_string(),
@ -103,4 +103,28 @@ impl GameWasm {
Ok(serde_wasm_bindgen::to_value(&scores)?) Ok(serde_wasm_bindgen::to_value(&scores)?)
} }
pub fn exchange_tiles(&mut self, player: &str, tray_tile_locations: JsValue) -> Result<JsValue, Error>{
let tray_tile_locations: Vec<bool> = serde_wasm_bindgen::from_value(tray_tile_locations)?;
match self.0.exchange_tiles(player, tray_tile_locations) {
Ok(tray) => {
serde_wasm_bindgen::to_value(&MyResult {
response_type: ResponseType::OK,
value: tray
})
},
Err(e) => {
serde_wasm_bindgen::to_value(&MyResult {
response_type: ResponseType::ERR,
value: e
})
}
}
}
} }

View file

@ -211,11 +211,35 @@ export function Game(props: {wasm: GameWasm, settings: Settings}) {
} }
}, [logInfo]) }, [logInfo])
function exchangeFunction(selectedArray: Array<boolean>) {
let numSelected = 0;
selectedArray.forEach((x) => {
if (x){
numSelected++;
}
})
const result: MyResult<Tray | string> = props.wasm.exchange_tiles("Player", selectedArray);
console.log({result});
if(result.response_type === "ERR") {
logDispatch(<div><em>{(result.value as string)}</em></div>);
} else {
logDispatch(<div><em>You exchanged {numSelected} tiles.</em></div>);
setTurnCount(turnCount + 1);
}
}
return <> return <>
<TileExchangeModal playerLetters={playerLetters} isOpen={isTileExchangeOpen} setOpen={setIsTileExchangeOpen} /> <TileExchangeModal
playerLetters={playerLetters}
isOpen={isTileExchangeOpen}
setOpen={setIsTileExchangeOpen}
exchangeFunction={exchangeFunction}
/>
<div className="board-log"> <div className="board-log">
<Grid cellTypes={cellTypes} playerLetters={playerLetters} boardLetters={boardLetters} dispatch={trayDispatch}/> <Grid cellTypes={cellTypes} playerLetters={playerLetters} boardLetters={boardLetters} dispatch={trayDispatch}/>
<div className="message-log"> <div className="message-log">
@ -275,7 +299,9 @@ export function Game(props: {wasm: GameWasm, settings: Settings}) {
}}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points ✅` : "Check"}</button> }}>{confirmedScorePoints > -1 ? `Score ${confirmedScorePoints} points ✅` : "Check"}</button>
<button onClick={(e) => {setIsTileExchangeOpen(true);}}>Open Tile Exchange</button> <button
onClick={(e) => {setIsTileExchangeOpen(true);}}
>Open Tile Exchange</button>
</>; </>;
@ -470,7 +496,12 @@ function Scores(props: {playerScores: Array<PlayerAndScore>}){
</div> </div>
} }
function TileExchangeModal(props: {playerLetters: PlayableLetterData[], isOpen: boolean, setOpen: (isOpen: boolean) => void}) { function TileExchangeModal(props: {
playerLetters: PlayableLetterData[],
isOpen: boolean,
setOpen: (isOpen: boolean) => void,
exchangeFunction: (selectedArray: Array<boolean>) => void
}) {
function clearExchangeTiles() { function clearExchangeTiles() {
const array: boolean[] = []; const array: boolean[] = [];
@ -479,6 +510,9 @@ function TileExchangeModal(props: {playerLetters: PlayableLetterData[], isOpen:
} }
const [tilesToExchange, setTilesToExchange] = useState<boolean[]>(clearExchangeTiles); const [tilesToExchange, setTilesToExchange] = useState<boolean[]>(clearExchangeTiles);
useEffect(() => {
setTilesToExchange(clearExchangeTiles());
}, [props.playerLetters])
let tilesExchangedSelected = 0; let tilesExchangedSelected = 0;
for (let i of tilesToExchange) { for (let i of tilesToExchange) {
@ -518,8 +552,8 @@ function TileExchangeModal(props: {playerLetters: PlayableLetterData[], isOpen:
props.setOpen(false); props.setOpen(false);
}}>Cancel</button> }}>Cancel</button>
<button disabled = {tilesExchangedSelected == 0} onClick={(e) => { <button disabled = {tilesExchangedSelected == 0} onClick={(e) => {
// TODO exchange code props.exchangeFunction(tilesToExchange);
props.setOpen(false);
}}>Exchange</button> }}>Exchange</button>
</div> </div>
</div> </div>