Final version :-)
authorPeter Gervai <grin@grin.hu>
Tue, 31 Jan 2023 00:15:54 +0100
changeset 2 950660fddec0
parent 1 5f81068a8a88
child 3 3d9dba5b16e0
Final version :-)
Cargo.toml
src/main.rs
--- a/Cargo.toml	Sun Jan 29 23:59:45 2023 +0100
+++ b/Cargo.toml	Tue Jan 31 00:15:54 2023 +0100
@@ -7,3 +7,5 @@
 
 [dependencies]
 rand = "0"
+log="0.4"
+pretty_env_logger="0.4"
--- 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
+    }
 }
 
 /*