1use std::{cmp::Ordering, error::Error, fmt::Display, usize};
2
3use rand::prelude::*;
4use serde_derive::{Deserialize, Serialize};
5use uuid::Uuid;
6
7pub const DECK_SIZE: usize = 52;
8pub const PLAYER_CARD_SIZE: usize = 13;
9pub const NUMBER_REPLACEABLE_CARDS: usize = 3;
10pub const PLAYER_NUMBER: usize = 4;
11pub const CARD_TO_START: Card = Card::Number(2, TypeCard::Club, "🃒");
12pub const QUEEN_OF_SPADE: Card = Card::Queen(TypeCard::Spade, "🂭");
13pub const ACE_OF_HEARTS: Card = Card::Ace(TypeCard::Heart, "🂱");
14
15pub const MAX_SCORE: usize = 26;
16
17const GREATER: Option<Ordering> = Some(Ordering::Greater);
18const LESS: Option<Ordering> = Some(Ordering::Less);
19const EQUAL: Option<Ordering> = Some(Ordering::Equal);
20
21pub type PositionInDeck = usize;
22
23pub fn get_card_by_idx(idx: usize) -> &'static Card {
24 &DECK.0[idx]
25}
26
27#[derive(Debug)]
28struct StackState {
29 first_card_played_pos: usize,
30 current_losing_player_pos: usize,
31 current_losing_card_pos: usize,
32 score: usize,
33}
34
35#[derive(Clone, Copy, Serialize, Debug, Deserialize, PartialEq, Eq)]
36pub struct PlayerState {
37 pub player_id: Uuid,
38 pub score: usize,
39}
40
41#[derive(Debug, Serialize, Copy, Clone, Deserialize, PartialEq)]
42#[serde(rename_all = "UPPERCASE")]
43pub enum Card {
44 Queen(TypeCard, &'static str),
45 King(TypeCard, &'static str),
46 Jack(TypeCard, &'static str),
47 Ace(TypeCard, &'static str),
48 Number(u8, TypeCard, &'static str),
49}
50
51impl Display for Card {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 let emoji = self.get_emoji();
54 write!(f, "{emoji}")
55 }
56}
57
58impl Display for Player {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 let card_emojis = self
61 .get_cards()
62 .map(|c| if let Some(c) = c { c.get_emoji() } else { "-" })
63 .join(" ");
64 write!(f, "Player {}: {}", self.id, card_emojis)
65 }
66}
67
68impl Card {
69 pub const fn get_emoji(&self) -> &'static str {
70 match self {
71 Card::King(_, emoji)
72 | Card::Jack(_, emoji)
73 | Card::Queen(_, emoji)
74 | Card::Ace(_, emoji)
75 | Card::Number(_, _, emoji) => emoji,
76 }
77 }
78 pub const fn get_value(&self) -> usize {
79 match self {
80 Card::Queen(TypeCard::Spade, _) => 13,
81 Card::Ace(TypeCard::Heart, _)
82 | Card::King(TypeCard::Heart, _)
83 | Card::Queen(TypeCard::Heart, _)
84 | Card::Jack(TypeCard::Heart, _)
85 | Card::Number(_, TypeCard::Heart, _) => 1,
86 _ => 0,
87 }
88 }
89
90 pub const fn get_type(&self) -> &TypeCard {
91 match self {
92 Card::Queen(TypeCard::Heart, _)
93 | Card::King(TypeCard::Heart, _)
94 | Card::Ace(TypeCard::Heart, _)
95 | Card::Number(_, TypeCard::Heart, _)
96 | Card::Jack(TypeCard::Heart, _) => &TypeCard::Heart,
97 Card::King(TypeCard::Diamond, _)
98 | Card::Queen(TypeCard::Diamond, _)
99 | Card::Ace(TypeCard::Diamond, _)
100 | Card::Number(_, TypeCard::Diamond, _)
101 | Card::Jack(TypeCard::Diamond, _) => &TypeCard::Diamond,
102 Card::King(TypeCard::Spade, _)
103 | Card::Queen(TypeCard::Spade, _)
104 | Card::Ace(TypeCard::Spade, _)
105 | Card::Number(_, TypeCard::Spade, _)
106 | Card::Jack(TypeCard::Spade, _) => &TypeCard::Spade,
107 Card::King(TypeCard::Club, _)
108 | Card::Queen(TypeCard::Club, _)
109 | Card::Ace(TypeCard::Club, _)
110 | Card::Number(_, TypeCard::Club, _)
111 | Card::Jack(TypeCard::Club, _) => &TypeCard::Club,
112 }
113 }
114}
115impl PartialOrd for Card {
116 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
118 match (self.get_type(), other.get_type()) {
119 (TypeCard::Heart, TypeCard::Spade)
120 | (TypeCard::Heart, TypeCard::Diamond)
121 | (TypeCard::Heart, TypeCard::Club) => return GREATER,
122 (TypeCard::Spade, TypeCard::Heart)
123 | (TypeCard::Diamond, TypeCard::Heart)
124 | (TypeCard::Club, TypeCard::Heart) => return LESS,
125 _ => {}
126 }
127 match (self, other) {
128 (Card::Queen(_, _), Card::Queen(_, _)) => EQUAL,
129 (Card::Queen(_, _), Card::King(_, _)) => LESS,
130 (Card::Queen(_, _), Card::Jack(_, _)) => GREATER,
131 (Card::Queen(_, _), Card::Ace(_, _)) => LESS,
132 (Card::Queen(_, _), Card::Number(_, _, _)) => GREATER,
133 (Card::King(_, _), Card::Queen(_, _)) => GREATER,
134 (Card::King(_, _), Card::King(_, _)) => EQUAL,
135 (Card::King(_, _), Card::Jack(_, _)) => GREATER,
136 (Card::King(_, _), Card::Ace(_, _)) => LESS,
137 (Card::King(_, _), Card::Number(_, _, _)) => GREATER,
138 (Card::Jack(_, _), Card::Queen(_, _)) => LESS,
139 (Card::Jack(_, _), Card::King(_, _)) => LESS,
140 (Card::Jack(_, _), Card::Jack(_, _)) => EQUAL,
141 (Card::Jack(_, _), Card::Ace(_, _)) => LESS,
142 (Card::Jack(_, _), Card::Number(_, _, _)) => GREATER,
143 (Card::Ace(_, _), Card::Queen(_, _)) => GREATER,
144 (Card::Ace(_, _), Card::King(_, _)) => GREATER,
145 (Card::Ace(_, _), Card::Jack(_, _)) => GREATER,
146 (Card::Ace(_, _), Card::Ace(_, _)) => EQUAL,
147 (Card::Ace(_, _), Card::Number(_, _, _)) => GREATER,
148 (Card::Number(_, _, _), Card::Queen(_, _)) => LESS,
149 (Card::Number(_, _, _), Card::King(_, _)) => LESS,
150 (Card::Number(_, _, _), Card::Jack(_, _)) => LESS,
151 (Card::Number(_, _, _), Card::Ace(_, _)) => LESS,
152 (Card::Number(n1, _, _), Card::Number(n2, _, _)) => n1.partial_cmp(n2),
153 }
154 }
155}
156#[derive(Debug, Serialize, Copy, Clone, Deserialize, PartialEq)]
157#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
158pub enum TypeCard {
159 Heart, Spade, Diamond, Club, }
164
165#[derive(Debug)]
166pub struct Deck([Card; DECK_SIZE]);
167
168impl TryFrom<&str> for &Card {
169 type Error = GameError;
170
171 fn try_from(value: &str) -> Result<Self, Self::Error> {
172 if let Some(card) = DECK.0.iter().find(|c| c.get_emoji() == value) {
173 Ok(card)
174 } else {
175 Err(GameError::UnknownCard)
176 }
177 }
178}
179
180impl Deck {
181 pub const fn new() -> Self {
182 Deck([
183 CARD_TO_START,
184 Card::Number(3, TypeCard::Club, "🃓"),
185 Card::Number(4, TypeCard::Club, "🃔"),
186 Card::Number(5, TypeCard::Club, "🃕"),
187 Card::Number(6, TypeCard::Club, "🃖"),
188 Card::Number(7, TypeCard::Club, "🃗"),
189 Card::Number(8, TypeCard::Club, "🃘"),
190 Card::Number(9, TypeCard::Club, "🃙"),
191 Card::Number(10, TypeCard::Club, "🃚"),
192 Card::Jack(TypeCard::Club, "🃛"),
193 Card::Queen(TypeCard::Club, "🃝"),
194 Card::King(TypeCard::Club, "🃞"),
195 Card::Ace(TypeCard::Club, "🃑"),
196 Card::Number(2, TypeCard::Diamond, "🃂"),
197 Card::Number(3, TypeCard::Diamond, "🃃"),
198 Card::Number(4, TypeCard::Diamond, "🃄"),
199 Card::Number(5, TypeCard::Diamond, "🃅"),
200 Card::Number(6, TypeCard::Diamond, "🃆"),
201 Card::Number(7, TypeCard::Diamond, "🃇"),
202 Card::Number(8, TypeCard::Diamond, "🃈"),
203 Card::Number(9, TypeCard::Diamond, "🃉"),
204 Card::Number(10, TypeCard::Diamond, "🃊"),
205 Card::Jack(TypeCard::Diamond, "🃋"),
206 Card::Queen(TypeCard::Diamond, "🃍"),
207 Card::King(TypeCard::Diamond, "🃎"),
208 Card::Ace(TypeCard::Diamond, "🃁"),
209 Card::Number(2, TypeCard::Spade, "🂢"),
210 Card::Number(3, TypeCard::Spade, "🂣"),
211 Card::Number(4, TypeCard::Spade, "🂤"),
212 Card::Number(5, TypeCard::Spade, "🂥"),
213 Card::Number(6, TypeCard::Spade, "🂦"),
214 Card::Number(7, TypeCard::Spade, "🂧"),
215 Card::Number(8, TypeCard::Spade, "🂨"),
216 Card::Number(9, TypeCard::Spade, "🂩"),
217 Card::Number(10, TypeCard::Spade, "🂪"),
218 Card::Jack(TypeCard::Spade, "🂫"),
219 QUEEN_OF_SPADE,
220 Card::King(TypeCard::Spade, "🂮"),
221 Card::Ace(TypeCard::Spade, "🂡"),
222 Card::Number(2, TypeCard::Heart, "🂲"),
223 Card::Number(3, TypeCard::Heart, "🂳"),
224 Card::Number(4, TypeCard::Heart, "🂴"),
225 Card::Number(5, TypeCard::Heart, "🂵"),
226 Card::Number(6, TypeCard::Heart, "🂶"),
227 Card::Number(7, TypeCard::Heart, "🂷"),
228 Card::Number(8, TypeCard::Heart, "🂸"),
229 Card::Number(9, TypeCard::Heart, "🂹"),
230 Card::Number(10, TypeCard::Heart, "🂺"),
231 Card::Jack(TypeCard::Heart, "🂻"),
232 Card::Queen(TypeCard::Heart, "🂽"),
233 Card::King(TypeCard::Heart, "🂾"),
234 ACE_OF_HEARTS,
235 ])
236 }
237}
238
239const DECK: Deck = Deck::new();
240
241#[derive(Copy, Clone, Debug)]
242pub struct Player {
243 id: Uuid,
244 score: usize,
245 cards: [Option<usize>; PLAYER_CARD_SIZE], is_bot: bool,
247}
248
249impl Player {
250 pub fn new(id: Uuid, is_bot: bool, cards: [Option<usize>; PLAYER_CARD_SIZE]) -> Self {
251 Self {
252 id,
253 score: 0,
254 cards,
255 is_bot,
256 }
257 }
258 fn replace_cards(&mut self, new_cards: [usize; NUMBER_REPLACEABLE_CARDS]) {
259 let empty_slots: Vec<&mut Option<usize>> =
260 self.cards.iter_mut().filter(|c| c.is_none()).collect();
261
262 if empty_slots.len() != NUMBER_REPLACEABLE_CARDS {
263 panic!("{NUMBER_REPLACEABLE_CARDS} slots must be empty to replace cards!");
264 }
265 for (i, slot) in empty_slots.into_iter().enumerate() {
266 *slot = Some(new_cards[i]);
267 }
268 }
269 fn remove_card(&mut self, card_p: usize) -> Option<usize> {
270 let pos = self.cards.iter_mut().find(|p| p == &&Some(card_p));
271 if let Some(pos) = pos {
272 pos.take()
273 } else {
274 None
275 }
276 }
277
278 pub fn get_cards(&self) -> [Option<&Card>; 13] {
279 self.cards.map(|c| c.map(get_card_by_idx))
280 }
281 pub fn get_cards_and_pos_in_deck(&self) -> [Option<(PositionInDeck, &Card)>; 13] {
282 let mut cards = self.cards.map(|c| c.map(|c| (c, get_card_by_idx(c))));
283 cards.sort_by(|c1, c2| c1.map(|c11| c11.0).cmp(&c2.map(|c22| c22.0))); cards
286 }
287
288 pub fn has_card(&self, card_p: usize) -> bool {
289 self.cards.iter().any(|c| c == &Some(card_p))
290 }
291 pub fn get_score(&self) -> usize {
292 self.score
293 }
294 pub fn get_id(&self) -> Uuid {
295 self.id
296 }
297 pub fn is_bot(&self) -> bool {
298 self.is_bot
299 }
300}
301
302#[derive(Debug, PartialEq, Copy, Clone)]
303pub enum GameState {
304 ExchangeCards {
305 commands: [Option<(usize, [usize; NUMBER_REPLACEABLE_CARDS])>; PLAYER_NUMBER],
306 },
307 PlayingHand {
308 stack: [Option<(usize, usize)>; PLAYER_NUMBER],
309 current_scores: [usize; PLAYER_NUMBER],
310 },
311 ComputeScore {
312 stack: [Option<(usize, usize)>; PLAYER_NUMBER],
313 current_scores: [usize; PLAYER_NUMBER],
314 },
315 EndHand,
316
317 End,
318}
319impl From<&GameState> for &str {
320 fn from(value: &GameState) -> Self {
321 match value {
322 GameState::ExchangeCards { commands: _ } => "ExchangeCards",
323 GameState::PlayingHand {
324 stack: _,
325 current_scores: _,
326 } => "PlayingHand",
327 GameState::EndHand => "EndHand",
328 GameState::ComputeScore {
329 stack: _,
330 current_scores: _,
331 } => "ComputeScore",
332 GameState::End => "End",
333 }
334 }
335}
336
337#[derive(Debug, Copy, Clone)]
338pub struct Game {
339 pub players: [Player; PLAYER_NUMBER],
340 pub current_player_pos: usize,
341 pub current_hand: u8,
342 back_in_deck: [Option<usize>; DECK_SIZE],
343 pub state: GameState,
344 pub hands: u8,
345}
346
347impl Game {
348 pub fn new(player_builders: [(Uuid, bool); PLAYER_NUMBER], hands: u8) -> Self {
349 let players = player_builders.map(|(player_id, is_bot)| {
350 let player_cards: [Option<usize>; PLAYER_CARD_SIZE] = [None; PLAYER_CARD_SIZE];
351 Player::new(player_id, is_bot, player_cards)
352 });
353
354 let mut game = Self {
355 hands,
356 current_hand: 1,
357 players,
358 state: GameState::ExchangeCards {
359 commands: [None; PLAYER_NUMBER],
360 },
361 back_in_deck: [None; DECK_SIZE],
362 current_player_pos: 0,
363 };
364 game.deal_cards().expect("should be unreachable");
365 game
366 }
367
368 pub fn exchange_cards(
369 &mut self,
370 cards: [usize; NUMBER_REPLACEABLE_CARDS],
371 ) -> Result<(), GameError> {
372 match &mut self.state {
373 GameState::ExchangeCards { commands } if commands.iter().any(|c| c.is_none()) => {
374 let next = commands
375 .iter_mut()
376 .find(|c| c.is_none())
377 .ok_or(GameError::StateError)?;
378 let player = self
379 .players
380 .get_mut(self.current_player_pos)
381 .ok_or(GameError::StateError)?;
382 for card in &cards {
383 if !player.has_card(*card) {
384 return Err(GameError::ForbiddenMove);
385 }
386 player.remove_card(*card);
387 }
388 *next = Some((self.current_player_pos, cards));
389 if self.current_player_pos == PLAYER_NUMBER - 1 {
390 self.current_player_pos = 0;
391 } else {
392 self.current_player_pos += 1;
393 }
394 if commands.iter().all(|c| c.is_some()) {
395 for command in commands {
396 let (player_pos, command) = command.take().unwrap();
397 let next_player_pos = if player_pos == PLAYER_NUMBER - 1 {
398 0
399 } else {
400 player_pos + 1
401 };
402 let next_player = self.players.get_mut(next_player_pos).unwrap();
403 next_player.replace_cards(command);
404 }
405
406 self.current_player_pos = self
407 .players
408 .iter()
409 .enumerate()
410 .find(|(_, p)| p.get_cards().contains(&Some(&CARD_TO_START)))
411 .map(|(idx, _)| idx)
412 .ok_or(GameError::StateError)?;
413 self.state = GameState::PlayingHand {
414 stack: [None; PLAYER_NUMBER],
415 current_scores: [0; PLAYER_NUMBER],
416 }
417 }
418
419 Ok(())
420 }
421 _ => Err(GameError::StateError),
422 }
423 }
424
425 pub fn get_player_cards(
426 &self,
427 player_id: Uuid,
428 ) -> [Option<(PositionInDeck, &Card)>; PLAYER_CARD_SIZE] {
429 if let Some(player) = self.players.iter().find(|p| p.id == player_id) {
430 player.get_cards_and_pos_in_deck()
431 } else {
432 [None; PLAYER_CARD_SIZE]
433 }
434 }
435 fn is_deal_valid(&self) -> bool {
436 !self.players.iter().any(|p| {
437 p.get_cards()
438 .iter()
439 .filter_map(|c| c.as_ref().map(|c| c.get_type()))
440 .filter(|t| !matches!(t, TypeCard::Heart))
441 .count()
442 == 0
443 })
444 }
445 fn deal(&mut self, rng: &mut ThreadRng) {
446 let mut deck_shuffled_positions = [0usize; DECK_SIZE];
447 for (n, item) in deck_shuffled_positions
448 .iter_mut()
449 .enumerate()
450 .take(DECK_SIZE)
451 {
452 *item = n;
453 }
454 deck_shuffled_positions.shuffle(rng);
455 for (pos_player, player) in self.players.iter_mut().enumerate() {
456 let start_pos = pos_player * PLAYER_CARD_SIZE;
457 for (i, random_pos_in_deck) in deck_shuffled_positions
458 .into_iter()
459 .skip(start_pos)
460 .take(PLAYER_CARD_SIZE)
461 .enumerate()
462 {
463 player.cards[i] = Some(random_pos_in_deck);
464 if DECK.0[random_pos_in_deck] == CARD_TO_START {
465 self.current_player_pos = pos_player;
466 }
467 }
468 }
469 }
470
471 pub fn deal_cards(&mut self) -> Result<(), GameError> {
472 let mut rng = thread_rng();
473 match &self.state {
474 GameState::ExchangeCards { commands: _ } => {
475 self.players.shuffle(&mut rng);
476 }
477 GameState::EndHand if self.current_hand <= self.hands => {
478 self.state = GameState::ExchangeCards {
479 commands: [None; PLAYER_NUMBER],
480 };
481 }
482 _ => return Err(GameError::StateError),
483 }
484 while !self.is_deal_valid() {
485 self.deal(&mut rng);
486 }
487
488 for player in &mut self.players {
489 player.cards.sort_by(|c1, c2| match (c1, c2) {
490 (Some(c1), Some(c2)) => c1.cmp(c2),
491 _ => unreachable!(),
492 });
493 }
494 Ok(())
495 }
496 pub fn play_bot(&mut self) -> Result<(), GameError> {
497 if let GameState::PlayingHand {
498 stack: _,
499 current_scores: _,
500 } = &self.state
501 {
502 fn filter_not_empty_slot<'a>(
503 o: &'a Option<(usize, &'a Card)>,
504 ) -> Option<(usize, &'a Card)> {
505 o.as_ref().copied()
506 }
507
508 let player = self.players.get(self.current_player_pos).unwrap();
509
510 let mut cards = player.get_cards_and_pos_in_deck();
511 cards.sort_by(|c1, c2| match (c1, c2) {
512 (Some((_, c1)), Some((_, c2))) => {
513 if c1 == &&ACE_OF_HEARTS || c2 == &&QUEEN_OF_SPADE {
514 Ordering::Less
515 } else if c1 == &&QUEEN_OF_SPADE {
516 Ordering::Greater
517 } else {
518 let Some(ordering) =c1.partial_cmp(c2) else {unreachable!()};
519 ordering
520 }
521 }
522 (None, None) => Ordering::Equal,
523 (Some(_), None) => Ordering::Greater,
524 (None, Some(_)) => Ordering::Less,
525 });
526 let current_stack_state = self.get_current_stack_state();
527
528 let mut min_card: Option<(usize, &Card)> = None;
529 for (idx, card) in cards.iter().filter_map(filter_not_empty_slot) {
531 if let Ok(idx) = self.validate_play(idx) {
532 if let Some((min_idx, min_card)) = &mut min_card {
533 if let Some(current_stack_state) = ¤t_stack_state {
534 let first_card =
535 get_card_by_idx(current_stack_state.first_card_played_pos);
536 let current_losing_card =
537 get_card_by_idx(current_stack_state.current_losing_card_pos);
538
539 let first_card_type = first_card.get_type();
540 let min_card_type = min_card.get_type();
541
542 if min_card_type == first_card_type {
543 if (current_losing_card > card
544 && card > min_card
545 && min_card != &&QUEEN_OF_SPADE)
546 || (current_losing_card < min_card
547 && (card < min_card || min_card == &&QUEEN_OF_SPADE))
548 {
549 (*min_idx, *min_card) = (idx, card);
550 }
551 } else if (card > min_card && min_card != &&QUEEN_OF_SPADE)
552 || card == &QUEEN_OF_SPADE
553 {
554 (*min_idx, *min_card) = (idx, card);
556 }
557 } else if (card < min_card && card != &QUEEN_OF_SPADE)
558 || min_card == &&QUEEN_OF_SPADE
559 {
560 (*min_idx, *min_card) = (idx, card); }
563 } else {
564 min_card = Some((idx, card));
565 }
566 }
567 }
568
569 let Some((min_idx, _)) = min_card else {
570 unreachable!("should still not happen bro, bcos trust me. if it happens,
571 then probably deck not well shuffled")
572 };
573 self.play(min_idx)
574 } else if let GameState::ExchangeCards { commands: _ } = &self.state {
575 let Some(player) = self.players.get(self.current_player_pos) else {unreachable!()};
576 let mut exchange = [0; 3];
577 let player_cards = player.get_cards_and_pos_in_deck();
578
579 for (i, (c, _)) in player_cards.iter().rev().take(3).flatten().enumerate() {
580 exchange[i] = *c;
581 }
582 self.exchange_cards(exchange)
583 } else {
584 Err(GameError::StateError)
585 }
586 }
587
588 pub fn validate_play(&self, card_to_play_idx: usize) -> Result<usize, GameError> {
589 match &self.state {
590 GameState::PlayingHand {
591 stack,
592 current_scores: _,
593 } => {
594 let player = self.players.get(self.current_player_pos).unwrap();
595 if stack.iter().all(|s| s.is_some()) {
596 return Err(GameError::ForbiddenMove);
597 }
598 if !player.cards.contains(&Some(card_to_play_idx)) {
599 return Err(GameError::PlayerDoesntHaveCard);
600 }
601 if player.cards.iter().any(|c| {
602 if let Some(c) = c {
603 if c == &card_to_play_idx {
604 return false;
605 }
606 DECK.0[*c] == CARD_TO_START
607 } else {
608 false
609 }
610 }) {
611 return Err(GameError::MustUseStartCard);
612 }
613 let card_to_play = get_card_by_idx(card_to_play_idx);
614 let card_to_play_type = card_to_play.get_type();
615 if let Some(Some((_, card_idx))) = stack.get(0) {
617 let firs_played_card = get_card_by_idx(*card_idx);
618 let first_played_type_card = firs_played_card.get_type();
619
620 if card_to_play_type != first_played_type_card {
621 if player.get_cards().iter().any(|c| {
623 if let Some(c) = c {
624 return c.get_type() == first_played_type_card;
625 }
626 false
627 }) {
628 return Err(GameError::MustPlaySameKind);
629 }
630
631 if firs_played_card == &CARD_TO_START
634 && (card_to_play_type == &TypeCard::Heart
635 || card_to_play == &QUEEN_OF_SPADE)
636 {
637 return Err(GameError::CannotStartWithQueenOrHeart);
638 }
639 }
640 } else {
641 if card_to_play_type == &TypeCard::Heart
645 && !self.back_in_deck.iter().any(|c| {
646 if let Some(c) = c {
647 DECK.0[*c].get_type() == &TypeCard::Heart
648 } else {
649 false
650 }
651 })
652 && player
653 .get_cards()
654 .iter()
655 .filter_map(|c| c.as_ref().map(|c| c.get_type()))
656 .any(|c| c != &TypeCard::Heart)
657 {
658 return Err(GameError::HeartNeverPlayedBefore);
659 }
660 }
661 Ok(card_to_play_idx)
662 }
663 _ => Err(GameError::StateError),
664 }
665 }
666
667 pub fn play(&mut self, card_to_play_idx: usize) -> Result<(), GameError> {
668 let card_to_play_idx = self.validate_play(card_to_play_idx)?;
669 let GameState::PlayingHand { stack, current_scores } = &mut self.state else {
670 unreachable!("already validated")};
671 let player = self.players.get_mut(self.current_player_pos).unwrap();
672
673 let Some(empty_slot) = stack.iter_mut().find(|s| s.is_none()) else {
675 return Err(GameError::ForbiddenMove)};
676 *empty_slot = Some((self.current_player_pos, card_to_play_idx));
677 if self.current_player_pos == PLAYER_NUMBER - 1 {
679 self.current_player_pos = 0;
680 } else {
681 self.current_player_pos += 1;
682 }
683 if let Some(card) = player
685 .cards
686 .iter_mut()
687 .find(|c| c == &&Some(card_to_play_idx))
688 {
689 card.take();
690 }
691 if stack.iter().all(|s| s.is_some()) {
693 self.state = GameState::ComputeScore {
694 stack: *stack,
695 current_scores: *current_scores,
696 };
697 }
698
699 Ok(())
700 }
701 pub fn compute_score(&mut self) -> Result<(), GameError> {
702 let Some(current_stack_state) = self.get_current_stack_state() else {unreachable!()};
703 self.current_player_pos = current_stack_state.current_losing_player_pos;
704 let GameState::ComputeScore { stack, current_scores } = &mut self.state else {
705 return Err(GameError::StateError)};
706 if current_scores[self.current_player_pos] + current_stack_state.score == MAX_SCORE {
707 for (idx, score) in current_scores.iter_mut().enumerate() {
708 if idx == self.current_player_pos {
709 *score = 0;
710 } else {
711 *score = MAX_SCORE;
712 }
713 }
714 } else {
715 current_scores[self.current_player_pos] += current_stack_state.score;
716 }
717
718 if self.back_in_deck.iter().filter(|s| s.is_some()).count() == DECK_SIZE - PLAYER_NUMBER {
719 for s in *stack {
720 let Some((pos_player, _)) = s else {unreachable!()};
721 self.players[pos_player].score += current_scores[pos_player];
722 }
723
724 self.current_hand += 1;
725 for card_in_deck in self.back_in_deck.iter_mut() {
726 *card_in_deck = None;
727 }
728 if self.current_hand <= self.hands {
729 self.state = GameState::EndHand;
730 } else {
731 self.state = GameState::End;
732 }
733 } else {
734 for s in stack.iter_mut() {
735 let Some(empty_slot) = self.back_in_deck.iter_mut()
736 .find(|s| s.is_none()) else {unreachable!()};
737 empty_slot.replace(s.take().map(|(_, c)| c).unwrap());
738 }
739 self.state = GameState::PlayingHand {
740 stack: *stack,
741 current_scores: *current_scores,
742 };
743 }
744 Ok(())
745 }
746
747 fn get_current_stack_state(&self) -> Option<StackState> {
748 let (stack, _) = match self.state {
749 GameState::PlayingHand {
750 stack,
751 current_scores,
752 }
753 | GameState::ComputeScore {
754 stack,
755 current_scores,
756 } => Some((stack, current_scores)),
757 _ => None,
758 }?;
759 let (first_player_pos, first_card_idx) = &stack[0]?;
760 let first_played_card = get_card_by_idx(*first_card_idx);
761 let first_played_type_card = first_played_card.get_type();
762 let mut max_card = (first_player_pos, first_played_card, *first_card_idx);
763 let mut score = first_played_card.get_value();
764 for c in stack.iter().skip(1).filter(|c| c.is_some()) {
765 let Some((next_player_pos, next_card_idx)) = c else {return None};
766
767 let next_played_card = get_card_by_idx(*next_card_idx);
768 let next_played_type_card = next_played_card.get_type();
769
770 if next_played_type_card == first_played_type_card && max_card.1 < next_played_card {
771 max_card = (next_player_pos, next_played_card, *next_card_idx);
772 }
773 score += next_played_card.get_value();
774 }
775 Some(StackState {
776 first_card_played_pos: *first_card_idx,
777 current_losing_player_pos: *max_card.0,
778 current_losing_card_pos: max_card.2,
779 score,
780 })
781 }
782
783 pub fn current_player_id(&self) -> Option<Uuid> {
784 self.players.get(self.current_player_pos).map(|p| p.id)
785 }
786 pub fn current_player_is_bot(&self) -> bool {
787 if let Some(current_player) = self.players.get(self.current_player_pos) {
788 current_player.is_bot()
789 } else {
790 false
791 }
792 }
793
794 pub fn player_ids_in_order(&self) -> [Uuid; PLAYER_NUMBER] {
795 self.players.map(|player| player.id)
796 }
797
798 pub fn player_score_by_id(&self) -> [PlayerState; PLAYER_NUMBER] {
799 self.players.map(|p| PlayerState {
800 player_id: p.id,
801 score: p.score,
802 })
803 }
804 pub fn current_score_by_id(&self) -> [PlayerState; PLAYER_NUMBER] {
805 let mut player_scores = self.player_score_by_id();
806 match self.state {
807 GameState::ExchangeCards { commands: _ } | GameState::EndHand | GameState::End => {}
808 GameState::PlayingHand {
809 stack: _,
810 current_scores,
811 }
812 | GameState::ComputeScore {
813 stack: _,
814 current_scores,
815 } => {
816 for (idx, score) in current_scores.iter().enumerate() {
817 player_scores[idx].score = *score;
818 }
819 }
820 }
821 player_scores
822 }
823
824 pub fn print_state(&self) {
825 println!("************* current state **************");
826 println!(
827 "Current player: {}\nState: {}",
828 self.players[self.current_player_pos].id,
829 Into::<&str>::into(&self.state)
830 );
831 match &self.state {
832 GameState::ComputeScore {
833 stack,
834 current_scores,
835 } => {
836 println!("Player order:");
838 for (pl_idx, _) in stack.iter().flatten() {
839 println!("\t{pl_idx}: {}", self.players[*pl_idx].id);
840 }
841 println!();
842
843 println!(
844 "Current stack: {}",
845 stack
846 .map(|c| if let Some((_, c)) = c {
847 get_card_by_idx(c).get_emoji()
848 } else {
849 "-"
850 })
851 .join(" ")
852 );
853 print!("Current Score: ");
855 for (pl_idx, _) in stack.iter().flatten() {
856 print!("{} ", current_scores[*pl_idx]);
857 }
858 println!();
859 for player in self.players.iter() {
860 println!("{player}");
861 }
862 }
863 GameState::ExchangeCards { commands: _ } => {
864 for player in self.players.iter() {
865 println!("{player}");
866 }
867 }
868 GameState::EndHand | GameState::End => {
869 print!("Hand Score: ");
871 for p in &self.players {
872 print!("{} ", p.score);
873 }
874 println!();
875 }
876 _ => {}
877 }
878 }
879}
880
881#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
882#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
883pub enum GameError {
884 UnknownCard,
885 ForbiddenMove,
886 PlayerDoesntHaveCard,
887 HeartNeverPlayedBefore,
888 MustUseStartCard,
889 MustPlaySameKind,
890 CannotStartWithQueenOrHeart,
891 StateError,
892}
893impl Display for GameError {
894 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
895 write!(f, "{:?}", self)
896 }
897}
898impl Error for GameError {}
899
900#[cfg(test)]
901mod test {
902 use uuid::Uuid;
903
904 use crate::{Card, TypeCard, CARD_TO_START, PLAYER_CARD_SIZE, QUEEN_OF_SPADE};
905
906 use super::{Game, GameState};
907
908 #[test]
909 pub fn new_game_test() {
910 let game = Game::new(
911 [
912 (Uuid::new_v4(), true),
913 (Uuid::new_v4(), true),
914 (Uuid::new_v4(), true),
915 (Uuid::new_v4(), true),
916 ],
917 1,
918 );
919 let first_player = game.players.get(game.current_player_pos).unwrap();
920 assert!(
921 first_player.get_cards().contains(&Some(&CARD_TO_START)),
922 "player {first_player:?} is not supposed to start!"
923 );
924
925 let all_positions: Vec<Option<usize>> = game
926 .players
927 .into_iter()
928 .flat_map(|p| p.cards.into_iter())
929 .collect();
930
931 let mut copy: Vec<&Option<usize>> = all_positions.iter().collect();
932 copy.sort();
933 copy.dedup();
934 assert_eq!(copy.len(), all_positions.len());
935 }
936
937 #[test]
938 pub fn test_compare() {
939 assert!(Card::Queen(TypeCard::Spade, "_") > Card::Jack(TypeCard::Spade, "_"));
940 }
941
942 #[test]
943 pub fn exchange_cards_test() {
944 let mut game = Game::new(
945 [
946 (Uuid::new_v4(), true),
947 (Uuid::new_v4(), true),
948 (Uuid::new_v4(), true),
949 (Uuid::new_v4(), true),
950 ],
951 1,
952 );
953
954 let clone: Vec<[Option<usize>; PLAYER_CARD_SIZE]> =
955 game.players.iter().map(|p| p.cards).collect();
956 while matches!(&game.state, &GameState::ExchangeCards { commands: _ }) {
957 let player = game.players.get(game.current_player_pos).unwrap();
958 let mut exchange = [0; 3];
959 for (i, c) in player.cards.iter().take(3).enumerate() {
960 exchange[i] = c.unwrap();
961 }
962 game.exchange_cards(exchange).unwrap();
963 }
964
965 assert_eq!(clone[0][0..3], game.players[1].cards[0..3]);
966 assert_eq!(clone[1][0..3], game.players[2].cards[0..3]);
967 assert_eq!(clone[2][0..3], game.players[3].cards[0..3]);
968 assert_eq!(clone[3][0..3], game.players[0].cards[0..3]);
969 }
970 #[test]
971 pub fn play() {
972 let mut game = Game::new(
973 [
974 (Uuid::new_v4(), true),
975 (Uuid::new_v4(), true),
976 (Uuid::new_v4(), true),
977 (Uuid::new_v4(), true),
978 ],
979 1,
980 );
981 assert!(matches!(
982 game.state,
983 GameState::ExchangeCards { commands: _ }
984 ));
985
986 loop {
987 match game.state {
988 GameState::ExchangeCards { commands: _ } => {
989 game.play_bot().unwrap();
990 }
991 GameState::PlayingHand {
992 stack: _,
993 current_scores: _,
994 } => game.play_bot().unwrap(),
995 GameState::ComputeScore {
996 stack: _,
997 current_scores: _,
998 } => {
999 game.print_state();
1000 game.compute_score().unwrap();
1001 }
1002 GameState::EndHand => {
1003 game.print_state();
1004 game.deal_cards().unwrap();
1005 }
1006 GameState::End => {
1007 game.print_state();
1008 break;
1009 }
1010 }
1011 }
1012 }
1013 #[test]
1014 fn test_serialize() {
1015 println!("{}", serde_json::to_string_pretty(&QUEEN_OF_SPADE).unwrap());
1016 }
1017}