1use super::digu::{eval_hand, Score};
2use super::stack::{Stack, DECK};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6
7#[derive(Serialize, Deserialize, JsonSchema)]
8pub enum Action {
9 InitiateDraw,
10 FinalizeDraw(Option<usize>),
11 Swap(usize),
12 Forfeit,
13}
14
15#[derive(Clone, Serialize, Deserialize, JsonSchema)]
16pub struct Outcome {
17 pub winner: u8,
18 pub scores: Vec<Score>,
19}
20
21#[derive(Serialize, Deserialize, JsonSchema)]
22pub struct PublicState {
23 pub completed: bool,
24 pub steps: u32,
25 pub draw_in_progress: bool,
26 pub active_player: u8,
27 pub n_players: u8,
28 pub forfeitures: HashSet<u8>,
29 pub pile: Vec<u8>,
30 pub outcome: Option<Outcome>,
31}
32
33#[derive(Clone, Serialize, Deserialize, JsonSchema)]
34pub struct PrivateState {
35 pub hand: [u8; 10],
36 pub deck_top: Option<u8>,
37}
38
39#[derive(Serialize, Deserialize, JsonSchema)]
40pub struct Game {
41 completed: bool,
42 steps: u32,
43 draw_in_progress: bool,
44 active_player: u8,
45 n_players: u8,
46 forfeitures: HashSet<u8>,
47 deck: Stack,
48 pile: Stack,
49 hands: HashMap<u8, [u8; 10]>,
50 outcome: Option<Outcome>,
51}
52
53impl Game {
54 pub fn new(n_players: u8) -> Result<(Self, PublicState, Vec<PrivateState>), String> {
55 if n_players == 1 || n_players > 4 {
56 return Err(String::from("Invalid number of players"));
57 }
58
59 let mut deck = Stack::new(DECK.to_vec());
60 deck.shuffle();
61
62 let mut hands: HashMap<u8, [u8; 10]> = HashMap::new();
63 for i in 0..n_players {
64 let mut hand: [u8; 10] = [0; 10];
65 for i in 0..10 {
66 hand[i] = deck.deal().unwrap();
67 }
68 hands.insert(i, hand);
69 }
70
71 let forfeitures: HashSet<u8> = HashSet::new();
72 let pile = Stack::new(vec![]);
73
74 let gme = Self {
75 completed: false,
76 steps: 0,
77 draw_in_progress: false,
78 active_player: 0,
79 n_players,
80 forfeitures: forfeitures.clone(),
81 deck,
82 pile: pile.clone(),
83 hands,
84 outcome: None,
85 };
86
87 let public_state = PublicState {
88 completed: gme.completed,
89 steps: 0,
90 draw_in_progress: gme.draw_in_progress,
91 active_player: gme.active_player,
92 n_players: gme.n_players,
93 forfeitures: forfeitures,
94 pile: pile.list(),
95 outcome: None,
96 };
97
98 let mut private_states: Vec<PrivateState> = vec![];
99 for i in 0..n_players {
100 private_states.push(PrivateState {
101 hand: *gme.hands.get(&i).unwrap(),
102 deck_top: None,
103 });
104 }
105
106 Ok((gme, public_state, private_states))
107 }
108
109 pub fn step(&mut self, action: Action) -> Result<(PublicState, Vec<PrivateState>), String> {
110 if self.completed {
111 return Err(String::from("Game is already over"));
112 }
113
114 if self.draw_in_progress {
116 if !(matches!(action, Action::FinalizeDraw(_)) || matches!(action, Action::Forfeit)) {
117 return Err(String::from(
118 "Invalid action. Expected FinalizeDraw or Forfeit",
119 ));
120 }
121 }
122
123 let active_player = self.active_player;
124 let hand = self.hands.get_mut(&active_player).unwrap();
125 match action {
126 Action::InitiateDraw => {
127 self.draw_in_progress = true;
128 }
129 Action::FinalizeDraw(possible_discarded_index) => {
130 if let Some(discarded_index) = possible_discarded_index {
131 match discarded_index {
132 0..=9 => {
133 self.pile.stack(hand[discarded_index]);
134 hand[discarded_index] = self.deck.deal().unwrap();
135 }
136 _ => {
137 return Err(String::from(
138 "Invalid index, please provide a value between 0 and 9",
139 ));
140 }
141 }
142 } else {
143 self.pile.stack(self.deck.deal().unwrap());
144 }
145
146 self.draw_in_progress = false;
147 }
148 Action::Swap(discarded_index) => {
149 if self.pile.is_empty() {
150 return Err(String::from("Pile is empty, Please choose another action"));
151 }
152
153 match discarded_index {
154 0..=9 => {
155 let discarded_card = hand[discarded_index];
156 hand[discarded_index] = self.pile.deal().unwrap();
157 self.pile.stack(discarded_card);
158 }
159 _ => {
160 return Err(String::from(
161 "Invalid index, please provide a value between 0 and 9",
162 ))
163 }
164 }
165 }
166 Action::Forfeit => {
167 for (_, &c) in hand.iter().enumerate() {
168 self.deck.stack(c);
169 }
170 self.deck.shuffle();
171 self.forfeitures.insert(active_player);
172 self.draw_in_progress = false;
173 }
174 }
175
176 self.steps += 1;
177
178 if !self.draw_in_progress {
179 loop {
180 self.active_player = (self.active_player + 1) % self.n_players;
181 if !self.forfeitures.contains(&self.active_player) {
182 break;
183 }
184 if self.active_player == active_player {
185 break;
186 }
187 }
188 }
189
190 if self.deck.is_empty() {
192 self.deck = Stack::new(self.pile.dump());
193 self.deck.shuffle();
194 }
195
196 let score = eval_hand(hand);
198 if score.winner || self.forfeitures.len() == usize::from(self.n_players - 1) {
199 let mut outcome = Outcome {
200 winner: active_player,
201 scores: vec![],
202 };
203
204 for p in 0..self.n_players {
205 if p == active_player {
206 outcome.scores.push(score.clone());
207 continue;
208 }
209
210 let hand = self.hands.get(&p).unwrap();
211 let score = eval_hand(hand);
212 outcome.scores.push(score);
213 }
214
215 self.outcome = Some(outcome);
216 self.completed = true;
217 }
218
219 let public_state = PublicState {
220 completed: self.completed,
221 steps: self.steps,
222 draw_in_progress: self.draw_in_progress,
223 active_player: self.active_player,
224 n_players: self.n_players,
225 forfeitures: self.forfeitures.clone(),
226 pile: self.pile.list(),
227 outcome: self.outcome.clone(),
228 };
229
230 let mut private_states: Vec<PrivateState> = vec![];
231 for i in 0..self.n_players {
232 private_states.push(PrivateState {
233 hand: *self.hands.get(&i).unwrap(),
234 deck_top: match self.draw_in_progress && i == active_player {
235 true => Some(self.deck.top().unwrap()),
236 false => None,
237 },
238 });
239 }
240
241 Ok((public_state, private_states))
242 }
243}