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 } |
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(), |