2
|
1 |
// #[allow(dead_code, unused)] |
0
|
2 |
// vigyazz6! autplayer |
|
3 |
// |
|
4 |
|
4
|
5 |
use core::panic; |
3
|
6 |
use std::{collections::VecDeque, time::Instant, cmp::Reverse}; |
4
|
7 |
|
|
8 |
use crate::{deck::Deck, player::Player, table::Table}; |
|
9 |
// use rand::Rng; |
|
10 |
// use std::mem; |
|
11 |
|
|
12 |
mod deck; |
|
13 |
mod table; |
|
14 |
mod player; |
|
15 |
mod card; |
|
16 |
mod row; |
0
|
17 |
|
2
|
18 |
extern crate pretty_env_logger; |
|
19 |
#[macro_use] extern crate log; |
|
20 |
|
4
|
21 |
const GAME_ROUNDS: i32 = 100_000; |
2
|
22 |
|
0
|
23 |
fn main() { |
2
|
24 |
// RUST_LOG=debug cargo run |
|
25 |
pretty_env_logger::init(); |
|
26 |
info!("Program starting."); |
1
|
27 |
game(); |
2
|
28 |
info!("End of run."); |
0
|
29 |
} |
|
30 |
|
4
|
31 |
// Card |
|
32 |
// Deck |
|
33 |
// Player |
|
34 |
// Row |
0
|
35 |
|
|
36 |
|
|
37 |
|
2
|
38 |
/*** Game ****/ |
|
39 |
|
|
40 |
struct GameStat { |
|
41 |
game_count: i64, |
|
42 |
shuffle_count: i64, |
|
43 |
start_time: Instant, // use start_time.elapsed().as_nanos() or .as_secs() |
|
44 |
} |
|
45 |
|
0
|
46 |
fn game() { |
2
|
47 |
let mut stats = GameStat { game_count:0, shuffle_count: 0, start_time: Instant::now() }; |
1
|
48 |
|
|
49 |
let mut deck = Deck::new(); |
|
50 |
|
|
51 |
let player_names = vec![ "grin", "moni", "icbalint", "orsi", "topi", "kgb", "zsu", "csilla" ]; |
|
52 |
let mut players: Vec<Player> = player_names.iter().map( |n| Player::new(n.to_string()) ).collect(); |
|
53 |
|
|
54 |
let player_count = players.len(); // pc - 1 |
|
55 |
|
2
|
56 |
for cnt_game in 1..=GAME_ROUNDS { |
|
57 |
debug!("Game round {} starts", cnt_game); |
1
|
58 |
deck.shuffle(); |
2
|
59 |
stats.shuffle_count += 1; |
|
60 |
stats.game_count += 1; |
1
|
61 |
|
|
62 |
// dealing |
2
|
63 |
debug!("Dealing."); |
3
|
64 |
for _i in 1..=10 { |
1
|
65 |
for player in 0 .. player_count { |
|
66 |
players[player].get_card( deck.pop().expect("Deck is empty while dealing to players") ); |
|
67 |
} |
|
68 |
} |
|
69 |
|
|
70 |
// we need 5 crds from deck |
2
|
71 |
debug!("Building the rows."); |
|
72 |
let mut cards = VecDeque::new(); |
3
|
73 |
(1..=5).for_each(|_| { |
2
|
74 |
cards.push_back( deck.pop().expect("deck empty before starting the game") ); |
3
|
75 |
}); |
2
|
76 |
// println!("We push 5 cards to rows: {:?}\n", cards); |
1
|
77 |
let mut table = Table::new(cards); |
|
78 |
|
|
79 |
// DEBUG |
2
|
80 |
/* println!("Table: {:?}\n", table); |
1
|
81 |
println!("PLayers: {:?}\n", players); |
|
82 |
println!("Deck: {:?}\n", deck); |
2
|
83 |
*/ |
|
84 |
debug!("We have the table ready: {:?}", table); |
1
|
85 |
|
|
86 |
// playing |
2
|
87 |
debug!("Players start taking turns"); |
1
|
88 |
for turn in 1..=10 { |
2
|
89 |
debug!("Turn {}!", turn); |
|
90 |
trace!("The table: {:?}", table); |
0
|
91 |
|
1
|
92 |
// everyone puts a card face down |
|
93 |
for player in 0 .. player_count { |
|
94 |
let player_id: i32 = player.try_into().unwrap(); |
|
95 |
// get a card from the player |
|
96 |
let topcard = players[player].throw_card(); |
|
97 |
// put it on the table ("turned face down") |
|
98 |
table.lay_player_card( topcard, player_id ); |
|
99 |
} |
|
100 |
|
|
101 |
// process cards |
2
|
102 |
debug!("The Table process the throws."); |
1
|
103 |
table.sort_cards(); |
|
104 |
|
2
|
105 |
while table.has_player_cards() { |
|
106 |
let smallest = table.get_smallest_player_card(); |
|
107 |
trace!("Take smallest card {:?}", &smallest); |
|
108 |
|
|
109 |
let closest_row = table.get_closest_row(&smallest); |
|
110 |
trace!("Choose closest row: {:?}", closest_row); |
0
|
111 |
|
2
|
112 |
match closest_row { |
|
113 |
Some(rowid) => { |
|
114 |
debug!("Putting down card into row {}", rowid); |
|
115 |
let player_id: usize = smallest.player_id.try_into().unwrap(); |
|
116 |
let overflow = table.put_card_into_row(smallest, rowid); |
|
117 |
if let Some(cards) = overflow { |
|
118 |
// row is full, got pile |
4
|
119 |
debug!("Row is busted, {} collects", players[player_id].get_name()); |
2
|
120 |
// player gets pile, card gets into row head |
|
121 |
players[ player_id ].give_pile( cards ); |
|
122 |
} |
|
123 |
}, |
|
124 |
None => { |
|
125 |
// card too small, need to pick row! |
|
126 |
let player_id: usize = smallest.player_id.try_into().unwrap(); |
4
|
127 |
debug!("Too small from {}, picking row", players[player_id].get_name()); |
2
|
128 |
// pick any row to take |
4
|
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); |
2
|
131 |
trace!("Picked row {}", rowid); |
|
132 |
// take the row cards |
|
133 |
let cards = table.take_row(rowid); |
|
134 |
trace!("Took cards: {:?}", cards); |
|
135 |
players[ player_id ].give_pile( cards ); |
|
136 |
// put new card in the row |
|
137 |
let overflow = table.put_card_into_row(smallest, rowid); |
3
|
138 |
if let Some(_) = overflow { |
2
|
139 |
panic!("Player took whole row and it's already full"); |
|
140 |
} |
1
|
141 |
} |
|
142 |
} |
|
143 |
} |
|
144 |
} |
|
145 |
|
|
146 |
// end of round |
2
|
147 |
info!("Round finished, len is {} ??sec", stats.start_time.elapsed().as_micros()); |
1
|
148 |
|
2
|
149 |
debug!("End of round, counting and collecting back piles"); |
|
150 |
let mut winners: Vec<usize> = Vec::new(); |
|
151 |
let mut winscore: i32 = i32::MAX-1; |
|
152 |
|
|
153 |
for i in 0..player_count { |
4
|
154 |
info!("Player {} has {} points", players[i].get_name(), players[i].get_points()); |
2
|
155 |
|
4
|
156 |
if players[i].get_points() < winscore { |
|
157 |
trace!("New winner {} with score {}", players[i].get_name(), players[i].get_points()); |
2
|
158 |
winners.clear(); |
|
159 |
winners.push(i); |
4
|
160 |
winscore = players[i].get_points(); |
2
|
161 |
|
4
|
162 |
} else if players[i].get_points() == winscore { |
|
163 |
trace!("New co-winner {} with score {}", players[i].get_name(), players[i].get_points()); |
2
|
164 |
winners.push(i); |
|
165 |
} |
|
166 |
trace!("The list of winners is {:?}", winners); |
|
167 |
|
|
168 |
// get pile from player |
|
169 |
let cards = players[i].get_pile(); |
|
170 |
// and give it back to the deck |
|
171 |
deck.push_cards(cards); |
|
172 |
// close player round and update stats |
|
173 |
players[i].close_round(); |
|
174 |
} |
|
175 |
|
|
176 |
// collect cards from table |
|
177 |
deck.push_cards( table.collect_rows() ); |
4
|
178 |
trace!("Shall have full deck now, len {}", deck.len()); |
1
|
179 |
|
2
|
180 |
let mut finals = Vec::new(); |
|
181 |
for i in winners.iter() { |
|
182 |
players[*i].inc_wins(); |
|
183 |
} |
|
184 |
|
|
185 |
for i in winners { |
4
|
186 |
finals.push( players[i].get_name() ); |
2
|
187 |
} |
|
188 |
info!("The winner(s): {:?}", &finals); |
|
189 |
|
|
190 |
/* players.iter().for_each(|player| { |
|
191 |
println!("Player {} has {} points", player.name, player.game_point); |
|
192 |
let cards = player.get_pile(); |
|
193 |
deck.push_cards(cards); |
|
194 |
player.close_round(); |
|
195 |
}); |
|
196 |
*/ |
|
197 |
} |
|
198 |
|
4
|
199 |
// do the type conversions _very_ visibly |
3
|
200 |
let elapsed_micro: f64 = stats.start_time.elapsed().as_micros() as f64; |
|
201 |
let game_rounds: f64 = GAME_ROUNDS.into(); |
4
|
202 |
// which is same as the slightly uglier |
3
|
203 |
let _res: f64 = stats.start_time.elapsed().as_micros() as f64 / <i32 as Into<f64>>::into(GAME_ROUNDS); |
|
204 |
|
|
205 |
println!("Totals (game time {} ??s, or {} s; {} ??s/game), {} games played ({} shuffles):", |
2
|
206 |
stats.start_time.elapsed().as_micros(), |
|
207 |
stats.start_time.elapsed().as_secs(), |
3
|
208 |
elapsed_micro / game_rounds, |
2
|
209 |
stats.game_count, |
|
210 |
stats.shuffle_count, |
|
211 |
); |
|
212 |
|
3
|
213 |
// 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 |
4
|
215 |
players.sort_by_cached_key( |x| Reverse(x.get_wins()) ); // DESC wins (caching is just for the show) |
2
|
216 |
|
|
217 |
for i in 0..players.len() { |
|
218 |
let p = &players[i]; |
4
|
219 |
println!("Player {} has wins {}, score {} (busted {} times)", |
|
220 |
p.get_name(), |
|
221 |
p.get_wins(), |
|
222 |
p.get_total_points(), |
|
223 |
p.get_rows_busted()); |
0
|
224 |
} |
|
225 |
} |
|
226 |
|
|
227 |
#[cfg(test)] |
|
228 |
mod tests { |
1
|
229 |
use std::collections::VecDeque; |
|
230 |
use rand::Rng; |
|
231 |
|
4
|
232 |
use crate::{card::Card, player::{Player, PlayerCard}, row::Row, table::Table}; |
0
|
233 |
|
|
234 |
#[test] |
|
235 |
fn card_values() { |
|
236 |
let card_values = vec![1,2,5,10,33,55,77]; |
|
237 |
let card_points = vec![1,1,2,3, 5, 7, 5]; |
|
238 |
for i in 1 .. card_values.len() { |
|
239 |
let c = Card::new( card_values[i] ); |
|
240 |
let p = c.points; |
|
241 |
assert!(p == card_points[i], "card={} card points={} i={} expected point={}", card_values[i], p, i, card_points[i]); |
|
242 |
} |
|
243 |
} |
|
244 |
|
|
245 |
#[test] |
|
246 |
fn player_take_pile() { |
|
247 |
// create a player |
|
248 |
let mut p = Player::new("bob".to_string()); |
|
249 |
// create a pile |
1
|
250 |
let mut pile = VecDeque::new(); |
2
|
251 |
let mut refpile = VecDeque::new(); |
0
|
252 |
for i in 5..10 { |
|
253 |
let c = Card::new(i); |
1
|
254 |
pile.push_back(c); |
0
|
255 |
let c = Card::new(i); |
2
|
256 |
refpile.push_back(c); |
0
|
257 |
} |
2
|
258 |
// give the pile to player |
|
259 |
p.give_pile(pile); |
4
|
260 |
assert!( p.get_rows_busted() == 1 ); |
0
|
261 |
|
|
262 |
// get back the pile from player |
|
263 |
// p = p.gimme_pile(); |
2
|
264 |
let pile = p.get_pile(); |
0
|
265 |
|
|
266 |
// the pile we got shall be same as the pile we gave |
|
267 |
// this check is O(n^2), doesn't matter for less than 100 items |
2
|
268 |
assert_eq!( pile, refpile ); |
0
|
269 |
assert!( pile.iter().all( |item| refpile.contains(item)) ); |
|
270 |
|
1
|
271 |
let mut pile = VecDeque::new(); |
|
272 |
for i in 4..=9 { |
0
|
273 |
let c = Card::new(i); |
1
|
274 |
pile.push_back(c); |
0
|
275 |
} |
2
|
276 |
p.give_pile(pile); |
4
|
277 |
assert!( p.get_rows_busted() == 2 ); |
0
|
278 |
} |
|
279 |
|
|
280 |
#[test] |
|
281 |
fn row_push() { |
|
282 |
let mut row = Row::new(); |
2
|
283 |
let mut refcard = VecDeque::new(); // reference vec to check |
0
|
284 |
for i in 1..=7 { |
|
285 |
let cval = i+5; |
|
286 |
let card = Card::new(cval); |
|
287 |
// push a card into the row |
|
288 |
if let Some(cards) = row.push_or_collect(card) { |
|
289 |
// got the overflow |
|
290 |
println!("Got overflow row at {}!", i); |
|
291 |
assert!( i == 6, "Overflow at wrong position: {} != 6", i ); |
|
292 |
// we need to get the proper vec |
|
293 |
assert!( cards.iter().all( |item| refcard.contains(item) ), "Got cards {:?}", cards ); |
|
294 |
} else { |
|
295 |
println!("push success {}", i); |
|
296 |
} |
|
297 |
// remember the correct vec for checking |
|
298 |
let card = Card::new(cval); |
2
|
299 |
refcard.push_back(card); |
0
|
300 |
|
|
301 |
// check card value |
|
302 |
assert!( row.last_card_value() == cval, "Last card value mismatch: got {} vs expected {}", row.last_card_value(), cval ); |
|
303 |
} |
4
|
304 |
assert!( row.len() == 2, "Row contains wrong amount of cards: {}", row.len() ); |
0
|
305 |
} |
1
|
306 |
|
|
307 |
#[test] |
|
308 |
fn sort_cards() { |
2
|
309 |
let mut cards: VecDeque<Card> = VecDeque::new(); |
1
|
310 |
let mut rng = rand::thread_rng(); |
3
|
311 |
for _ in 1..50 { |
1
|
312 |
let n = rng.gen_range(1..104); |
2
|
313 |
cards.push_back( Card::new(n) ); |
1
|
314 |
} |
2
|
315 |
cards.make_contiguous().sort(); |
1
|
316 |
|
|
317 |
for i in 1..cards.len() { |
|
318 |
assert!( cards[i-1].value <= cards[i].value, "Bad ordering: {} > {}", cards[i-1].value,cards[i].value ); |
|
319 |
} |
|
320 |
} |
|
321 |
|
2
|
322 |
#[test] |
|
323 |
fn check_closest_row() { |
|
324 |
let table = generate_table(); |
|
325 |
let pcard = PlayerCard{ player_id: 42, card: Card::new(42) }; |
|
326 |
let closest = table.get_closest_row(&pcard); |
|
327 |
assert_eq!( closest, Some(3) ); // index from 0 |
|
328 |
} |
|
329 |
|
|
330 |
#[test] |
|
331 |
fn check_smallest_player_card() { |
|
332 |
let mut table = generate_table(); |
|
333 |
|
|
334 |
let mut player_id = 1; |
|
335 |
vec![103, 8, 71, 93, 6].into_iter().for_each(|c| { |
|
336 |
table.lay_player_card( Card::new(c), player_id); |
|
337 |
player_id += 1; |
|
338 |
}); |
|
339 |
|
|
340 |
let smallest = table.get_smallest_player_card(); |
|
341 |
assert_eq!( smallest, PlayerCard{ player_id: 5, card: Card::new(6) } ); |
|
342 |
} |
|
343 |
|
|
344 |
fn generate_table() -> Table { |
|
345 |
let mut row_cards = VecDeque::new(); |
|
346 |
vec![5,7,10,33,70].into_iter().for_each(|c| row_cards.push_back( Card::new(c) )); |
|
347 |
let table = Table::new(row_cards); |
|
348 |
table |
|
349 |
} |
0
|
350 |
} |
|
351 |
|
|
352 |
/* |
|
353 |
|
|
354 |
- 1-104 lap, |
|
355 |
- *5 - 2 |
|
356 |
- *0 - 3 |
|
357 |
- NN - 5 |
|
358 |
- 55 - 7 |
|
359 |
- deck |
|
360 |
- jatekosok; kezben tartott lapok; elvitt lapok; pontszamok; counter: elvitt sorok, okrok, total pont |
|
361 |
-- keveres (keveresek szama) |
|
362 |
-- osztas: mindenki 10 lap |
|
363 |
-- start sorok: 5 kartya |
|
364 |
- jatek (jatekok szama) |
|
365 |
-- mindenki a felso lapot kiteszi |
|
366 |
-- szamsorrendben felkerulnek, aki viszi, viszi |
|
367 |
--- ha kisebb, akkor dontes |
|
368 |
---- ha van legkevesebb, viszi |
|
369 |
---- ha tobb min van, random valaszt |
|
370 |
- osszesites |
|
371 |
-- okrok, elvitt sorok |
|
372 |
-- keveresek szama, jatekok szama, eltelt ido |
|
373 |
- deck osszegyujtes |
|
374 |
|
|
375 |
|
|
376 |
*/ |