use super::card::{Card, Suit};
use super::deck::Deck;
use super::player::{Player, PlayerType};
use std::fmt::Display;
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GamePhase {
Setup,
Attack,
Defense,
Drawing,
GameOver,
}
impl Display for GamePhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, Clone)]
pub struct GameState {
pub players: [Player; 2],
pub deck: Deck,
pub discard_pile: Vec<Card>,
pub table_cards: Vec<(Card, Option<Card>)>, pub current_attacker: usize,
pub current_defender: usize,
pub trump_suit: Option<Suit>,
pub game_phase: GamePhase,
pub winner: Option<usize>,
pub stuck_counter: usize, }
impl GameState {
pub fn new() -> Self {
Self {
players: [
Player::new("Player".to_string(), PlayerType::Human),
Player::new("Computer".to_string(), PlayerType::Computer),
],
deck: Deck::new(),
discard_pile: Vec::new(),
table_cards: Vec::new(),
current_attacker: 0,
current_defender: 0,
trump_suit: None,
game_phase: GamePhase::Setup,
winner: None,
stuck_counter: 0, }
}
pub fn setup_game(&mut self) {
let deck = Deck::new();
self.deck = deck;
self.deck.shuffle();
self.trump_suit = self.deck.trump_suit();
for p in &mut self.players {
p.add_cards(self.deck.deal(6));
}
self.determine_first_player();
self.current_defender = (self.current_attacker + 1) & 1;
self.game_phase = GamePhase::Attack;
self.stuck_counter = 0; }
fn determine_first_player(&mut self) {
if let Some(trump_suit) = self.trump_suit {
let mut best = None;
for (idx, p) in self.players.iter().enumerate() {
if let Some((_, c)) = p.get_lowest_trump(trump_suit) {
let r = c.rank;
match best {
None => best = Some((idx, r)),
Some((_, best_r)) if r < best_r => best = Some((idx, r)),
_ => {}
}
}
}
if let Some((idx, _)) = best {
self.current_attacker = idx;
return;
}
}
self.current_attacker = 0;
}
#[inline]
pub fn attack(&mut self, card_idx: usize, player_idx: usize) -> Result<(), &'static str> {
match self.players[player_idx].remove_card(card_idx) {
Some(card) => {
self.table_cards.push((card, None));
self.game_phase = GamePhase::Defense;
self.current_attacker = player_idx;
self.current_defender = (player_idx + 1) & 1;
Ok(())
}
None => Err("Invalid card index"),
}
}
pub fn pass_attack(&mut self, card_idx: usize, _attack_idx: usize) -> Result<(), &'static str> {
let defender = self.current_defender;
match self.players[defender].remove_card(card_idx) {
Some(card) => {
self.table_cards.push((card, None));
self.current_attacker = defender;
self.current_defender = (defender + 1) & 1;
self.game_phase = GamePhase::Defense;
Ok(())
}
None => Err("Failed to remove card from hand during pass"),
}
}
pub fn defend(&mut self, card_idx: usize) -> Result<(), &'static str> {
let attack_idx = match self.table_cards.iter().position(|(_, d)| d.is_none()) {
Some(i) => i,
None => return Err("No undefended attacks"),
};
let defender = &mut self.players[self.current_defender];
if card_idx >= defender.hand().len() {
return Err("Invalid card index");
}
let defense_card = defender.hand()[card_idx];
let attack_card = self.table_cards[attack_idx].0;
if defense_card.rank == attack_card.rank {
return self.pass_attack(card_idx, attack_idx);
}
let trump = self.trump_suit;
let valid = match trump {
Some(t) => {
let a_t = attack_card.suit == t;
let d_t = defense_card.suit == t;
if a_t {
d_t && defense_card.rank > attack_card.rank
} else if d_t {
true
} else {
defense_card.suit == attack_card.suit && defense_card.rank > attack_card.rank
}
}
None => defense_card.suit == attack_card.suit && defense_card.rank > attack_card.rank,
};
if !valid {
return Err("Invalid defense");
}
if let Some(card) = defender.remove_card(card_idx) {
self.table_cards[attack_idx].1 = Some(card);
Ok(())
} else {
Err("Failed to remove card from hand")
}
}
pub fn discard_cards(&mut self, cards: Vec<(usize, Card)>) {
for (idx, card) in cards {
self.table_cards[idx].1 = Some(card); }
let all_defended = !self
.table_cards
.iter()
.any(|(_, defense)| defense.is_none());
if !all_defended {
return;
}
for (a, d) in self.table_cards.drain(..) {
self.discard_pile.push(a);
if let Some(x) = d {
self.discard_pile.push(x);
}
}
let old_defender = self.current_defender;
self.current_attacker = old_defender;
self.current_defender = (old_defender + 1) % self.players.len();
self.game_phase = GamePhase::Drawing;
}
pub fn take_cards(&mut self) -> Result<(), &'static str> {
assert!(self.game_phase == GamePhase::Defense);
if self.table_cards.is_empty() {
return Err("No cards on table to take");
}
let defender = &mut self.players[self.current_defender];
let mut taken = Vec::with_capacity(self.table_cards.len() * 2);
for (a, d) in self.table_cards.drain(..) {
taken.push(a);
if let Some(x) = d {
taken.push(x);
}
}
defender.add_cards(taken);
self.game_phase = GamePhase::Drawing;
Ok(())
}
pub fn draw_cards(&mut self) {
if self.game_phase != GamePhase::Drawing {
return;
}
self.stuck_counter += 1;
if self.stuck_counter > 5 {
self.game_phase = GamePhase::Attack;
self.stuck_counter = 0;
for (a, d) in self.table_cards.drain(..) {
self.discard_pile.push(a);
if let Some(x) = d {
self.discard_pile.push(x);
}
}
return;
}
let players_need_cards = self
.players
.iter()
.any(|p| p.hand_size() < 6 && !self.deck.is_empty());
if !players_need_cards {
self.game_phase = GamePhase::Attack;
self.stuck_counter = 0;
return;
}
let n = self.players.len();
let mut idx = self.current_attacker;
for _ in 0..n {
if self.deck.is_empty() {
break;
}
let p = &mut self.players[idx];
let need = 6usize.saturating_sub(p.hand_size());
if need > 0 {
let new_cards = self.deck.deal(need);
p.add_cards(new_cards);
}
idx = (idx + 1) % n;
}
self.check_game_over();
if self.game_phase != GamePhase::GameOver {
if !self.table_cards.is_empty() {
self.current_attacker = (self.current_defender + 1) % n;
self.current_defender = (self.current_attacker + 1) % n;
}
self.game_phase = GamePhase::Attack;
}
if matches!(self.game_phase, GamePhase::Attack | GamePhase::GameOver) {
self.stuck_counter = 0;
}
}
pub fn check_game_over(&mut self) -> bool {
if !self.deck.is_empty() {
return false;
}
let mut count = 0;
let mut last = None;
for (i, p) in self.players.iter().enumerate() {
if !p.is_empty_hand() {
count += 1;
last = Some(i);
}
}
if count <= 1 {
match (count, last) {
(1, Some(loser)) => {
let winner = if loser == 1 { 0 } else { 1 };
self.winner = Some(winner);
}
_ => {}
}
self.game_phase = GamePhase::GameOver;
return true;
}
false
}
#[inline]
pub fn players(&self) -> &[Player] {
&self.players
}
#[inline]
pub fn players_mut(&mut self) -> &mut [Player; 2] {
&mut self.players
}
#[inline]
pub fn deck(&self) -> &Deck {
&self.deck
}
#[inline]
pub fn trump_suit(&self) -> Option<Suit> {
self.trump_suit
}
#[inline]
pub fn table_cards(&self) -> &[(Card, Option<Card>)] {
&self.table_cards
}
#[inline]
pub fn current_attacker(&self) -> usize {
self.current_attacker
}
#[inline]
pub fn current_defender(&self) -> usize {
self.current_defender
}
#[inline]
pub fn game_phase(&self) -> &GamePhase {
&self.game_phase
}
#[inline]
pub fn winner(&self) -> Option<usize> {
self.winner
}
#[inline]
#[allow(dead_code)]
pub fn discard_pile(&self) -> &[Card] {
&self.discard_pile
}
pub fn force_attack_phase(&mut self) {
self.game_phase = GamePhase::Attack;
self.stuck_counter = 0;
if !self.table_cards.is_empty() {
let mut cards_to_discard = Vec::new();
for (attack, defense) in self.table_cards.drain(..) {
cards_to_discard.push(attack);
if let Some(def_card) = defense {
cards_to_discard.push(def_card);
}
}
self.discard_pile.extend(cards_to_discard);
}
}
#[inline]
pub fn set_phase_to_defense(&mut self, attacker_idx: usize, defender_idx: usize) {
self.game_phase = GamePhase::Defense;
self.current_attacker = attacker_idx;
self.current_defender = defender_idx;
}
}