Compare commits

...

2 Commits

Author SHA1 Message Date
d78b8b1416 Add card structure and treasure card type 2021-01-08 18:46:39 +01:00
a8246888ee Rename Duchery to Duchy 2021-01-08 18:45:11 +01:00
3 changed files with 175 additions and 66 deletions

View File

@ -1,4 +1,7 @@
mod cards;
use async_std::{prelude::*, sync::RwLock}; use async_std::{prelude::*, sync::RwLock};
use cards::*;
use itertools::Itertools; use itertools::Itertools;
use rand::{seq::SliceRandom, thread_rng}; use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -7,8 +10,6 @@ use tide::{Body, Redirect, Request, Response};
use tide_websockets::{Message, WebSocket, WebSocketConnection}; use tide_websockets::{Message, WebSocket, WebSocketConnection};
use uuid::Uuid; use uuid::Uuid;
type Card = String;
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
enum ClientMessage { enum ClientMessage {
@ -70,23 +71,33 @@ struct PlayerState {
name: String, name: String,
draw_pile_count: usize, draw_pile_count: usize,
hand_count: usize, hand_count: usize,
discard_pile: Option<Card>, discard_pile: Option<String>,
played_cards: Vec<Card>, played_cards: Vec<String>,
} }
#[derive(Serialize)] #[derive(Serialize)]
struct PileState { struct PileState {
name: String, name: Card,
count: usize, count: usize,
} }
#[derive(Serialize)] #[derive(Clone, Serialize)]
struct TurnState { struct TurnState {
actions: u32, actions: u32,
buys: u32, buys: u32,
coin: u32, coin: u32,
} }
impl Default for TurnState {
fn default() -> Self {
TurnState {
actions: 1,
buys: 1,
coin: 0,
}
}
}
struct Player { struct Player {
id: String, id: String,
name: String, name: String,
@ -121,10 +132,6 @@ impl Player {
} }
} }
pub fn play_card(&mut self, index: usize) {
self.played_cards.push(self.hand.remove(index));
}
pub fn discard(&mut self, index: usize) { pub fn discard(&mut self, index: usize) {
self.discard_pile.push(self.hand.remove(index)); self.discard_pile.push(self.hand.remove(index));
} }
@ -141,16 +148,16 @@ impl Default for GameSetup {
fn default() -> GameSetup { fn default() -> GameSetup {
GameSetup { GameSetup {
deck: vec![ deck: vec![
"Copper".into(), copper(),
"Copper".into(), copper(),
"Copper".into(), copper(),
"Copper".into(), copper(),
"Copper".into(), copper(),
"Copper".into(), copper(),
"Copper".into(), copper(),
"Estate".into(), estate(),
"Estate".into(), estate(),
"Estate".into(), estate(),
], ],
} }
} }
@ -162,8 +169,9 @@ struct Game {
setup: GameSetup, setup: GameSetup,
connections: HashMap<Uuid, (String, WebSocketConnection)>, connections: HashMap<Uuid, (String, WebSocketConnection)>,
active_player: usize, active_player: usize,
supply: Vec<(String, usize)>, supply: Vec<(Card, usize)>,
trash: Vec<Card>, trash: Vec<Card>,
turn_state: TurnState,
} }
impl Game { impl Game {
@ -176,6 +184,7 @@ impl Game {
active_player: 0, active_player: 0,
supply: vec![], supply: vec![],
trash: vec![], trash: vec![],
turn_state: TurnState::default(),
} }
} }
@ -190,28 +199,119 @@ impl Game {
} }
let victory_qty = match self.players.len() { let victory_qty = match self.players.len() {
x if x <=2 => 8, x if x <= 2 => 8,
_ => 12, _ => 12,
}; };
self.supply = vec![ self.supply = vec![
("Copper".into(), 60 - self.players.len() * 7), (copper(), 60 - self.players.len() * 7),
("Silver".into(), 40), (silver(), 40),
("Gold".into(), 30), (gold(), 30),
("Estate".into(), victory_qty), (estate(), victory_qty),
("Duchery".into(), victory_qty), (
("Province".into(), victory_qty), Card {
("Curse".into(), 10), name: "Duchy".into(),
("Cellar".into(), 10), cost: 5,
("Moat".into(), 10), types: vec![],
("Village".into(), 10), },
("Merchant".into(), 10), victory_qty,
("Workshop".into(), 10), ),
("Smithy".into(), 10), (
("Remodel".into(), 10), Card {
("Militia".into(), 10), name: "Province".into(),
("Market".into(), 10), cost: 8,
("Mine".into(), 10), types: vec![],
},
victory_qty,
),
(
Card {
name: "Curse".into(),
cost: 0,
types: vec![],
},
10,
),
(
Card {
name: "Cellar".into(),
cost: 2,
types: vec![],
},
10,
),
(
Card {
name: "Moat".into(),
cost: 2,
types: vec![],
},
10,
),
(
Card {
name: "Village".into(),
cost: 3,
types: vec![],
},
10,
),
(
Card {
name: "Merchant".into(),
cost: 3,
types: vec![],
},
10,
),
(
Card {
name: "Workshop".into(),
cost: 3,
types: vec![],
},
10,
),
(
Card {
name: "Smithy".into(),
cost: 4,
types: vec![],
},
10,
),
(
Card {
name: "Remodel".into(),
cost: 4,
types: vec![],
},
10,
),
(
Card {
name: "Militia".into(),
cost: 4,
types: vec![],
},
10,
),
(
Card {
name: "Market".into(),
cost: 5,
types: vec![],
},
10,
),
(
Card {
name: "Mine".into(),
cost: 5,
types: vec![],
},
10,
),
]; ];
} }
@ -233,12 +333,16 @@ impl Game {
self.active_player += 1; self.active_player += 1;
self.active_player %= self.players.len(); self.active_player %= self.players.len();
self.turn_state = TurnState::default();
} }
// Check if the end game condition is reached and finish the game if so, // Check if the end game condition is reached and finish the game if so,
// sending a message to all clients. // sending a message to all clients.
fn end_game(&mut self) { fn end_game(&mut self) {
let provinces = self.supply.iter().any(|(c, n)| *c == "Province" && *n == 0); let provinces = self
.supply
.iter()
.any(|(c, n)| (*c).name == "Province" && *n == 0);
let supply = self.supply.iter().filter(|(_, n)| *n == 0).count() > 2; let supply = self.supply.iter().filter(|(_, n)| *n == 0).count() > 2;
if supply || provinces { if supply || provinces {
@ -263,6 +367,17 @@ impl Game {
self.trash self.trash
.push(self.players[player_number].hand.remove(index)); .push(self.players[player_number].hand.remove(index));
} }
pub fn play_card(&mut self, player_number: usize, index: usize) {
let player = self.players.get_mut(player_number).unwrap();
let card = player.hand.remove(index);
if let Some(coin) = card.treasure() {
self.turn_state.coin += coin;
}
player.played_cards.push(card);
}
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -293,24 +408,20 @@ async fn broadcast_state(game: &Game) {
name: html_escape::encode_text(&p.name).into(), name: html_escape::encode_text(&p.name).into(),
draw_pile_count: p.draw_pile.len(), draw_pile_count: p.draw_pile.len(),
hand_count: p.hand.len(), hand_count: p.hand.len(),
discard_pile: p.discard_pile.last().map(|c| c.clone()), discard_pile: p.discard_pile.last().map(|c| c.name.clone()),
played_cards: p.played_cards.clone(), played_cards: p.played_cards.iter().map(|c| c.name.clone()).collect(),
}) })
.collect(), .collect(),
active_player: game.active_player, active_player: game.active_player,
supply: game supply: game
.supply .supply
.iter() .iter()
.map(|(name, count)| PileState { .map(|(card, count)| PileState {
name: name.into(), name: card.clone(),
count: count.clone(), count: count.clone(),
}) })
.collect(), .collect(),
turn_state: TurnState { turn_state: game.turn_state.clone(),
actions: 1,
buys: 1,
coin: 0,
},
trash: game.trash.clone(), trash: game.trash.clone(),
}; };
@ -331,12 +442,15 @@ async fn broadcast_state(game: &Game) {
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, p)| { .map(|(i, p)| {
let score = p.draw_pile.iter().fold(0, |acc, card| match card.as_str() { let score = p
"Province" => acc + 6, .draw_pile
"Duchery" => acc + 3, .iter()
"Estate" => acc + 1, .fold(0, |acc, card| match card.name.as_str() {
_ => acc, "Province" => acc + 6,
}); "Duchery" => acc + 3,
"Estate" => acc + 1,
_ => acc,
});
(i, score) (i, score)
}) })
.sorted_by(|a, b| Ord::cmp(&b.1, &a.1)) .sorted_by(|a, b| Ord::cmp(&b.1, &a.1))
@ -537,8 +651,8 @@ async fn main() -> Result<(), std::io::Error> {
let mut games = req.state().games.write().await; let mut games = req.state().games.write().await;
let game = games.get_mut(&game_id).unwrap(); let game = games.get_mut(&game_id).unwrap();
let card_name = game.players[player_number].hand[index].clone(); let card_name = game.players[player_number].hand[index].name.clone();
game.players[player_number].play_card(index); game.play_card(player_number, index);
notify_players( notify_players(
&game, &game,
@ -554,15 +668,13 @@ async fn main() -> Result<(), std::io::Error> {
game.supply.get_mut(index).unwrap().1 = game.supply.get_mut(index).unwrap().1 =
game.supply.get(index).unwrap().1 - 1; game.supply.get(index).unwrap().1 - 1;
let card_name = game.supply[index].0.clone(); let card = game.supply[index].0.clone();
game.players[player_number] game.players[player_number].discard_pile.push(card.clone());
.discard_pile
.push(card_name.clone());
notify_players( notify_players(
&game, &game,
format!("{} nimmt {}", game.players[player_number].name, card_name), format!("{} nimmt {}", game.players[player_number].name, card.name),
) )
.await; .await;
broadcast_state(&game).await; broadcast_state(&game).await;

View File

@ -883,7 +883,7 @@ img.card:hover {
setup_state = { setup_state = {
starting_deck: [], starting_deck: [],
basic_cards: ["Copper", "Silver", "Gold", "Estate", "Duchery", "Province", "Curse"], basic_cards: ["Copper", "Silver", "Gold", "Estate", "Duchy", "Province", "Curse"],
kingdom_cards: ["Cellar", "Moat", "Village", "Merchant", "Workshop", "Smithy", "Remodel", "Militia", "Market", "Mine"], kingdom_cards: ["Cellar", "Moat", "Village", "Merchant", "Workshop", "Smithy", "Remodel", "Militia", "Market", "Mine"],
socket: webSocket socket: webSocket
} }

BIN
static/images/cards/duchery.jpg (Stored with Git LFS)

Binary file not shown.