src/main.rs
changeset 5 0dd7f2c9fd81
parent 4 a2f0cb2b5c13
child 8 91154fe07100
--- a/src/main.rs	Tue Jan 31 23:25:50 2023 +0100
+++ b/src/main.rs	Sat Feb 04 22:46:13 2023 +0100
@@ -1,65 +1,95 @@
 // #[allow(dead_code, unused)]
-// vigyazz6! autplayer
+
+// vigyazz6! autoplayer
+//
+// This is a program automagically playing a card game called "Vigyazz6!".
+//
+// The rules are rather simple, but I'mrather lazy to reprint it in detail,
+// so it'll be brief.
+// 
+// The code is based on a deck of 104 numbered cards, all of them having
+// a face `value` and punishment `points` (the less the better).
+// There are 2 to 10 players (but above 8 the code shall be modified to
+// deal less cards), and the game has 5 rows of maximum 5 cards on the
+// table. 
+// All players throw cards at once, and they are put in the row with 
+// closest less value; if there is none, they have to pick and take
+// any row. When a row is full (5) they have to take the row and their
+// card starts a new row head.
+// When players are out of cards the least points collected wins.
+//
+// Important: I do not use `unsafe` code, this is all tightly controlled.
 //
 
+/// We use panic in several places where the code shall not go. 
 use core::panic;
+/// `VecDeque` is a Vec which can be pushed/popped at both ends
+/// `time::Instant` is used to measure running time.
+/// `cmp::Reverse` used for reverse sorting.
 use std::{collections::VecDeque, time::Instant, cmp::Reverse};
 
-use crate::{deck::Deck, player::Player, table::Table};
-// use rand::Rng;
-// use std::mem;
+/// We use this `pretty_env_logger` as a logging framework.
+extern crate pretty_env_logger;
+#[macro_use] extern crate log;
 
+// I import structures from my module files to use here.
+use crate::{deck::Deck, player::{Player, PlayerCard}, table::Table};
+
+// Here we read the files and "get to know" what's in them. This isn't including, only scanning.
 mod deck;
 mod table;
 mod player;
 mod card;
 mod row;
 
-extern crate pretty_env_logger;
-#[macro_use] extern crate log;
-
+// We play this many game rounds. You want to keep this under 3 for any debug level above "warnings".
 const GAME_ROUNDS: i32 = 100_000;
 
 fn main() {
+    // Env variable makes the logger to log more or less:
     // RUST_LOG=debug cargo run
     pretty_env_logger::init();
-    info!("Program starting.");
+    info!("Program starting."); // info level logging
     game();
     info!("End of run.");
 }
 
-// Card
-// Deck
-// Player
-// Row
-
-
 
 /*** Game ****/
 
+/// `GameStat` structure collects global statistics about the run.
 struct GameStat {
     game_count: i64,
     shuffle_count: i64,
     start_time: Instant, // use start_time.elapsed().as_nanos() or .as_secs()
 }
 
+/// Play all the game rounds.
 fn game() {
+    // Whole game statistics stored here
     let mut stats = GameStat { game_count:0, shuffle_count: 0, start_time: Instant::now() };
 
+    // Create the game deck with all the card (ordered). Open box. Remove shrink wrap. :-)
     let mut deck = Deck::new();
 
-    let player_names = vec![ "grin", "moni", "icbalint", "orsi", "topi", "kgb", "zsu", "csilla" ];
+    // Create the players (up to 8 w/o changing the hand cards)
+    let player_names = vec![ "peter", "moni", "icbalint", "orsi", "topi", "kgb", "zsu", "csilla" ];
+    // Iterate on player names, create Player struct from them and collect them into a Vec'tor.
+    // (On the borrow level we don't consume the player_names vector [using `iter()` instead of `into_iter()`].)
     let mut players: Vec<Player> = player_names.iter().map( |n| Player::new(n.to_string()) ).collect();
 
-    let player_count = players.len(); // pc - 1
+    // We use this often, so take it now.
+    let player_count = players.len(); // = <number of players> - 1
 
+    // Start playing - GAME_ROUNDS rounds.
     for cnt_game in 1..=GAME_ROUNDS {
-        debug!("Game round {} starts", cnt_game);
+        debug!("Game round {} starts", cnt_game); // debug level logging
+        // Shuffle the deck (using various methods)
         deck.shuffle();
         stats.shuffle_count += 1;
         stats.game_count += 1;
 
-        // dealing
+        // dealing 10 cards for each player
         debug!("Dealing.");
         for _i in 1..=10 {
             for player in 0 .. player_count {
@@ -67,75 +97,88 @@
             }
         }
 
-        // we need 5 crds from deck
+        // we need 5 cards from deck to build the rows
         debug!("Building the rows.");
         let mut cards = VecDeque::new();
+        // This is another way to write "for _ in (1..=5) { ... }"
         (1..=5).for_each(|_| {
             cards.push_back( deck.pop().expect("deck empty before starting the game") );
         });
-        // println!("We push 5 cards to rows: {:?}\n", cards);
+        trace!("We pushed 5 cards to rows: {:?}\n", cards);
+
+        // Create the Table (contains the Deck, the Rows (with 5 starting `cards`) and occasional cards thrown by Players)
         let mut table = Table::new(cards);
 
-        // DEBUG
-/*         println!("Table: {:?}\n", table);
-        println!("PLayers: {:?}\n", players);
-        println!("Deck: {:?}\n", deck);
+/*      debug!("Table: {:?}\n", table);
+        debug!("Players: {:?}\n", players);
+        debug!("Deck: {:?}\n", deck);
  */        
         debug!("We have the table ready: {:?}", table);
 
-        // playing
+        // playing one game, players taking turns
         debug!("Players start taking turns");
+        // Since players have 10 cards dealt we have exactly 10 turns.
         for turn in 1..=10 {
             debug!("Turn {}!", turn);
             trace!("The table: {:?}", table);
 
-            // everyone puts a card face down
+            // Everyone puts a card "face down" (Rust promised not to peek).
+            // If I used the `players.for_each()` form I would hit the borrow checker:
+            // `players` would be repeatedly borrowed mutable and program wouldn't compile.
             for player in 0 .. player_count {
-                let player_id: i32 = player.try_into().unwrap();
                 // get a card from the player
-                let topcard = players[player].throw_card();
+                let player_id: i32 = player.try_into().unwrap();    // convert `usize` array index into `i32` [that was an unnecessary hassle: in fact I should rewrite this using usize everywhere]
+                let topcard = players[player].throw_card();         // If Player [module] was smart this would be a chosen card, but it would need player
+                                                                    // not just brains but possibility to peek at the rows. This wasn't in the plan, so 
+                                                                    // we just take the top card in hand.
                 // put it on the table ("turned face down")
-                table.lay_player_card( topcard, player_id );
+                table.lay_player_card( topcard, player_id );        // We actually move the card variable with its ownership everywhere, so no &refs.
             }
 
-            // process cards
+            // Process cards on the Table.
             debug!("The Table process the throws.");
+            // Sort the cards by `value` the players have thrown.
             table.sort_cards();
 
+            // Pick them one by one, from smallest to larger ones
             while table.has_player_cards() {
-                let smallest = table.get_smallest_player_card();
+                let smallest: PlayerCard = table.get_smallest_player_card(); // I just wrote the PlayerCard type to show; Rust is smart enough to know.
                 trace!("Take smallest card {:?}", &smallest);
 
-                let closest_row = table.get_closest_row(&smallest);
+                // Find the row with the closest smallest card, or None if smaller than any row head.
+                let closest_row = table.get_closest_row(&smallest); // That is an Option<usize>.
                 trace!("Choose closest row: {:?}", closest_row);
 
                 match closest_row {
                     Some(rowid) => {
+                        // We have to put it at the end of `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);
+                        let player_id: usize = smallest.player_id.try_into().unwrap(); // By "just" indexing `players` (by `player_id`) I have avoided borrowing it and get into trouble later.
+                        // Try to put the card to the end of the row, or collect full row first
+                        let overflow = table.put_card_into_row(smallest, rowid); // And this is Option<VecDeque<Card>>.
                         if let Some(cards) = overflow {
-                            // row is full, got pile
-                            debug!("Row is busted, {} collects", players[player_id].get_name());
-                            // player gets pile, card gets into row head
+                            // Row is full, we got pile ("bust")
+                            debug!("Row is busted, {} collects", players[player_id].get_name());    
+                            // Player gets pile, thrown card was put 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();
+                        // Card too small, need to pick any row to take!
+                        let player_id: usize = smallest.player_id.try_into().unwrap(); // We _directly_ access `PlayerCard.player_id` structure member, because borrow checker wasn't helping my mental hygiene.
                         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);
+                        // Pick any row to take
                         let rowid = players[ player_id ].pick_row_for_small_card(table.peek_rows(), &smallest.card);
                         trace!("Picked row {}", rowid);
-                        // take the row cards
+                        // take the row cards ("bust")
                         let cards = table.take_row(rowid);
                         trace!("Took cards: {:?}", cards);
+                        // and give them to the player who owns the card
                         players[ player_id ].give_pile( cards );
                         // put new card in the row
                         let overflow = table.put_card_into_row(smallest, rowid);
                         if let Some(_) = overflow {
+                            // If this fires then I have fucked something up. :-)
                             panic!("Player took whole row and it's already full");
                         }
                     }
@@ -147,8 +190,9 @@
         info!("Round finished, len is {} µsec", stats.start_time.elapsed().as_micros());
 
         debug!("End of round, counting and collecting back piles");
+        // We collect the winner(s) and their score here
         let mut winners: Vec<usize> = Vec::new();
-        let mut winscore: i32 = i32::MAX-1;
+        let mut winscore: i32 = i32::MAX-1; // Getting facy here about "larger than any valid value".
 
         for i in 0..player_count {
             info!("Player {} has {} points", players[i].get_name(), players[i].get_points());
@@ -165,41 +209,36 @@
             }
             trace!("The list of winners is {:?}", winners);
 
-            // get pile from player
+            // get pile from Player
             let cards = players[i].get_pile();
-            // and give it back to the deck
+            // and give it back to the Deck
             deck.push_cards(cards);
-            // close player round and update stats
+            // close Player round and update stats
             players[i].close_round();
         }
 
-        // collect cards from table
+        // collect remaining Cards from Table
         deck.push_cards( table.collect_rows() );
         trace!("Shall have full deck now, len {}", deck.len());
 
-        let mut finals = Vec::new();
+        // This is a Vec of `player_id`s, i is a &ref
         for i in winners.iter() {
-            players[*i].inc_wins();
+            players[*i].inc_wins(); // Increment win count of &Vec<usize> player_id
         }
 
+        // Collect the names of the winners.
+        let mut finals = Vec::new();
         for i in winners {
             finals.push( players[i].get_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();
-        });
- */
     }
 
-    // do the type conversions _very_ visibly 
+    // Do the type conversions _very_ visibly. `as_micros()` results `u128`
     let elapsed_micro: f64 = stats.start_time.elapsed().as_micros() as f64;
+    // and `GAME_ROUNDS` is `i32`. We'll use `elapsed_micro/game_rounds` later.
     let game_rounds: f64 = GAME_ROUNDS.into();
-    // which is same as the slightly uglier
+    // …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):", 
@@ -212,10 +251,11 @@
 
     // 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.get_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); using std::cmp::Reverse
 
+    // Print out totals
     for i in 0..players.len() {
-        let p = &players[i];
+        let p = &players[i]; // the rest looks simpler by using this. (Seems to confuse rust-analyzer in codium though.)
         println!("Player {} has wins {}, score {} (busted {} times)", 
             p.get_name(), 
             p.get_wins(),