summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToby Vincent <tobyv@tobyvin.dev>2023-11-25 00:17:06 -0600
committerToby Vincent <tobyv@tobyvin.dev>2023-11-25 00:18:27 -0600
commit5f7db64aa6767f142ca5b3272195b881eda10492 (patch)
tree84c7b8059548c0737b6ab2445bfe5b67b4e0900b
chore: inital commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock75
-rw-r--r--Cargo.toml9
-rw-r--r--src/main.rs190
4 files changed, 275 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..cb81762
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,75 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "take-five"
+version = "0.1.0"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..0b8402d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "take-five"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rand = "0.8.5"
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..7af07bf
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,190 @@
+use std::{collections::HashMap, fmt::Display};
+
+use rand::{seq::SliceRandom, thread_rng};
+
+const MIN_CARD: u8 = 0;
+const MAX_CARD: u8 = 104;
+
+fn main() {
+ let mut players = Vec::from([
+ Player {
+ value_function: |card, line| line.push_cost(card).unwrap_or_default(),
+ hand: Default::default(),
+ score: 0,
+ },
+ Player {
+ value_function: |card, line| line.push_cost(card).unwrap_or_default(),
+ hand: Default::default(),
+ score: 0,
+ },
+ Player {
+ value_function: |card, line| line.push_cost(card).unwrap_or_default(),
+ hand: Default::default(),
+ score: 0,
+ },
+ ]);
+
+ round(&mut players);
+ round(&mut players);
+ round(&mut players);
+ round(&mut players);
+}
+
+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().map(Line::from).collect::<Vec<Line>>();
+
+ for player in &mut *players {
+ player.set_hand(deck.take(10).copied().collect());
+ println!("Player: {player}");
+ }
+
+ for (i, line) in state.iter().enumerate() {
+ println!("Line {i}: {line}");
+ }
+
+ while !players.iter().any(|p| p.hand.is_empty() || p.score > 66) {
+ let cards: Vec<u8> = players.iter_mut().map(|p| p.evaluate(&state)).collect();
+
+ for (player, card) in players.iter_mut().zip(cards) {
+ let line = state
+ .iter_mut()
+ .min_by_key(|line| (player.value_function)(card, line))
+ .unwrap();
+
+ if let Some(score) = line.push(card) {
+ player.score += score;
+ }
+ }
+
+ for (i, line) in state.iter().enumerate() {
+ println!("Line {i}: {line}");
+ }
+ }
+
+ for player in &mut *players {
+ println!("Player: {player}");
+ }
+}
+
+fn display_cards(cards: &[u8]) -> String {
+ cards
+ .iter()
+ .map(|c| format!("{c}"))
+ .collect::<Vec<_>>()
+ .join("][")
+}
+
+#[derive(Debug, Default)]
+struct Line {
+ cards: Vec<u8>,
+}
+
+impl Line {
+ fn head(&self) -> &u8 {
+ self.cards.last().unwrap()
+ }
+
+ fn count(&self) -> u8 {
+ self.cards.len().try_into().unwrap()
+ }
+
+ fn push(&mut self, card: u8) -> Option<u8> {
+ let cost = self.push_cost(card);
+ if cost.is_some() {
+ self.cards.clear();
+ }
+
+ self.cards.push(card);
+
+ cost
+ }
+
+ fn push_cost(&self, card: u8) -> Option<u8> {
+ (self.count() >= 5 || card <= *self.head()).then_some(self.count())
+ }
+}
+
+impl Display for Line {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let cards = self
+ .cards
+ .iter()
+ .map(|c| format!("{c}"))
+ .collect::<Vec<_>>()
+ .join("][");
+ write!(f, "[{cards}]")
+ }
+}
+
+impl From<u8> for Line {
+ fn from(value: u8) -> Self {
+ Self {
+ cards: Vec::from([value]),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
+struct Move {
+ value: u8,
+ line: usize,
+ card: u8,
+}
+
+impl Display for Move {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "[{}] to line {}", self.card, self.line)
+ }
+}
+
+struct Player {
+ value_function: fn(card: u8, line: &Line) -> u8,
+ hand: Vec<u8>,
+ score: u8,
+}
+
+impl Player {
+ fn set_hand(&mut self, mut hand: Vec<u8>) {
+ hand.sort();
+ self.hand = hand
+ }
+
+ fn evaluate(&mut self, state: &[Line]) -> u8 {
+ let value_function = self.value_function;
+ let mut cards = HashMap::new();
+
+ for line in state {
+ for card in &self.hand {
+ 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.1))
+ .map(|index| self.hand.remove(index))
+ .unwrap()
+ }
+}
+
+impl Display for Player {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Score: {}, Hand: {}",
+ self.score,
+ display_cards(&self.hand)
+ )
+ }
+}