Add basic tile exchange support
Still need to support handling if the player reordered their tray
This commit is contained in:
parent
960e8c31fb
commit
ca5ab097c5
3 changed files with 132 additions and 38 deletions
98
src/game.rs
98
src/game.rs
|
@ -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())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
28
src/wasm.rs
28
src/wasm.rs
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue