# HG changeset patch # User Peter Gervai # Date 1675120554 -3600 # Node ID 950660fddec0e6a2358f5969f81200d899444866 # Parent 5f81068a8a883a3e8ef9f99122bb1d2b4930788c Final version :-) diff -r 5f81068a8a88 -r 950660fddec0 Cargo.toml --- 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" diff -r 5f81068a8a88 -r 950660fddec0 src/main.rs --- 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, @@ -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 ) { + 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, + pile: VecDeque, 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 ) { + fn give_pile( &mut self, cards: VecDeque ) { 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 { + fn get_pile( &mut self ) -> VecDeque { + 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, 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> { + 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 { + // 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, - player_cards: Vec, // owned by a player + player_cards: VecDeque, // owned by a player } impl Table { - fn new(row_cards: Vec) -> Self { + fn new(row_cards: VecDeque) -> 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 { + // 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> { + self.rows[row_id as usize].push_or_collect(pcard.card) } - fn get_closest_row( &self, pcard: &PlayerCard ) -> Option { - // get the row id with last card closest smaller to players' - todo!() + fn get_row_heads( &self ) -> Vec { + let mut heads: Vec = 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> { - 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 { + self.rows[row_id].take_row() + } + + // collect remaining cards in the rows at the end of round + fn collect_rows( &mut self ) -> VecDeque { + 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 = 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 = Vec::new(); + let mut cards: VecDeque = 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 + } } /*