Add card structure and treasure card type
This commit is contained in:
parent
a8246888ee
commit
d78b8b1416
236
src/main.rs
236
src/main.rs
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user