fantasy_realms_unofficial_api/lib.rs
1#[cfg(test)]
2mod tests;
3pub mod deck;
4pub mod card_collection;
5pub mod hand;
6
7use crate::card_collection::{CardCollection, CardCollectionContains};
8use crate::hand::{Hand, Turn};
9use crate::deck::Card;
10
11/// Represents a player with complete information.
12#[derive(Debug, PartialEq, Eq, Clone)]
13pub struct Player {
14 /// The players name.
15 pub name: String,
16 /// The players hand.
17 pub hand: Hand,
18 /// The cards in this players hand that are known by opponents.
19 pub cards_known_to_opponents: CardCollection,
20} impl Player {
21 /// Creates a new `Player` instance.
22 /// `cards_known_to_opponents` is initialized as empty,
23 /// as no cards are known to opponents at the start of the game.
24 /// # Arguments
25 /// * `name` - A `String` representing the player's name.
26 /// * `hand` - A `Hand` containing the cards initially dealt to the player.
27 /// # Returns
28 /// A new `Player` instance.
29 pub fn new(name: String, hand: Hand) -> Self {
30 Player {
31 name: name,
32 hand: hand,
33 cards_known_to_opponents: CardCollection::new()
34 }
35 }
36}
37
38/// Represents a game with complete information.
39#[derive(Debug, PartialEq, Eq, Clone)]
40pub struct Game {
41 /// The current turn.
42 /// This value is zero-indexed, and ranges from `0` to `number_of_players - 1`.
43 pub current_turn: usize,
44 /// A collection of cards in the discard pile.
45 pub discard_pile: CardCollection,
46 /// A collection of cards in the deck.
47 pub deck: CardCollection,
48 /// A list of the players playing the game.
49 pub players: Vec<Player>,
50 /// Weather or not the game is over.
51 pub over: bool,
52} impl Game {
53 /// Creates a new `Game` instance.
54 /// `current_turn` is initialized to 0.
55 /// `discard_pile` is initialized as empty,
56 /// as the discard pile is empty at the start of the game.
57 /// `deck` is initialized to all cards not in the hands of players.
58 /// `over` is initialized to false.
59 /// # Arguments
60 /// * `players` - A `Vec<Player>` that contains all players in the game
61 /// # Errors
62 /// This function returns an `Err(String)` if:
63 /// * The number of `players` is not between 3 and 6 (inclusive).
64 /// * The same card is found in multiple hands.
65 /// # Returns
66 /// A `Result<Self, String>` which is:
67 /// * `Ok(Game)` if the game can be successfully initialized.
68 /// * `Err(String)` containing an error message if validation fails.
69 pub fn new(players: Vec<Player>) -> Result<Self, String> {
70 if players.len() < 3 || players.len() > 6 {
71 return Err("Number of players must be between 3 and 6.".to_string());
72 }
73 let unique_hand_cards: CardCollection = players
74 .iter()
75 .fold(CardCollection::new(),
76 |mut acc, player| {
77 for card in &player.hand {
78 acc += *card;
79 }
80 acc
81 });
82 if unique_hand_cards.len() != 7 * players.len() as u32 {
83 return Err("Same card found in multiple hands.".to_string());
84 }
85 Ok(Game {
86 current_turn: 0,
87 players: players,
88 discard_pile: CardCollection::new(),
89 deck: (!CardCollection::new()) - unique_hand_cards,
90 over: false,
91 })
92 }
93
94 /// Plays a single turn in the game based on the provided `Turn` action.
95 /// This function validates a draw and discard action and updates the game state accordingly.
96 /// # Arguments
97 /// * `turn` - A `Turn` represents a draw and a discard action.
98 /// # Errors
99 /// This function returns an `Err(String)` if:
100 /// * The `discard_pile` already contains 10 or more cards.
101 /// * The card the player attempts to `draw` is not available in the deck or discard pile.
102 /// * The card the current player attempts to `discard` is not found in their own hand.
103 /// # Side Effects
104 /// * **`discard_pile`**:
105 /// * Removes draw card if drawn from discard pile
106 /// * Adds discarded card to the
107 /// * **`deck`**:
108 /// * Removes draw card if drawn from deck
109 /// * **`players[self.current_turn].hand`**: The current player's hand,
110 /// * Removes discard card
111 /// * Adds draw card
112 /// * **`players[self.current_turn].cards_known_to_opponents`**: The
113 /// * Adds draw card if drawn from discard pile
114 /// * Removes discard card if known
115 /// * **`current_turn`**:
116 /// * Incremented to the next player's turn
117 /// * **`over`**:
118 /// * Set to `true` if the `discard_pile` reaches 10 cards
119 /// # Returns
120 /// A `Result<(), String>` which is:
121 /// * `Ok(())` if the turn was executed successfully
122 /// * `Err(String)` containing an error message if validation fails
123 pub fn play_turn(&mut self, turn: Turn) -> Result<(), String> {
124 if self.discard_pile.len() >= 10 {
125 return Err("Game is over.".to_string());
126 }
127 if self.players.iter().any(|player| player.hand.contains(&turn.draw)) {
128 return Err(format!("{:?} cannot be drawn.", turn.draw.name))
129 }
130 if turn.discard == turn.draw {
131 if !self.discard_pile.contains(turn.draw) {
132 self.discard_pile += turn.discard;
133 self.deck -= turn.draw;
134 }
135 if self.discard_pile.len() == 10 {
136 self.over = true;
137 } else {
138 self.current_turn = (self.current_turn + 1) % self.players.len();
139 }
140 return Ok(());
141 }
142 if !self.players[self.current_turn].hand.contains(&turn.discard) {
143 return Err(format!("{:?} not found in hand.", turn.discard.name));
144 }
145 self.discard_pile += turn.discard;
146 self.players[self.current_turn].hand.play(&turn);
147 self.players[self.current_turn].cards_known_to_opponents -= turn.discard;
148 if self.discard_pile.contains(turn.draw) {
149 self.discard_pile -= turn.draw;
150 self.players[self.current_turn].cards_known_to_opponents += turn.draw;
151 } else {
152 self.deck -= turn.draw;
153 }
154 if self.discard_pile.len() == 10 {
155 self.over = true;
156 } else {
157 self.current_turn = (self.current_turn + 1) % self.players.len();
158 }
159 Ok(())
160 }
161}
162
163/// Represents a player with incomplete information.
164#[derive(Debug, PartialEq, Eq, Clone)]
165pub struct PartialPlayer {
166 /// The name of the player.
167 pub name: String,
168 /// The cards in this players hand that are known by opponents.
169 pub cards_known_to_opponents: CardCollection,
170} impl PartialPlayer {
171 /// Creates a new `PartialPlayer` instance.
172 /// `cards_known_to_opponents` is initialized as empty,
173 /// as no cards are known to opponents at the start of the game.
174 /// # Arguments
175 /// * `name` - A `String` representing the player's name.
176 /// # Returns
177 /// A new `PartialPlayer` instance.
178 pub fn new(name: String) -> Self {
179 PartialPlayer {
180 name: name,
181 cards_known_to_opponents: CardCollection::new()
182 }
183 }
184}
185
186/// Represents the two options for a player in a game with incomplete information.
187/// # Variants
188/// * `Human (PartialPlayer)` - A player with information unknown by the program.
189/// * `Bot (Player)` - A player with all information know by the program.
190#[derive(Debug, PartialEq, Eq, Clone)]
191pub enum PartialGamePlayer {
192 Human (PartialPlayer),
193 Bot (Player),
194}
195
196/// Represents the two options for a player of a `PartialGame`.
197/// # Variants
198/// * `Human` (PartialTurn) - A turn with information unknown by the program.
199/// * `Bot` (Turn) - A turn with all information know by the program.
200#[derive(Debug, PartialEq, Eq, Clone, Copy)]
201pub enum PartialGameTurn {
202 Human (PartialTurn),
203 Bot (Turn),
204}
205
206/// Represents the two options for a card that is drawn.
207/// # Variants
208/// * `Deck` - A card drawn from the deck.
209/// * `Discard (Card)` - A card drawn from the discard pile.
210#[derive(Debug, PartialEq, Eq, Clone, Copy)]
211pub enum DrawCard {
212 Deck,
213 Discard (Card),
214}
215
216/// Represents a turn with incomplete information
217#[derive(Debug, PartialEq, Eq, Clone, Copy)]
218pub struct PartialTurn {
219 /// The card drawn, either a specific card from the
220 /// discard pile or an unkown card from the deck
221 pub draw: DrawCard,
222 /// The card discarded
223 pub discard: Card,
224} impl PartialTurn {
225 /// Creates a new `PartialTurn` instance.
226 /// # Arguments
227 /// * `draw` - A `DrawCard` representing the card drawn.
228 /// * `discard` - A `Card` representing the card discarded.
229 /// # Returns
230 /// A new `PartialTurn` instance.
231 pub fn new(draw: DrawCard, discard: Card) -> Self {
232 PartialTurn {draw: draw, discard: discard}
233 }
234}
235
236/// Represents a game with some incomplete information.
237#[derive(Debug, PartialEq, Eq, Clone)]
238pub struct PartialGame {
239 /// The current turn.
240 /// This value is zero-indexed, and ranges from `0` to `number_of_players - 1`.
241 pub current_turn: usize,
242 /// A collection of cards in the discard pile.
243 pub discard_pile: CardCollection,
244 /// A list of the players playing the game.
245 pub players: Vec<PartialGamePlayer>,
246 /// Weather or not the game is over.
247 pub over: bool,
248} impl PartialGame {
249 /// Creates a new `PartialGame` instance.
250 /// `current_turn` is initialized to 0.
251 /// `discard_pile` is initialized as empty,
252 /// as the discard pile is empty at the start of the game.
253 /// `over` is initialized to false.
254 /// # Arguments
255 /// * `players` - A `Vec<PartialGamePlayer>` that contains all players in the game
256 /// # Errors
257 /// This function returns an `Err(String)` if:
258 /// * The number of `players` is not between 3 and 6 (inclusive).
259 /// # Returns
260 /// A `Result<Self, String>` which is:
261 /// * `Ok(Game)` if the game can be successfully initialized.
262 /// * `Err(String)` containing an error message if validation fails.
263 pub fn new(players: Vec<PartialGamePlayer>) -> Result<Self, String> {
264 if players.len() < 3 || players.len() > 6 {
265 return Err("Number of players must be between 3 and 6.".to_string());
266 }
267 Ok(PartialGame {
268 current_turn: 0,
269 players: players,
270 discard_pile: CardCollection::new(),
271 over: false,
272 })
273 }
274
275 /// Plays a single turn in the game based on the provided `PartialGameTurn` action.
276 /// This function validates a draw and discard action and updates the game state accordingly.
277 /// # Arguments
278 /// * `turn` - A `PartialGameTurn` represents a draw and a discard action.
279 /// # Errors
280 /// This function returns an `Err(String)` if:
281 /// * The `discard_pile` already contains 10 or more cards.
282 /// * The card the player attempts to `draw` is not available in the deck or discard pile.
283 /// * The card the current player attempts to `discard` is not found in their own hand.
284 /// # Side Effects
285 /// * **`discard_pile`**:
286 /// * Removes draw card if drawn from discard pile
287 /// * Adds discarded card to the
288 /// * **`current_turn`**:
289 /// * Incremented to the next player's turn
290 /// * **`over`**:
291 /// * Set to `true` if the `discard_pile` reaches 10 cards
292 /// ## Human Turn
293 /// * **`players[self.current_turn].cards_known_to_opponents`**: The
294 /// * Adds draw card if drawn from discard pile
295 /// * Removes discard card if known
296 /// ## Bot Turn
297 /// * **`players[self.current_turn].hand`**: The current player's hand,
298 /// * Removes discard card
299 /// * Adds draw card
300 /// * **`players[self.current_turn].cards_known_to_opponents`**: The
301 /// * Adds draw card if drawn from discard pile
302 /// * Removes discard card if known
303 /// # Returns
304 /// A `Result<(), String>` which is:
305 /// * `Ok(())` if the turn was executed successfully
306 /// * `Err(String)` containing an error message if validation fails
307 pub fn play_turn(&mut self, turn: PartialGameTurn) -> Result<(), String> {
308 if self.discard_pile.len() >= 10 {
309 return Err("Game is over.".to_string());
310 }
311 match turn {
312 PartialGameTurn::Human (turn) => self.play_human_turn(turn),
313 PartialGameTurn::Bot (turn) => self.play_bot_turn(turn),
314 }
315 }
316
317 /// Helper function of `play_turn`
318 /// Plays a single turn in the game for a human provided a `PartialTurn` action.
319 /// # Errors
320 /// This function returns an `Err(String)` if:
321 /// * the current turn is a `PartialGamePlayer (Bot)`
322 /// # Returns
323 /// A `Result<(), String>` which is:
324 /// * `Ok(())` if the turn was executed successfully
325 /// * `Err(String)` containing an error message if validation fails
326 fn play_human_turn(&mut self, turn: PartialTurn) -> Result<(), String> {
327 let player = match &mut self.players[self.current_turn] {
328 PartialGamePlayer::Human (player) => player,
329 PartialGamePlayer::Bot (_) => {
330 return Err("Received a bot turn on a human player's turn".to_string());
331 }
332 };
333 if let DrawCard::Discard (card) = turn.draw {
334 player.cards_known_to_opponents += card;
335 self.discard_pile -= card;
336 }
337 player.cards_known_to_opponents -= turn.discard;
338 self.discard_pile += turn.discard;
339 if self.discard_pile.len() == 10 {
340 self.over = true;
341 } else {
342 self.current_turn = (self.current_turn + 1) % self.players.len();
343 }
344 Ok(())
345 }
346
347 /// Helper function of `play_turn`
348 /// Plays a single turn in the game for a bot provided a `PartialTurn` action.
349 /// # Errors
350 /// This function returns an `Err(String)` if:
351 /// * the current turn is a `PartialGamePlayer (Human)`
352 /// # Returns
353 /// A `Result<(), String>` which is:
354 /// * `Ok(())` if the turn was executed successfully
355 /// * `Err(String)` containing an error message if validation fails
356 fn play_bot_turn(&mut self, turn: Turn) -> Result<(), String> {
357 let player = match &mut self.players[self.current_turn] {
358 PartialGamePlayer::Human (_) => {
359 return Err("Received a human turn on a bot player's turn".to_string());
360 }
361 PartialGamePlayer::Bot (player) => player,
362 };
363 if self.discard_pile.contains(turn.draw){
364 player.cards_known_to_opponents += turn.draw;
365 }
366 if turn.discard == turn.draw {
367 if !self.discard_pile.contains(turn.draw) {
368 self.discard_pile += turn.discard;
369 }
370 if self.discard_pile.len() == 10 {
371 self.over = true;
372 } else {
373 self.current_turn = (self.current_turn + 1) % self.players.len();
374 }
375 return Ok(());
376 }
377 if !player.hand.contains(&turn.discard) {
378 return Err(format!("{:?} not found in hand.", turn.discard.name));
379 }
380 player.hand.play(&turn);
381 player.cards_known_to_opponents -= turn.discard;
382 self.discard_pile += turn.discard;
383 if self.discard_pile.contains(turn.draw) {
384 self.discard_pile -= turn.draw;
385 player.cards_known_to_opponents += turn.draw;
386 }
387 if self.discard_pile.len() == 10 {
388 self.over = true;
389 } else {
390 self.current_turn = (self.current_turn + 1) % self.players.len();
391 }
392 Ok(())
393 }
394}