use std::collections::HashSet; use rand::seq::SliceRandom; #[derive(Clone, Copy, Hash, PartialEq, Eq)] struct Card { color: Color, value: u8, } impl std::fmt::Debug for Card { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.color.get_symbol(), self.value) } } impl Card { fn new(color: Color, value: u8) -> Self { Self { color, value } } fn cost(&self) -> u8 { match self.color { Color::Spades => { if self.value == 5 { 4 } else { 0 } } Color::Hearts => 1, _ => 0, } } } #[derive(Clone, Copy, PartialEq, Eq, Hash)] enum Color { Clubs, Spades, Hearts, Diamonds, } impl Color { fn get_symbol(&self) -> char { match self { Color::Clubs => '♣', Color::Spades => '♠', Color::Hearts => '♥', Color::Diamonds => '♦', } } } #[derive(Debug)] enum Knowledge { Full(Vec), Partial([bool; 4]), } fn solve( current: usize, knowledge: &[Knowledge], unseen: &[Card], stack: &mut Vec, remaining: usize, played: &mut HashSet, ) -> Option<(Option, Vec)> { let num_player = knowledge.len(); if remaining == 0 { Some((None, vec![0.0; num_player])) } else if stack.len() == num_player { let first_card = stack.first().unwrap(); let winning_card = stack .iter() .enumerate() .max_by_key(|g| { if g.1.color == first_card.color { g.1.value } else { 0 } }) .unwrap(); let winning_player = (current + winning_card.0) % num_player; let cost = stack.iter().map(|c| c.cost()).sum::(); let mut new_stack = Vec::new(); if let Some(mut s) = solve( winning_player, knowledge, unseen, &mut new_stack, remaining - 1, played, ) { s.1[winning_player] += cost as f64; Some(s) } else { None } } else { match &knowledge[current] { Knowledge::Full(cards) => { if let Some(f) = stack .first() .copied() .filter(|&f| cards.iter().any(|&c| f.color == c.color)) { let mut min: Option<(Option, Vec)> = None; for &c in cards.iter().filter(|&&c| c.color == f.color) { if !played.contains(&c) { stack.push(c); played.insert(c); if let Some(m) = solve( (current + 1) % num_player, knowledge, unseen, stack, remaining, played, ) { if let Some(min) = &mut min { if m.1[current] < min.1[current] { *min = (Some(c), m.1); } } else { min = Some((Some(c), m.1)); } } stack.pop(); played.remove(&c); } } min } else { let mut min: Option<(Option, Vec)> = None; for &c in cards { if !played.contains(&c) { stack.push(c); played.insert(c); if let Some(m) = solve( (current + 1) % num_player, knowledge, unseen, stack, remaining, played, ) { if let Some(min) = &mut min { if m.1[current] < min.1[current] { *min = (Some(c), m.1); } } else { min = Some((Some(c), m.1)); } } stack.pop(); played.remove(&c); } } min } } Knowledge::Partial(_) => todo!(), } } } fn main() { let mut full_deck = (1..=8) .flat_map(|u| { [ Card::new(Color::Clubs, u), Card::new(Color::Spades, u), Card::new(Color::Hearts, u), Card::new(Color::Diamonds, u), ] .into_iter() }) .collect::>(); let mut rng = rand::thread_rng(); full_deck.shuffle(&mut rng); let knowledge = [ Knowledge::Full(full_deck[0..10].to_vec()), Knowledge::Full(full_deck[10..20].to_vec()), Knowledge::Full(full_deck[20..30].to_vec()), ]; dbg!(&knowledge); let unseen = []; let mut stack = Vec::new(); let mut played = HashSet::new(); dbg!(solve(0, &knowledge, &unseen, &mut stack, 5, &mut played)); }