--- a/src/main.rs Sun Jan 29 23:59:45 2023 +0100
+++ b/src/main.rs Tue Jan 31 00:15:54 2023 +0100
@@ -1,15 +1,26 @@
+// #[allow(dead_code, unused)]
// vigyazz6! autplayer
//
use core::{fmt, panic};
-use std::collections::VecDeque;
+use std::{collections::VecDeque, time::Instant, i64::MAX};
use rand::Rng;
use std::mem;
+extern crate pretty_env_logger;
+#[macro_use] extern crate log;
+
+const GAME_ROUNDS: i32 = 1_000_000;
+
fn main() {
+ // RUST_LOG=debug cargo run
+ pretty_env_logger::init();
+ info!("Program starting.");
game();
+ info!("End of run.");
}
+/*** Card ****/
#[derive(Debug)]
struct Card {
value: i8,
@@ -78,7 +89,7 @@
}
}
-
+/*** Deck ****/
#[derive(Debug)]
struct Deck {
content: VecDeque<Card>,
@@ -86,13 +97,15 @@
impl Deck {
fn new_empty() -> Self {
+ debug!("Empty deck generated");
Deck {
content: VecDeque::new(),
}
}
fn new() -> Self {
- let content = (1..104).into_iter().map( |n| Card::new(n) ).collect();
+ debug!("Full deck generated");
+ let content = (1..=104).into_iter().map( |n| Card::new(n) ).collect();
Deck {
content,
}
@@ -100,7 +113,9 @@
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
@@ -114,6 +129,7 @@
self.content.swap(c1, c2);
}
}
+ trace!("Deck after shuffle: len {}, {:?}", self.content.len(), self);
}
// get top card from deck
@@ -126,6 +142,12 @@
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()
}
@@ -139,36 +161,42 @@
}
}
+/*** Player ****/
#[derive(Debug)]
struct Player {
name: String,
hand: Deck,
- pile: Vec<Card>,
+ 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: Vec::new(),
+ 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);
@@ -176,12 +204,13 @@
}
// get a busted row of cards
- fn get_pile( &mut self, cards: VecDeque<Card> ) {
+ fn give_pile( &mut self, cards: VecDeque<Card> ) {
for c in cards.into_iter() {
self.game_point += c.points as i32;
- self.pile.push(c);
+ self.pile.push_back(c);
}
self.rows_busted += 1;
+ trace!("Player {} got busted, count {}", self.name, &self.rows_busted);
}
// ask the player their score
@@ -189,8 +218,13 @@
self.game_point
}
+ fn inc_wins( &mut self ) {
+ self.wins += 1;
+ }
+
// give back cards from the pile
- fn give_pile( &mut self ) -> Vec<Card> {
+ 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()
@@ -222,11 +256,54 @@
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::new();
+ // 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 {
@@ -243,11 +320,14 @@
}
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);
@@ -258,12 +338,27 @@
}
}
+ 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());
+ // 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,
@@ -291,14 +386,15 @@
}
}
+/*** Table ****/
#[derive(Debug)]
struct Table {
rows: Vec<Row>,
- player_cards: Vec<PlayerCard>, // owned by a player
+ player_cards: VecDeque<PlayerCard>, // owned by a player
}
impl Table {
- fn new(row_cards: Vec<Card>) -> Self {
+ fn new(row_cards: VecDeque<Card>) -> Self {
let mut rows = Vec::new();
for card in row_cards {
// create a new row then put a card into it
@@ -311,35 +407,84 @@
Table {
rows,
- player_cards: Vec::new(),
+ player_cards: VecDeque::new(),
}
}
fn lay_player_card( &mut self, card: Card, player_id: i32 ) {
- self.player_cards.push( PlayerCard { player_id, card } );
+ self.player_cards.push_back( PlayerCard { player_id, card } );
}
fn sort_cards( &mut self ) {
- self.player_cards.sort_by( |a,b| b.card.cmp(&a.card) );
+ 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 {
- self.player_cards.pop().expect("out of player cards on table")
+ // 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_closest_row( &self, pcard: &PlayerCard ) -> Option<i8> {
- // get the row id with last card closest smaller to players'
- todo!()
+ 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
}
- fn put_card_into_row( &mut self, pcard: PlayerCard, row_id: i8 ) -> Option<VecDeque<Card>> {
- self.rows[row_id as usize].push_or_collect(pcard.card)
+ // 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 ****/
+
+struct GameStat {
+ game_count: i64,
+ shuffle_count: i64,
+ start_time: Instant, // use start_time.elapsed().as_nanos() or .as_secs()
+}
+
fn game() {
- let mut cnt_shuffle = 0;
+ let mut stats = GameStat { game_count:0, shuffle_count: 0, start_time: Instant::now() };
let mut deck = Deck::new();
@@ -348,11 +493,14 @@
let player_count = players.len(); // pc - 1
- for _cnt_game in 1..=2 {
+ for cnt_game in 1..=GAME_ROUNDS {
+ debug!("Game round {} starts", cnt_game);
deck.shuffle();
- cnt_shuffle += 1;
+ stats.shuffle_count += 1;
+ stats.game_count += 1;
// dealing
+ debug!("Dealing.");
for i in 1..=10 {
for player in 0 .. player_count {
players[player].get_card( deck.pop().expect("Deck is empty while dealing to players") );
@@ -360,21 +508,26 @@
}
// we need 5 crds from deck
- let mut cards = Vec::new();
+ debug!("Building the rows.");
+ let mut cards = VecDeque::new();
for i in 1..=5 {
- cards.push( deck.pop().expect("deck empty before starting the game") );
+ cards.push_back( deck.pop().expect("deck empty before starting the game") );
}
- println!("We push 5 cards to rows: {:?}\n", cards);
+ // println!("We push 5 cards to rows: {:?}\n", cards);
let mut table = Table::new(cards);
// DEBUG
- println!("Table: {:?}\n", table);
+/* println!("Table: {:?}\n", table);
println!("PLayers: {:?}\n", players);
println!("Deck: {:?}\n", deck);
-
+ */
+ debug!("We have the table ready: {:?}", table);
// playing
+ debug!("Players start taking turns");
for turn in 1..=10 {
+ debug!("Turn {}!", turn);
+ trace!("The table: {:?}", table);
// everyone puts a card face down
for player in 0 .. player_count {
@@ -386,34 +539,114 @@
}
// process cards
+ debug!("The Table process the throws.");
table.sort_cards();
- let smallest = table.get_smallest_player_card();
+ while table.has_player_cards() {
+ let smallest = table.get_smallest_player_card();
+ trace!("Take smallest card {:?}", &smallest);
+
+ let closest_row = table.get_closest_row(&smallest);
+ trace!("Choose closest row: {:?}", closest_row);
- let closest_row = table.get_closest_row(&smallest);
- match closest_row {
- Some(rowid) => {
- let player_id: usize = smallest.player_id.try_into().unwrap();
- let overflow = table.put_card_into_row(smallest, rowid);
- if let Some(cards) = overflow {
- // row is full, got pile
- // player gets pile, card gets into row head
- players[ player_id ].get_pile( cards );
+ match closest_row {
+ Some(rowid) => {
+ debug!("Putting down card into row {}", rowid);
+ let player_id: usize = smallest.player_id.try_into().unwrap();
+ 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);
+ // player gets pile, card gets into row head
+ players[ player_id ].give_pile( cards );
+ }
+ },
+ 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);
+ // pick any row to take
+ let rowid = players[ player_id ].pick_row_for_small_card(&table.rows, &smallest.card);
+ trace!("Picked row {}", rowid);
+ // take the row cards
+ let cards = table.take_row(rowid);
+ trace!("Took cards: {:?}", cards);
+ players[ player_id ].give_pile( cards );
+ // put new card in the row
+ let overflow = table.put_card_into_row(smallest, rowid);
+ if let Some(c) = overflow {
+ panic!("Player took whole row and it's already full");
+ }
}
- },
- None => {
- // card too small, need to pick row!
- todo!();
}
}
-
}
// end of round
+ info!("Round finished, len is {} µsec", stats.start_time.elapsed().as_micros());
- // recollect deck
+ debug!("End of round, counting and collecting back piles");
+ let mut winners: Vec<usize> = Vec::new();
+ let mut winscore: i32 = i32::MAX-1;
+
+ for i in 0..player_count {
+ info!("Player {} has {} points", players[i].name, players[i].game_point);
+
+ if players[i].game_point < winscore {
+ trace!("New winner {} with score {}", players[i].name, players[i].game_point);
+ winners.clear();
+ winners.push(i);
+ winscore = players[i].game_point;
+
+ } else if players[i].game_point == winscore {
+ trace!("New co-winner {} with score {}", players[i].name, players[i].game_point);
+ winners.push(i);
+ }
+ trace!("The list of winners is {:?}", winners);
+
+ // get pile from player
+ let cards = players[i].get_pile();
+ // and give it back to the deck
+ deck.push_cards(cards);
+ // close player round and update stats
+ players[i].close_round();
+ }
+
+ // collect cards from table
+ deck.push_cards( table.collect_rows() );
+ trace!("Shall have full deck now, len {}", deck.content.len());
- panic!("FInish now.");
+ let mut finals = Vec::new();
+ for i in winners.iter() {
+ players[*i].inc_wins();
+ }
+
+ for i in winners {
+ finals.push( &players[i].name );
+ }
+ info!("The winner(s): {:?}", &finals);
+
+/* players.iter().for_each(|player| {
+ println!("Player {} has {} points", player.name, player.game_point);
+ let cards = player.get_pile();
+ deck.push_cards(cards);
+ player.close_round();
+ });
+ */
+ }
+
+ println!("Totals (game time {} µs, or {} s), {} games played ({} shuffles):",
+ stats.start_time.elapsed().as_micros(),
+ stats.start_time.elapsed().as_secs(),
+ stats.game_count,
+ stats.shuffle_count,
+ );
+
+ players.sort_by( |a, b| a.total_point.partial_cmp(&b.total_point).unwrap() );
+
+ 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);
}
}
@@ -424,7 +657,7 @@
use rand::Rng;
- use crate::{Card, Player, Row};
+ use crate::{Card, Player, Row, Table, PlayerCard};
#[test]
fn card_values() {
@@ -443,23 +676,24 @@
let mut p = Player::new("bob".to_string());
// create a pile
let mut pile = VecDeque::new();
- let mut refpile = Vec::new();
+ let mut refpile = VecDeque::new();
for i in 5..10 {
let c = Card::new(i);
pile.push_back(c);
let c = Card::new(i);
- refpile.push(c);
+ refpile.push_back(c);
}
- // add the pile to player
- p.get_pile(pile);
+ // give the pile to player
+ p.give_pile(pile);
assert!( p.rows_busted == 1 );
// get back the pile from player
// p = p.gimme_pile();
- let pile = p.give_pile();
+ let pile = p.get_pile();
// the pile we got shall be same as the pile we gave
// this check is O(n^2), doesn't matter for less than 100 items
+ assert_eq!( pile, refpile );
assert!( pile.iter().all( |item| refpile.contains(item)) );
let mut pile = VecDeque::new();
@@ -467,14 +701,14 @@
let c = Card::new(i);
pile.push_back(c);
}
- p.get_pile(pile);
+ p.give_pile(pile);
assert!( p.rows_busted == 2 );
}
#[test]
fn row_push() {
let mut row = Row::new();
- let mut refcard = Vec::new(); // reference vec to check
+ let mut refcard = VecDeque::new(); // reference vec to check
for i in 1..=7 {
let cval = i+5;
let card = Card::new(cval);
@@ -490,7 +724,7 @@
}
// remember the correct vec for checking
let card = Card::new(cval);
- refcard.push(card);
+ refcard.push_back(card);
// check card value
assert!( row.last_card_value() == cval, "Last card value mismatch: got {} vs expected {}", row.last_card_value(), cval );
@@ -500,19 +734,47 @@
#[test]
fn sort_cards() {
- let mut cards: Vec<Card> = Vec::new();
+ let mut cards: VecDeque<Card> = VecDeque::new();
let mut rng = rand::thread_rng();
for i in 1..50 {
let n = rng.gen_range(1..104);
- cards.push( Card::new(n) );
+ cards.push_back( Card::new(n) );
}
- cards.sort();
+ cards.make_contiguous().sort();
for i in 1..cards.len() {
assert!( cards[i-1].value <= cards[i].value, "Bad ordering: {} > {}", cards[i-1].value,cards[i].value );
}
}
+ #[test]
+ fn check_closest_row() {
+ let table = generate_table();
+ let pcard = PlayerCard{ player_id: 42, card: Card::new(42) };
+ let closest = table.get_closest_row(&pcard);
+ assert_eq!( closest, Some(3) ); // index from 0
+ }
+
+ #[test]
+ fn check_smallest_player_card() {
+ let mut table = generate_table();
+
+ let mut player_id = 1;
+ vec![103, 8, 71, 93, 6].into_iter().for_each(|c| {
+ table.lay_player_card( Card::new(c), player_id);
+ player_id += 1;
+ });
+
+ let smallest = table.get_smallest_player_card();
+ assert_eq!( smallest, PlayerCard{ player_id: 5, card: Card::new(6) } );
+ }
+
+ fn generate_table() -> Table {
+ let mut row_cards = VecDeque::new();
+ vec![5,7,10,33,70].into_iter().for_each(|c| row_cards.push_back( Card::new(c) ));
+ let table = Table::new(row_cards);
+ table
+ }
}
/*