M client/src/api/game/mod.rs => client/src/api/game/mod.rs +7 -4
@@ 9,6 9,8 @@
use serde::{Deserialize, Serialize};
use serde_repr::Deserialize_repr;
+use crate::game_status::Card;
+
use super::user::User;
mod forming;
@@ 31,12 33,13 @@ pub use details::*;
#[derive(Deserialize, Serialize, Clone)]
pub enum Command {
+ InitSupplyPile { card: Card, amount: usize },
+ /// take a card from pile N
+ TakeFromPile { index: usize },
/// draw N amount of cards from deck
- Draw { amount: u32 },
+ Draw { amount: usize },
/// discard card from hand in slot N
- Discard { index: u32 },
- /// shuffle discard pile to deck
- ShuffleDiscardToDeck { seed: u32 },
+ Discard { index: usize },
}
#[derive(Deserialize, Clone)]
M client/src/game_status/mod.rs => client/src/game_status/mod.rs +43 -4
@@ 6,17 6,56 @@
* See LICENSE for licensing information.
*/
-use crate::api::game::Action;
+use std::collections::HashMap;
+
+use serde::{Deserialize, Serialize};
+
+use crate::api::game::{Action, Command};
mod parser;
+#[derive(Deserialize, Serialize, Clone)]
+pub struct CardAction {
+ pub target: String,
+ pub command: Command,
+ pub seed: u64,
+}
+
+#[derive(Deserialize, Serialize, Clone)]
+pub struct Card {
+ pub name: String,
+ pub cost: u32,
+ /// if set, use this?
+ //vp_cost: Option<u32>,
+ pub actions: Vec<CardAction>,
+}
+
+/// a supply pile holds an amount of some card
+pub struct SupplyPile {
+ /// the card type that the supply pile holds
+ pub card: Card,
+ /// the amount of cards currently in the pile
+ pub amount: usize,
+}
+
+pub struct PlayerStatus {
+ pub hand: Vec<Card>,
+ pub deck: Vec<Card>,
+ pub discard: Vec<Card>,
+}
+
/// constructed from a vector of [`Action`]s
pub struct GameStatus {
-
+ pub supply_piles: Vec<SupplyPile>,
+ /// player ids mapped to statuses
+ pub players: HashMap<String, PlayerStatus>,
}
impl GameStatus {
- pub fn new(actions: Vec<Action>) -> Self {
- Self { }
+ pub fn new(actions: &Vec<Action>) -> Self {
+ match parser::parse(actions) {
+ Ok(res) => res,
+ Err(_) => panic!("parsing actions failed"),
+ }
}
}
M client/src/game_status/parser.rs => client/src/game_status/parser.rs +74 -0
@@ 6,3 6,77 @@
* See LICENSE for licensing information.
*/
+use std::collections::HashMap;
+use fastrand::Rng;
+
+use crate::{api::game::{Action, Command}, game_status::SupplyPile};
+
+use super::{GameStatus, PlayerStatus, Card};
+
+pub fn parse(actions: &Vec<Action>) -> Result<GameStatus, ()> {
+ let mut game_status = GameStatus {
+ supply_piles: vec![],
+ players: HashMap::new(),
+ };
+
+ for action in 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);
+ }
+
+ 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));
+ }
+ _ => todo!(),
+ }
+ }
+
+ Err(())
+}
+
+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);
+}
M client/src/plugins/game/mod.rs => client/src/plugins/game/mod.rs +3 -1
@@ 8,7 8,7 @@
use bevy::prelude::*;
-use crate::{api::game::Game, Global, AppState};
+use crate::{api::game::Game, Global, AppState, game_status::GameStatus};
use super::GameDetailsCallEvent;
@@ 27,12 27,14 @@ impl Plugin for GamePlugin {
#[derive(Resource)]
pub struct GameData {
pub game: Option<Game>,
+ pub game_status: Option<GameStatus>,
}
impl Default for GameData {
fn default() -> Self {
Self {
game: None,
+ game_status: None,
}
}
}