DEVELOPMENT ENVIRONMENT

~liljamo/deck-builder

c5909cd025c1f7ffec7b02fa467ed4a82c2ae6b2 — skye 1 year, 7 months ago 28bb3dc
feat(client): semi initial game state and parser
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,
        }
    }
}