From ef4090641c47c59ab242ff3014bff939a23dd561 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Mon, 27 Nov 2023 14:58:38 -0600 Subject: feat(wip): simplify code --- src/main.rs | 210 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 137 insertions(+), 73 deletions(-) diff --git a/src/main.rs b/src/main.rs index d1014f9..22881b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ -use std::{collections::HashMap, fmt::Display}; +use std::{ + fmt::Display, + ops::{Deref, DerefMut}, +}; use rand::{seq::SliceRandom, thread_rng}; @@ -46,17 +49,26 @@ fn main() { } } +fn pause(prompt: &str) { + use std::io::{Read, Write}; + + let mut stdin = std::io::stdin(); + let mut stdout = std::io::stdout(); + + // We want the cursor to stay at the end of the line, so we print without a newline and flush manually. + write!(stdout, "{prompt}").unwrap(); + stdout.flush().unwrap(); + + // Read a single byte and discard + let _ = stdin.read(&mut [0u8]).unwrap(); +} + fn round(players: &mut [Player]) { let mut cards: Vec = (MIN_CARD..MAX_CARD).collect(); cards.shuffle(&mut thread_rng()); let deck = &mut cards.iter(); - let mut state = deck - .take(5) - .copied() - .enumerate() - .map(Line::from) - .collect::>(); + let mut state = deck.take(5).copied().collect::(); for player in &mut *players { player.set_hand(deck.take(10).copied().collect()); @@ -67,56 +79,104 @@ fn round(players: &mut [Player]) { println!("Line {i}: {line}"); } + println!("{state}"); + + let mut turn = 1; while !players.iter().any(|p| p.hand.is_empty()) { - let cards: Vec = players.iter_mut().map(|p| p.evaluate(&state)).collect(); - let mut played: Vec<(&mut Player, u8)> = players.iter_mut().zip(cards).collect(); + pause("Press any key to continue to next turn..."); - played.sort_by_key(|p| p.1); + println!("Turn: {}", turn); - let mut actions = vec![HashMap::new(); state.len()]; - let mut player_names = Vec::new(); - print!("{:37}", "Table"); + for player in &mut *players { + player.sort_hand(&state); + } - for (player, card) in played { - print!("{:>10}", player.name); - player_names.push(&player.name); + players.sort_by(|p1, p2| p1.peek().cmp(p2.peek())); - let line = state - .iter_mut() - .min_by_key(|line| (player.value_function)(card, line)) + for player in &mut *players { + println!("{}'s turn", player.name); + println!( + "Hand: {}", + player + .hand + .iter() + .fold(Default::default(), |buf, card| format!("{buf}[{card:03}]")) + ); + + let action = state + .play_card(player.pop(), player.value_function) .unwrap(); + player.score += action.points; + println!("Action: {action}"); + println!("{state}"); + } + turn += 1; + } +} - actions[line.id].insert(&player.name, card); +#[derive(Debug, Clone, Default)] +struct Lines { + lines: Vec, +} - if let Some(score) = line.push(card) { - player.score += score; - } +impl Lines { + fn valid_lines(&self, card: &u8) -> Option<&Line> { + self.iter() + .filter(|l| l.head() < card) + .max_by_key(|l| l.head()) + } + + fn play_card(&mut self, card: u8, value_fn: fn(card: u8, line: &Line) -> u8) -> Option { + if self.iter().any(|l| l.head() < &card) { + self.iter_mut() + .filter(|l| l.head() < &card) + .max_by(|a, b| a.head().cmp(b.head())) + } else { + self.iter_mut().max_by_key(|l| value_fn(card, l)) } + .map(|l| Action { + card, + line: *l.head(), + points: l.push(card).unwrap_or_default(), + }) + } +} + +impl Deref for Lines { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.lines + } +} - println!(); - for (line, actions) in state.iter().zip(actions) { - let cards: String = line - .cards - .iter() - .fold(String::new(), |buf, c| format!("{buf}[{c:>3}]")); - - print!("Line {cards:<32}"); - for name in &player_names { - let card = actions - .get(name) - .map(|c| format!("[{c:>3}]")) - .unwrap_or("".to_string()); - print!("{:>10}", card); - } - - println!(); +impl DerefMut for Lines { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lines + } +} + +impl Display for Lines { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.lines + .iter() + .enumerate() + .try_for_each(|(i, l)| writeln!(f, "Line {i}: {l}")) + } +} + +impl FromIterator for Lines { + fn from_iter>(iter: T) -> Self { + let mut lines = Lines::default(); + for i in iter { + lines.lines.push(Line::from(i)) } + lines } } -#[derive(Debug, Default)] +#[derive(Debug, Clone, Default)] struct Line { - id: usize, cards: Vec, } @@ -147,17 +207,15 @@ impl Line { impl Display for Line { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: ", self.id)?; self.cards .iter() .try_for_each(|card| write!(f, "[{card:03}]")) } } -impl From<(usize, u8)> for Line { - fn from((id, value): (usize, u8)) -> Self { +impl From for Line { + fn from(value: u8) -> Self { Self { - id, cards: Vec::from([value]), } } @@ -176,34 +234,40 @@ impl Player { self.hand = hand } - fn evaluate(&mut self, state: &[Line]) -> u8 { + fn peek(&self) -> &u8 { + self.hand.last().unwrap() + } + + fn pop(&mut self) -> u8 { + self.hand.pop().unwrap() + } + + fn sort_hand(&mut self, state: &Lines) { let value_function = self.value_function; - let mut cards = HashMap::new(); - - for card in &self.hand { - let mut valid_play_found = false; - for line in state { - if line.head() < card { - valid_play_found = true - } else if valid_play_found { - continue; - } - - let value = value_function(*card, line); - cards - .entry(card) - .and_modify(|o| { - *o = value.max(*o); - }) - .or_insert(value); - } - } - cards - .iter() - .max_by_key(|e| e.1) - .and_then(|e| self.hand.iter().position(|c| c == *e.0)) - .map(|index| self.hand.remove(index)) - .unwrap() + self.hand.sort_by_key(|card| { + state + .valid_lines(card) + .map(|l| value_function(*card, l)) + .or_else(|| state.iter().map(|l| value_function(*card, l)).min()) + }); + + self.hand.reverse(); + } +} + +struct Action { + card: u8, + line: u8, + points: u8, +} + +impl Display for Action { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "[{:>3}] onto [{}] for {} points", + self.card, self.line, self.points + ) } } -- cgit v1.2.3-70-g09d2