From 5f7db64aa6767f142ca5b3272195b881eda10492 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Sat, 25 Nov 2023 00:17:06 -0600 Subject: chore: inital commit --- .gitignore | 1 + Cargo.lock | 75 ++++++++++++++++++++++++ Cargo.toml | 9 +++ src/main.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs 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 = (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::>(); + + 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 = 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::>() + .join("][") +} + +#[derive(Debug, Default)] +struct Line { + cards: Vec, +} + +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 { + 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 { + (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::>() + .join("]["); + write!(f, "[{cards}]") + } +} + +impl From 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, + score: u8, +} + +impl Player { + fn set_hand(&mut self, mut hand: Vec) { + 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) + ) + } +} -- cgit v1.2.3-70-g09d2