--- a/src/main.rs Tue Jan 31 22:16:26 2023 +0100
+++ b/src/main.rs Tue Jan 31 23:25:50 2023 +0100
@@ -2,15 +2,23 @@
// vigyazz6! autplayer
//
-use core::{fmt, panic};
+use core::panic;
use std::{collections::VecDeque, time::Instant, cmp::Reverse};
-use rand::Rng;
-use std::mem;
+
+use crate::{deck::Deck, player::Player, table::Table};
+// use rand::Rng;
+// use std::mem;
+
+mod deck;
+mod table;
+mod player;
+mod card;
+mod row;
extern crate pretty_env_logger;
#[macro_use] extern crate log;
-const GAME_ROUNDS: i32 = 1_000_000;
+const GAME_ROUNDS: i32 = 100_000;
fn main() {
// RUST_LOG=debug cargo run
@@ -20,459 +28,11 @@
info!("End of run.");
}
-/*** Card ****/
-#[derive(Debug)]
-struct Card {
- value: i8,
- points: i8,
-}
-
-impl Card {
- fn new(value: i8)->Self {
-
- let mut points = 0;
- if value % 10 == 5 {
- // ends with 5 = 2 point
- points = 2;
- // println!("*5 add 1, val={}, pt={}", value, points);
- }
-
- if value % 10 == 0 {
- // ends with 0 = 3 point
- points = 3;
- // println!("*0 add 2, val={}, pt={}", value, points);
- }
-
- if value % 10 == value / 10 {
- // same numbers = 5 points (55=7)
- points += 5;
- // println!("NN add 5, val={}, pt={}", value, points);
- }
-
- if points == 0 {
- points = 1;
- }
-
- Card {
- value,
- points,
- }
- }
-}
-
-impl fmt::Display for Card {
- fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result {
- write!(f, "(Card {}, points {})", self.value, self.points)
- }
-}
-
-impl PartialEq for Card {
- fn eq(&self, other: &Self) -> bool {
- self.value == other.value
- }
-}
-
-impl Eq for Card {}
-
-impl PartialOrd for Card {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- match self.value.partial_cmp(&other.value) {
- Some(core::cmp::Ordering::Equal) => {None}
- ord => return ord,
- }
- }
-}
-
-impl Ord for Card {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.value.cmp(&other.value)
- }
-}
-
-/*** Deck ****/
-#[derive(Debug)]
-struct Deck {
- content: VecDeque<Card>,
-}
-
-impl Deck {
- fn new_empty() -> Self {
- debug!("Empty deck generated");
- Deck {
- content: VecDeque::new(),
- }
- }
-
- fn new() -> Self {
- debug!("Full deck generated");
- let content = (1..=104).into_iter().map( |n| Card::new(n) ).collect();
- Deck {
- content,
- }
- }
-
- fn shuffle( &mut self ) {
- let mut rng = rand::thread_rng();
-
- trace!("Deck before shuffle: len {}, {:?}", self.content.len(), self);
- debug!("Deck shuffled");
- // shufflers:
- // * naive: swap cards n times
- // * kgb: half the deck, take 1..4 cards sequentially from each
- // * grin: take 1..6 from front and put at bottom
-
- // naive shuffle: exchange random cards
- for _i in 1..=500 {
- let c1 = rng.gen_range(0 .. self.content.len());
- let c2 = rng.gen_range(0 .. self.content.len());
- if c1 != c2 {
- self.content.swap(c1, c2);
- }
- }
- trace!("Deck after shuffle: len {}, {:?}", self.content.len(), self);
- }
-
- // get top card from deck
- fn pop( &mut self ) -> Option<Card> {
- self.content.pop_front()
- }
-
- // put a card into the bottom of the deck
- fn push( &mut self, c: Card ) {
- self.content.push_back(c);
- }
-
- fn push_cards( &mut self, cards: VecDeque<Card> ) {
- trace!("Collecting back, deck len is {}, cards {}", self.content.len(), cards.len());
- cards.into_iter().for_each( |card| self.push(card) );
- trace!("Deck len is {}", self.content.len());
- }
-
- fn len( &self ) -> usize {
- self.content.len()
- }
-
- fn _get_nth( &mut self, n: usize ) -> Card {
- if let Some(c) = self.content.remove(n) {
- c
- } else {
- panic!("get_nth: index {} out of bounds ({})!", n, self.content.len());
- }
- }
-}
-
-/*** Player ****/
-#[derive(Debug)]
-struct Player {
- name: String,
- hand: Deck,
- pile: VecDeque<Card>,
- game_point: i32,
- total_point: i32,
- rows_busted: i32,
- wins: i32,
-}
-
-impl Player {
- fn new(name: String)->Self {
- debug!("Player {} created", name);
- Player {
- name,
- hand: Deck::new_empty(),
- pile: VecDeque::new(),
- game_point: 0,
- total_point: 0,
- rows_busted: 0,
- wins: 0,
- }
- }
-
- // get one card from th dealer
- fn get_card( &mut self, card: Card ) {
- trace!("Player {} got a card {:?}, cards before {}", self.name, &card, self.hand.len());
- self.hand.push(card);
- }
-
- // throw a card from hand to the table
- fn throw_card( &mut self )->Card {
- if let Some(c) = self.hand.pop() {
- trace!("Player {} throws a card {:?}", self.name, &c);
- c
- } else {
- panic!("throw_card: Player {} has no card in hand!", self.name);
- }
- }
-
- // get a busted row of cards
- fn give_pile( &mut self, cards: VecDeque<Card> ) {
- for c in cards.into_iter() {
- self.game_point += c.points as i32;
- self.pile.push_back(c);
- }
- self.rows_busted += 1;
- trace!("Player {} got busted, count {}", self.name, &self.rows_busted);
- }
-
- // ask the player their score
- fn _tell_points( self ) -> i32 {
- self.game_point
- }
-
- fn inc_wins( &mut self ) {
- self.wins += 1;
- }
-
- // give back cards from the pile
- fn get_pile( &mut self ) -> VecDeque<Card> {
- trace!("Player {} gives back their pile", self.name);
- mem::take( &mut self.pile )
- // same effect:
- // self.pile.drain(..).collect()
-
- // very cumbersome manual fiddling (also reverted...)
-/* let mut throw: Vec<Card> = Vec::new();
- for _i in 0 .. self.pile.len() {
- throw.push( self.pile.pop().unwrap() );
- }
- throw
- */
- }
+// Card
+// Deck
+// Player
+// Row
- // I can do this just because I *throw away* c!
- // doesn't work if I want to use it.
-/* fn _gimme_pile(self)->Self {
- for c in &self.pile {
- println!("Throw {} ", c);
- }
- self
- }
- */
- fn close_round( &mut self ) {
- if self.hand.len() > 0 {
- panic!("Closing round when {} has {} cards in hand", self.name, self.hand.len());
- }
-
- if self.pile.len() > 0 {
- panic!("Closing round when {} stil have pile with {} cards", self.name, self.pile.len());
- }
-
- trace!("Player {} closing round; points={} total so far {}", self.name, self.game_point, self.total_point);
- self.total_point += self.game_point;
- self.game_point = 0;
- }
-
- // card too small: pick a row to collect from the rows
- fn pick_row_for_small_card( &self, rows: &Vec<Row>, playercard: &Card ) -> usize {
- trace!("Player {} picking a row for small card, card {:?}, rows {:?}", self.name, playercard, rows);
-
- // contains the summary point for each row
- let mut row_points = Vec::with_capacity(5);
- // the smallest row score
- let mut smallest = 999;
- // how many rows have the same smallest score
- let mut same_point = 0;
- // the first smallest row_id
- let mut smallest_rowid = 255;
-
- for rowid in 0 .. rows.len() {
- // DEBUG
- // println!("pick_row_for_small_card: rowlen {}, rowid {}", rows.len(), rowid);
- row_points.push( rows[rowid].sum() );
-
- if row_points[rowid] < smallest {
- // we have a new smallest row
- smallest = row_points[rowid];
- same_point = 0;
- smallest_rowid = rowid;
-
- } else if row_points[rowid] == smallest {
- // we have another row with same point as smallest
- same_point += 1;
- }
- }
-
- if same_point < 1 {
- // we have one smallest row
- smallest_rowid.try_into().unwrap() // it's tiny, will fit into u8
-
- } else {
- // bored, we pick the first now anyway
- smallest_rowid.try_into().unwrap()
- }
-
- }
-}
-
-/*** Row ****/
-// a row of cards on the table (max 5)
-#[derive(Debug)]
-struct Row {
- cards: VecDeque<Card>,
-}
-
-impl Row {
- const MAX_LEN: usize = 5;
-
- fn new() -> Self {
- Row {
- cards: VecDeque::with_capacity(5),
- }
- }
-
- fn push_or_collect( &mut self, card: Card ) -> Option<VecDeque<Card>> {
- trace!("Called push_or_collect on row {:?}", &self);
- if self.cards.len() < Self::MAX_LEN {
- trace!("Less than {} cards, putting at the end", Self::MAX_LEN);
- self.cards.push_back(card);
- None
-
- } else {
- trace!("Row is full, len {}, maxlen {}", self.cards.len(), Self::MAX_LEN);
- // row overflow
- let row_cards = mem::take( &mut self.cards );
- self.cards.push_back(card);
- if self.cards.len() != 1 {
- panic!("New row must have one card, not {}", self.cards.len());
- }
- Some(row_cards)
- }
- }
-
- fn take_row( &mut self ) -> VecDeque<Card> {
- // take cards and empty the row
- mem::take( &mut self.cards )
- }
-
- fn last_card_value(&self) -> i8 {
- // println!("last_card_value: cards {:?}, len {}", self.cards, self.cards.len());
- self.cards.get( self.cards.len()-1 ).unwrap().value
- }
-
- // sum of row card points
- fn sum(&self) -> i32 {
- let mut sum: i32 = 0;
- self.cards.iter().for_each(|card| {
- sum += card.points as i32;
- });
- sum
- }
-}
-
-/*** PlayerCard ****/
-#[derive(Debug)]
-struct PlayerCard {
- player_id: i32,
- card: Card,
-}
-
-impl PlayerCard {
- fn _get_player(&self) -> i32 {
- self.player_id
- }
-}
-
-impl PartialEq for PlayerCard {
- fn eq(&self, other: &Self) -> bool {
- self.card == other.card
- }
-}
-
-impl PartialOrd for PlayerCard {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- match self.card.partial_cmp(&other.card) {
- Some(core::cmp::Ordering::Equal) => {None}
- ord => return ord,
- }
- }
-}
-
-/*** Table ****/
-#[derive(Debug)]
-struct Table {
- rows: Vec<Row>,
- player_cards: VecDeque<PlayerCard>, // owned by a player
-}
-
-impl Table {
- fn new(row_cards: VecDeque<Card>) -> Self {
- let mut rows = Vec::with_capacity(5);
- for card in row_cards {
- // create a new row then put a card into it
- let mut row = Row::new();
- if let Some(_c) = row.push_or_collect(card) {
- panic!("Freshly created row overflowed");
- }
- rows.push( row );
- }
-
- Table {
- rows,
- player_cards: VecDeque::new(),
- }
- }
-
- fn lay_player_card( &mut self, card: Card, player_id: i32 ) {
- self.player_cards.push_back( PlayerCard { player_id, card } );
- }
-
- fn sort_cards( &mut self ) {
- self.player_cards.make_contiguous().sort_by( |a,b| b.card.cmp(&a.card) );
- }
-
- fn has_player_cards( &self ) -> bool {
- self.player_cards.len() > 0
- }
-
- fn get_smallest_player_card( &mut self ) -> PlayerCard {
- // FIXME: check!
- self.player_cards.pop_back().expect("out of player cards on table")
- }
-
- fn get_closest_row( &self, pcard: &PlayerCard ) -> Option<usize> {
- // get the row id with last card closest smaller to players'
- let row_heads = self.get_row_heads();
- let mut closest_val = None;
- let mut diff = 127;
- for i in 0..row_heads.len() {
- if row_heads[i] < pcard.card.value && pcard.card.value - row_heads[i] < diff {
- closest_val = Some(i);
- diff = pcard.card.value - row_heads[i];
- // println!("DEBUG: pcard {}, row {}, head {}, diff {}, closest {:?}", pcard.card.value, i, row_heads[i], diff, closest_val);
- }
- }
-
- closest_val
- }
-
- fn put_card_into_row( &mut self, pcard: PlayerCard, row_id: usize ) -> Option<VecDeque<Card>> {
- self.rows[row_id as usize].push_or_collect(pcard.card)
- }
-
- fn get_row_heads( &self ) -> Vec<i8> {
- let mut heads: Vec<i8> = Vec::new();
- for i in 0..self.rows.len() {
- heads.push( self.rows[i].last_card_value() );
- }
- heads
- }
-
- // take a whole row and hand it over
- fn take_row( &mut self, row_id: usize ) -> VecDeque<Card> {
- self.rows[row_id].take_row()
- }
-
- // collect remaining cards in the rows at the end of round
- fn collect_rows( &mut self ) -> VecDeque<Card> {
- let mut cards = VecDeque::new();
- for row in 0..self.rows.len() {
- self.rows[row]
- .take_row()
- .into_iter()
- .for_each(|card| cards.push_back(card));
- }
- cards
- }
-}
/*** Game ****/
@@ -556,7 +116,7 @@
let overflow = table.put_card_into_row(smallest, rowid);
if let Some(cards) = overflow {
// row is full, got pile
- debug!("Row is busted, {} collects", players[player_id].name);
+ debug!("Row is busted, {} collects", players[player_id].get_name());
// player gets pile, card gets into row head
players[ player_id ].give_pile( cards );
}
@@ -564,9 +124,10 @@
None => {
// card too small, need to pick row!
let player_id: usize = smallest.player_id.try_into().unwrap();
- debug!("Too small from {}, picking row", players[player_id].name);
+ debug!("Too small from {}, picking row", players[player_id].get_name());
// pick any row to take
- let rowid = players[ player_id ].pick_row_for_small_card(&table.rows, &smallest.card);
+ // let rowid = players[ player_id ].pick_row_for_small_card(&table.rows, &smallest.card);
+ let rowid = players[ player_id ].pick_row_for_small_card(table.peek_rows(), &smallest.card);
trace!("Picked row {}", rowid);
// take the row cards
let cards = table.take_row(rowid);
@@ -590,16 +151,16 @@
let mut winscore: i32 = i32::MAX-1;
for i in 0..player_count {
- info!("Player {} has {} points", players[i].name, players[i].game_point);
+ info!("Player {} has {} points", players[i].get_name(), players[i].get_points());
- if players[i].game_point < winscore {
- trace!("New winner {} with score {}", players[i].name, players[i].game_point);
+ if players[i].get_points() < winscore {
+ trace!("New winner {} with score {}", players[i].get_name(), players[i].get_points());
winners.clear();
winners.push(i);
- winscore = players[i].game_point;
+ winscore = players[i].get_points();
- } else if players[i].game_point == winscore {
- trace!("New co-winner {} with score {}", players[i].name, players[i].game_point);
+ } else if players[i].get_points() == winscore {
+ trace!("New co-winner {} with score {}", players[i].get_name(), players[i].get_points());
winners.push(i);
}
trace!("The list of winners is {:?}", winners);
@@ -614,7 +175,7 @@
// collect cards from table
deck.push_cards( table.collect_rows() );
- trace!("Shall have full deck now, len {}", deck.content.len());
+ trace!("Shall have full deck now, len {}", deck.len());
let mut finals = Vec::new();
for i in winners.iter() {
@@ -622,7 +183,7 @@
}
for i in winners {
- finals.push( &players[i].name );
+ finals.push( players[i].get_name() );
}
info!("The winner(s): {:?}", &finals);
@@ -635,9 +196,10 @@
*/
}
+ // do the type conversions _very_ visibly
let elapsed_micro: f64 = stats.start_time.elapsed().as_micros() as f64;
let game_rounds: f64 = GAME_ROUNDS.into();
-
+ // which is same as the slightly uglier
let _res: f64 = stats.start_time.elapsed().as_micros() as f64 / <i32 as Into<f64>>::into(GAME_ROUNDS);
println!("Totals (game time {} µs, or {} s; {} µs/game), {} games played ({} shuffles):",
@@ -650,22 +212,24 @@
// players.sort_by( |a, b| a.total_point.partial_cmp(&b.total_point).unwrap() ); // ASC points
// players.sort_by( |a, b| b.wins.partial_cmp(&a.wins).unwrap() ); // DESC wins
- players.sort_by_cached_key( |x| Reverse(x.wins) ); // DESC wins (caching is just for the show)
+ players.sort_by_cached_key( |x| Reverse(x.get_wins()) ); // DESC wins (caching is just for the show)
for i in 0..players.len() {
let p = &players[i];
- println!("Player {} has wins {}, score {} (busted {} times)", p.name, p.wins, p.total_point, p.rows_busted);
+ println!("Player {} has wins {}, score {} (busted {} times)",
+ p.get_name(),
+ p.get_wins(),
+ p.get_total_points(),
+ p.get_rows_busted());
}
}
#[cfg(test)]
mod tests {
- // use core::panic;
use std::collections::VecDeque;
-
use rand::Rng;
- use crate::{Card, Player, Row, Table, PlayerCard};
+ use crate::{card::Card, player::{Player, PlayerCard}, row::Row, table::Table};
#[test]
fn card_values() {
@@ -693,7 +257,7 @@
}
// give the pile to player
p.give_pile(pile);
- assert!( p.rows_busted == 1 );
+ assert!( p.get_rows_busted() == 1 );
// get back the pile from player
// p = p.gimme_pile();
@@ -710,7 +274,7 @@
pile.push_back(c);
}
p.give_pile(pile);
- assert!( p.rows_busted == 2 );
+ assert!( p.get_rows_busted() == 2 );
}
#[test]
@@ -737,7 +301,7 @@
// check card value
assert!( row.last_card_value() == cval, "Last card value mismatch: got {} vs expected {}", row.last_card_value(), cval );
}
- assert!( row.cards.len() == 2, "Row contains wrong amount of cards: {}", row.cards.len() );
+ assert!( row.len() == 2, "Row contains wrong amount of cards: {}", row.len() );
}
#[test]