summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.rs210
1 files 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<u8> = (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::<Vec<Line>>();
+ let mut state = deck.take(5).copied().collect::<Lines>();
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<u8> = 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<Line>,
+}
- 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<Action> {
+ 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<Line>;
+
+ 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<u8> for Lines {
+ fn from_iter<T: IntoIterator<Item = u8>>(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<u8>,
}
@@ -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<u8> 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
+ )
}
}