/*
* This file is part of laurelin_client
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use std::collections::HashMap;
use fastrand::Rng;
use crate::{api::game::{Action, Command, Game}, game_status::SupplyPile};
use super::{GameStatus, PlayerState, PlayerStatus, Card};
pub fn parse(game: &Game) -> Result<GameStatus, ()> {
let mut game_status = GameStatus {
actions: game.actions.as_ref().unwrap().to_vec(),
supply_piles: vec![],
players: HashMap::new()
};
game_status.players.insert(game.host_id.clone(), PlayerStatus {
state: PlayerState::Idle,
currency: 0,
vp: 2,
hand: vec![],
deck: vec![],
discard: vec![]
});
game_status.players.insert(game.guest_id.clone(), PlayerStatus {
state: PlayerState::Idle,
currency: 0,
vp: 2,
hand: vec![],
deck: vec![],
discard: vec![]
});
// TODO: a system for reparsing if needed, e.g. after something
// modifies the actions Vector.
for action in &game_status.actions {
// the one who invoked the action
let invoker = game_status.players.get_mut(&action.invoker)
.unwrap_or_else(|| unreachable!());
// the one who the action affects, may also be the invoker, e.g. draw
let target = game_status.players.get_mut(&action.target)
.unwrap_or_else(|| unreachable!());
match &action.command {
Command::InitSupplyPile { card, amount } => {
let pile = SupplyPile {
card: card.clone(),
amount: *amount,
};
game_status.supply_piles.push(pile);
}
Command::TakeFromPile { index } => {
// index should be within range
assert!(*index <= game_status.supply_piles.len());
let pile = &mut game_status.supply_piles.get_mut(*index)
.unwrap_or_else(|| unreachable!());
// pile should not be empty
assert!(pile.amount > 0);
pile.amount = pile.amount - 1;
target.discard.push(pile.card.clone());
}
Command::Draw { amount } => {
for _ in 0..*amount {
if target.deck.is_empty() {
shuffle_discard_to_deck(target, action.seed.parse::<u64>().unwrap());
}
target.hand.push(target.deck.pop()
.unwrap_or_else(|| unreachable!()));
}
}
Command::Discard { index } => {
// index should be within range
assert!(*index <= target.hand.len());
target.discard.push(target.hand.remove(*index));
}
Command::ChangePlayerState { state } => {
target.state = *state;
}
_ => todo!(),
}
}
Ok(game_status)
}
fn shuffle_discard_to_deck(target: &mut PlayerStatus, seed: u64) {
let cards = target.discard.to_vec();
target.discard.clear();
target.deck = cards;
let rng = Rng::with_seed(seed);
rng.shuffle(&mut target.deck);
}