src/main.rs
changeset 5 0dd7f2c9fd81
parent 4 a2f0cb2b5c13
child 8 91154fe07100
equal deleted inserted replaced
4:a2f0cb2b5c13 5:0dd7f2c9fd81
     1 // #[allow(dead_code, unused)]
     1 // #[allow(dead_code, unused)]
     2 // vigyazz6! autplayer
     2 
       
     3 // vigyazz6! autoplayer
     3 //
     4 //
     4 
     5 // This is a program automagically playing a card game called "Vigyazz6!".
       
     6 //
       
     7 // The rules are rather simple, but I'mrather lazy to reprint it in detail,
       
     8 // so it'll be brief.
       
     9 // 
       
    10 // The code is based on a deck of 104 numbered cards, all of them having
       
    11 // a face `value` and punishment `points` (the less the better).
       
    12 // There are 2 to 10 players (but above 8 the code shall be modified to
       
    13 // deal less cards), and the game has 5 rows of maximum 5 cards on the
       
    14 // table. 
       
    15 // All players throw cards at once, and they are put in the row with 
       
    16 // closest less value; if there is none, they have to pick and take
       
    17 // any row. When a row is full (5) they have to take the row and their
       
    18 // card starts a new row head.
       
    19 // When players are out of cards the least points collected wins.
       
    20 //
       
    21 // Important: I do not use `unsafe` code, this is all tightly controlled.
       
    22 //
       
    23 
       
    24 /// We use panic in several places where the code shall not go. 
     5 use core::panic;
    25 use core::panic;
       
    26 /// `VecDeque` is a Vec which can be pushed/popped at both ends
       
    27 /// `time::Instant` is used to measure running time.
       
    28 /// `cmp::Reverse` used for reverse sorting.
     6 use std::{collections::VecDeque, time::Instant, cmp::Reverse};
    29 use std::{collections::VecDeque, time::Instant, cmp::Reverse};
     7 
    30 
     8 use crate::{deck::Deck, player::Player, table::Table};
    31 /// We use this `pretty_env_logger` as a logging framework.
     9 // use rand::Rng;
    32 extern crate pretty_env_logger;
    10 // use std::mem;
    33 #[macro_use] extern crate log;
    11 
    34 
       
    35 // I import structures from my module files to use here.
       
    36 use crate::{deck::Deck, player::{Player, PlayerCard}, table::Table};
       
    37 
       
    38 // Here we read the files and "get to know" what's in them. This isn't including, only scanning.
    12 mod deck;
    39 mod deck;
    13 mod table;
    40 mod table;
    14 mod player;
    41 mod player;
    15 mod card;
    42 mod card;
    16 mod row;
    43 mod row;
    17 
    44 
    18 extern crate pretty_env_logger;
    45 // We play this many game rounds. You want to keep this under 3 for any debug level above "warnings".
    19 #[macro_use] extern crate log;
       
    20 
       
    21 const GAME_ROUNDS: i32 = 100_000;
    46 const GAME_ROUNDS: i32 = 100_000;
    22 
    47 
    23 fn main() {
    48 fn main() {
       
    49     // Env variable makes the logger to log more or less:
    24     // RUST_LOG=debug cargo run
    50     // RUST_LOG=debug cargo run
    25     pretty_env_logger::init();
    51     pretty_env_logger::init();
    26     info!("Program starting.");
    52     info!("Program starting."); // info level logging
    27     game();
    53     game();
    28     info!("End of run.");
    54     info!("End of run.");
    29 }
    55 }
    30 
    56 
    31 // Card
       
    32 // Deck
       
    33 // Player
       
    34 // Row
       
    35 
       
    36 
       
    37 
    57 
    38 /*** Game ****/
    58 /*** Game ****/
    39 
    59 
       
    60 /// `GameStat` structure collects global statistics about the run.
    40 struct GameStat {
    61 struct GameStat {
    41     game_count: i64,
    62     game_count: i64,
    42     shuffle_count: i64,
    63     shuffle_count: i64,
    43     start_time: Instant, // use start_time.elapsed().as_nanos() or .as_secs()
    64     start_time: Instant, // use start_time.elapsed().as_nanos() or .as_secs()
    44 }
    65 }
    45 
    66 
       
    67 /// Play all the game rounds.
    46 fn game() {
    68 fn game() {
       
    69     // Whole game statistics stored here
    47     let mut stats = GameStat { game_count:0, shuffle_count: 0, start_time: Instant::now() };
    70     let mut stats = GameStat { game_count:0, shuffle_count: 0, start_time: Instant::now() };
    48 
    71 
       
    72     // Create the game deck with all the card (ordered). Open box. Remove shrink wrap. :-)
    49     let mut deck = Deck::new();
    73     let mut deck = Deck::new();
    50 
    74 
    51     let player_names = vec![ "grin", "moni", "icbalint", "orsi", "topi", "kgb", "zsu", "csilla" ];
    75     // Create the players (up to 8 w/o changing the hand cards)
       
    76     let player_names = vec![ "peter", "moni", "icbalint", "orsi", "topi", "kgb", "zsu", "csilla" ];
       
    77     // Iterate on player names, create Player struct from them and collect them into a Vec'tor.
       
    78     // (On the borrow level we don't consume the player_names vector [using `iter()` instead of `into_iter()`].)
    52     let mut players: Vec<Player> = player_names.iter().map( |n| Player::new(n.to_string()) ).collect();
    79     let mut players: Vec<Player> = player_names.iter().map( |n| Player::new(n.to_string()) ).collect();
    53 
    80 
    54     let player_count = players.len(); // pc - 1
    81     // We use this often, so take it now.
    55 
    82     let player_count = players.len(); // = <number of players> - 1
       
    83 
       
    84     // Start playing - GAME_ROUNDS rounds.
    56     for cnt_game in 1..=GAME_ROUNDS {
    85     for cnt_game in 1..=GAME_ROUNDS {
    57         debug!("Game round {} starts", cnt_game);
    86         debug!("Game round {} starts", cnt_game); // debug level logging
       
    87         // Shuffle the deck (using various methods)
    58         deck.shuffle();
    88         deck.shuffle();
    59         stats.shuffle_count += 1;
    89         stats.shuffle_count += 1;
    60         stats.game_count += 1;
    90         stats.game_count += 1;
    61 
    91 
    62         // dealing
    92         // dealing 10 cards for each player
    63         debug!("Dealing.");
    93         debug!("Dealing.");
    64         for _i in 1..=10 {
    94         for _i in 1..=10 {
    65             for player in 0 .. player_count {
    95             for player in 0 .. player_count {
    66                 players[player].get_card( deck.pop().expect("Deck is empty while dealing to players") );
    96                 players[player].get_card( deck.pop().expect("Deck is empty while dealing to players") );
    67             }
    97             }
    68         }
    98         }
    69 
    99 
    70         // we need 5 crds from deck
   100         // we need 5 cards from deck to build the rows
    71         debug!("Building the rows.");
   101         debug!("Building the rows.");
    72         let mut cards = VecDeque::new();
   102         let mut cards = VecDeque::new();
       
   103         // This is another way to write "for _ in (1..=5) { ... }"
    73         (1..=5).for_each(|_| {
   104         (1..=5).for_each(|_| {
    74             cards.push_back( deck.pop().expect("deck empty before starting the game") );
   105             cards.push_back( deck.pop().expect("deck empty before starting the game") );
    75         });
   106         });
    76         // println!("We push 5 cards to rows: {:?}\n", cards);
   107         trace!("We pushed 5 cards to rows: {:?}\n", cards);
       
   108 
       
   109         // Create the Table (contains the Deck, the Rows (with 5 starting `cards`) and occasional cards thrown by Players)
    77         let mut table = Table::new(cards);
   110         let mut table = Table::new(cards);
    78 
   111 
    79         // DEBUG
   112 /*      debug!("Table: {:?}\n", table);
    80 /*         println!("Table: {:?}\n", table);
   113         debug!("Players: {:?}\n", players);
    81         println!("PLayers: {:?}\n", players);
   114         debug!("Deck: {:?}\n", deck);
    82         println!("Deck: {:?}\n", deck);
       
    83  */        
   115  */        
    84         debug!("We have the table ready: {:?}", table);
   116         debug!("We have the table ready: {:?}", table);
    85 
   117 
    86         // playing
   118         // playing one game, players taking turns
    87         debug!("Players start taking turns");
   119         debug!("Players start taking turns");
       
   120         // Since players have 10 cards dealt we have exactly 10 turns.
    88         for turn in 1..=10 {
   121         for turn in 1..=10 {
    89             debug!("Turn {}!", turn);
   122             debug!("Turn {}!", turn);
    90             trace!("The table: {:?}", table);
   123             trace!("The table: {:?}", table);
    91 
   124 
    92             // everyone puts a card face down
   125             // Everyone puts a card "face down" (Rust promised not to peek).
       
   126             // If I used the `players.for_each()` form I would hit the borrow checker:
       
   127             // `players` would be repeatedly borrowed mutable and program wouldn't compile.
    93             for player in 0 .. player_count {
   128             for player in 0 .. player_count {
    94                 let player_id: i32 = player.try_into().unwrap();
       
    95                 // get a card from the player
   129                 // get a card from the player
    96                 let topcard = players[player].throw_card();
   130                 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]
       
   131                 let topcard = players[player].throw_card();         // If Player [module] was smart this would be a chosen card, but it would need player
       
   132                                                                     // not just brains but possibility to peek at the rows. This wasn't in the plan, so 
       
   133                                                                     // we just take the top card in hand.
    97                 // put it on the table ("turned face down")
   134                 // put it on the table ("turned face down")
    98                 table.lay_player_card( topcard, player_id );
   135                 table.lay_player_card( topcard, player_id );        // We actually move the card variable with its ownership everywhere, so no &refs.
    99             }
   136             }
   100 
   137 
   101             // process cards
   138             // Process cards on the Table.
   102             debug!("The Table process the throws.");
   139             debug!("The Table process the throws.");
       
   140             // Sort the cards by `value` the players have thrown.
   103             table.sort_cards();
   141             table.sort_cards();
   104 
   142 
       
   143             // Pick them one by one, from smallest to larger ones
   105             while table.has_player_cards() {
   144             while table.has_player_cards() {
   106                 let smallest = table.get_smallest_player_card();
   145                 let smallest: PlayerCard = table.get_smallest_player_card(); // I just wrote the PlayerCard type to show; Rust is smart enough to know.
   107                 trace!("Take smallest card {:?}", &smallest);
   146                 trace!("Take smallest card {:?}", &smallest);
   108 
   147 
   109                 let closest_row = table.get_closest_row(&smallest);
   148                 // Find the row with the closest smallest card, or None if smaller than any row head.
       
   149                 let closest_row = table.get_closest_row(&smallest); // That is an Option<usize>.
   110                 trace!("Choose closest row: {:?}", closest_row);
   150                 trace!("Choose closest row: {:?}", closest_row);
   111 
   151 
   112                 match closest_row {
   152                 match closest_row {
   113                     Some(rowid) => {
   153                     Some(rowid) => {
       
   154                         // We have to put it at the end of `rowid`.
   114                         debug!("Putting down card into row {}", rowid);
   155                         debug!("Putting down card into row {}", rowid);
   115                         let player_id: usize = smallest.player_id.try_into().unwrap();
   156                         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.
   116                         let overflow = table.put_card_into_row(smallest, rowid);
   157                         // Try to put the card to the end of the row, or collect full row first
       
   158                         let overflow = table.put_card_into_row(smallest, rowid); // And this is Option<VecDeque<Card>>.
   117                         if let Some(cards) = overflow {
   159                         if let Some(cards) = overflow {
   118                             // row is full, got pile
   160                             // Row is full, we got pile ("bust")
   119                             debug!("Row is busted, {} collects", players[player_id].get_name());
   161                             debug!("Row is busted, {} collects", players[player_id].get_name());    
   120                             // player gets pile, card gets into row head
   162                             // Player gets pile, thrown card was put into row head
   121                             players[ player_id ].give_pile( cards );
   163                             players[ player_id ].give_pile( cards );
   122                         }
   164                         }
   123                     },
   165                     },
   124                     None => {
   166                     None => {
   125                         // card too small, need to pick row!
   167                         // Card too small, need to pick any row to take!
   126                         let player_id: usize = smallest.player_id.try_into().unwrap();
   168                         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.
   127                         debug!("Too small from {}, picking row", players[player_id].get_name());
   169                         debug!("Too small from {}, picking row", players[player_id].get_name());
   128                         // pick any row to take
   170                         // Pick any row to take
   129                         // let rowid = players[ player_id ].pick_row_for_small_card(&table.rows, &smallest.card);
       
   130                         let rowid = players[ player_id ].pick_row_for_small_card(table.peek_rows(), &smallest.card);
   171                         let rowid = players[ player_id ].pick_row_for_small_card(table.peek_rows(), &smallest.card);
   131                         trace!("Picked row {}", rowid);
   172                         trace!("Picked row {}", rowid);
   132                         // take the row cards
   173                         // take the row cards ("bust")
   133                         let cards = table.take_row(rowid);
   174                         let cards = table.take_row(rowid);
   134                         trace!("Took cards: {:?}", cards);
   175                         trace!("Took cards: {:?}", cards);
       
   176                         // and give them to the player who owns the card
   135                         players[ player_id ].give_pile( cards );
   177                         players[ player_id ].give_pile( cards );
   136                         // put new card in the row
   178                         // put new card in the row
   137                         let overflow = table.put_card_into_row(smallest, rowid);
   179                         let overflow = table.put_card_into_row(smallest, rowid);
   138                         if let Some(_) = overflow {
   180                         if let Some(_) = overflow {
       
   181                             // If this fires then I have fucked something up. :-)
   139                             panic!("Player took whole row and it's already full");
   182                             panic!("Player took whole row and it's already full");
   140                         }
   183                         }
   141                     }
   184                     }
   142                 }
   185                 }
   143             }
   186             }
   145 
   188 
   146         // end of round
   189         // end of round
   147         info!("Round finished, len is {} µsec", stats.start_time.elapsed().as_micros());
   190         info!("Round finished, len is {} µsec", stats.start_time.elapsed().as_micros());
   148 
   191 
   149         debug!("End of round, counting and collecting back piles");
   192         debug!("End of round, counting and collecting back piles");
       
   193         // We collect the winner(s) and their score here
   150         let mut winners: Vec<usize> = Vec::new();
   194         let mut winners: Vec<usize> = Vec::new();
   151         let mut winscore: i32 = i32::MAX-1;
   195         let mut winscore: i32 = i32::MAX-1; // Getting facy here about "larger than any valid value".
   152 
   196 
   153         for i in 0..player_count {
   197         for i in 0..player_count {
   154             info!("Player {} has {} points", players[i].get_name(), players[i].get_points());
   198             info!("Player {} has {} points", players[i].get_name(), players[i].get_points());
   155 
   199 
   156             if players[i].get_points() < winscore  {
   200             if players[i].get_points() < winscore  {
   163                 trace!("New co-winner {} with score {}", players[i].get_name(), players[i].get_points());
   207                 trace!("New co-winner {} with score {}", players[i].get_name(), players[i].get_points());
   164                 winners.push(i);
   208                 winners.push(i);
   165             }
   209             }
   166             trace!("The list of winners is {:?}", winners);
   210             trace!("The list of winners is {:?}", winners);
   167 
   211 
   168             // get pile from player
   212             // get pile from Player
   169             let cards = players[i].get_pile();
   213             let cards = players[i].get_pile();
   170             // and give it back to the deck
   214             // and give it back to the Deck
   171             deck.push_cards(cards);
   215             deck.push_cards(cards);
   172             // close player round and update stats
   216             // close Player round and update stats
   173             players[i].close_round();
   217             players[i].close_round();
   174         }
   218         }
   175 
   219 
   176         // collect cards from table
   220         // collect remaining Cards from Table
   177         deck.push_cards( table.collect_rows() );
   221         deck.push_cards( table.collect_rows() );
   178         trace!("Shall have full deck now, len {}", deck.len());
   222         trace!("Shall have full deck now, len {}", deck.len());
   179 
   223 
       
   224         // This is a Vec of `player_id`s, i is a &ref
       
   225         for i in winners.iter() {
       
   226             players[*i].inc_wins(); // Increment win count of &Vec<usize> player_id
       
   227         }
       
   228 
       
   229         // Collect the names of the winners.
   180         let mut finals = Vec::new();
   230         let mut finals = Vec::new();
   181         for i in winners.iter() {
       
   182             players[*i].inc_wins();
       
   183         }
       
   184 
       
   185         for i in winners {
   231         for i in winners {
   186             finals.push( players[i].get_name() );
   232             finals.push( players[i].get_name() );
   187         }
   233         }
   188         info!("The winner(s): {:?}", &finals);
   234         info!("The winner(s): {:?}", &finals);
   189 
   235     }
   190 /*         players.iter().for_each(|player| {
   236 
   191             println!("Player {} has {} points", player.name, player.game_point);
   237     // Do the type conversions _very_ visibly. `as_micros()` results `u128`
   192             let cards = player.get_pile();
       
   193             deck.push_cards(cards);
       
   194             player.close_round();
       
   195         });
       
   196  */
       
   197     }
       
   198 
       
   199     // do the type conversions _very_ visibly 
       
   200     let elapsed_micro: f64 = stats.start_time.elapsed().as_micros() as f64;
   238     let elapsed_micro: f64 = stats.start_time.elapsed().as_micros() as f64;
       
   239     // and `GAME_ROUNDS` is `i32`. We'll use `elapsed_micro/game_rounds` later.
   201     let game_rounds: f64 = GAME_ROUNDS.into();
   240     let game_rounds: f64 = GAME_ROUNDS.into();
   202     // which is same as the slightly uglier
   241     // …which is same as the slightly uglier:
   203     let _res: f64 = stats.start_time.elapsed().as_micros() as f64 / <i32 as Into<f64>>::into(GAME_ROUNDS);
   242     let _res: f64 = stats.start_time.elapsed().as_micros() as f64 / <i32 as Into<f64>>::into(GAME_ROUNDS);
   204 
   243 
   205     println!("Totals (game time {} µs, or {} s; {} µs/game), {} games played ({} shuffles):", 
   244     println!("Totals (game time {} µs, or {} s; {} µs/game), {} games played ({} shuffles):", 
   206         stats.start_time.elapsed().as_micros(), 
   245         stats.start_time.elapsed().as_micros(), 
   207         stats.start_time.elapsed().as_secs(),
   246         stats.start_time.elapsed().as_secs(),
   210         stats.shuffle_count,
   249         stats.shuffle_count,
   211     );
   250     );
   212 
   251 
   213     // players.sort_by( |a, b| a.total_point.partial_cmp(&b.total_point).unwrap() );    // ASC points
   252     // players.sort_by( |a, b| a.total_point.partial_cmp(&b.total_point).unwrap() );    // ASC points
   214     // players.sort_by( |a, b| b.wins.partial_cmp(&a.wins).unwrap() );                  // DESC wins
   253     // players.sort_by( |a, b| b.wins.partial_cmp(&a.wins).unwrap() );                  // DESC wins
   215     players.sort_by_cached_key( |x| Reverse(x.get_wins()) );                                  // DESC wins (caching is just for the show)
   254     players.sort_by_cached_key( |x| Reverse(x.get_wins()) );                            // DESC wins (caching is just for the show); using std::cmp::Reverse
   216 
   255 
       
   256     // Print out totals
   217     for i in 0..players.len() {
   257     for i in 0..players.len() {
   218         let p = &players[i];
   258         let p = &players[i]; // the rest looks simpler by using this. (Seems to confuse rust-analyzer in codium though.)
   219         println!("Player {} has wins {}, score {} (busted {} times)", 
   259         println!("Player {} has wins {}, score {} (busted {} times)", 
   220             p.get_name(), 
   260             p.get_name(), 
   221             p.get_wins(), 
   261             p.get_wins(), 
   222             p.get_total_points(), 
   262             p.get_total_points(), 
   223             p.get_rows_busted());
   263             p.get_rows_busted());