# HG changeset patch # User Peter Gervai # Date 1675547173 -3600 # Node ID 0dd7f2c9fd81eacc729d0a77fb33e7004c0fe706 # Parent a2f0cb2b5c1360606de78da044823606da3bd8d2 Comments. Lot of Comments. diff -r a2f0cb2b5c13 -r 0dd7f2c9fd81 src/card.rs --- a/src/card.rs Tue Jan 31 23:25:50 2023 +0100 +++ b/src/card.rs Sat Feb 04 22:46:13 2023 +0100 @@ -1,13 +1,20 @@ use core::fmt; -/*** Card ****/ +/// This is the implementation of a Card. +/// Smart card! ;-) Can do a few things by herself. + #[derive(Debug)] +/// A playing Card. Knows its face `value` +/// and the punishment `points` (in the game +/// these are symbolised by little bull heads). pub struct Card { pub value: i8, pub points: i8, } impl Card { + /// Generate a new card with the face `value` parameter. + /// Calculates the `points` for the value. pub fn new(value: i8)->Self { let mut points = 0; @@ -24,15 +31,17 @@ } if value % 10 == value / 10 { - // same numbers = 5 points (55=7) + // same numbers = 5 points (11,22,33... but 55=7) points += 5; // println!("NN add 5, val={}, pt={}", value, points); } if points == 0 { + // rest of the cards are 1 point points = 1; } + // Return a Card Card { value, points, @@ -41,20 +50,24 @@ } impl fmt::Display for Card { + /// Print formatter for a card, so it can be written in `println!()` fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result { write!(f, "(Card {}, points {})", self.value, self.points) } } impl PartialEq for Card { + /// This is used for sorting cards. fn eq(&self, other: &Self) -> bool { self.value == other.value } } +/// This is used for sorting cards. Eq is required some by some strict sorting methods. impl Eq for Card {} impl PartialOrd for Card { + /// This is used for sorting cards. fn partial_cmp(&self, other: &Self) -> Option { match self.value.partial_cmp(&other.value) { Some(core::cmp::Ordering::Equal) => {None} @@ -63,6 +76,7 @@ } } +/// This is used for sorting cards. Ord (and Eq) is required some by some strict sorting methods. impl Ord for Card { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.value.cmp(&other.value) diff -r a2f0cb2b5c13 -r 0dd7f2c9fd81 src/deck.rs --- a/src/deck.rs Tue Jan 31 23:25:50 2023 +0100 +++ b/src/deck.rs Sat Feb 04 22:46:13 2023 +0100 @@ -1,11 +1,19 @@ -/*** Deck ****/ +/// This is the implementation of a Deck of cards. +/// There are two basic kinds: +/// 1. The Game Deck, automatically generated 104 cards. +/// 2. Any Deck-type pile, like the hand of a Player. +/// Descks can be shuffled, cards pushed to bottom and +/// pulled from top. Maybe others, too. +/// + use std::collections::VecDeque; - use rand::Rng; +/// We reference [`Card`]s use crate::card::Card; #[derive(Debug)] +/// A Deck is an ordered two-ended vector (`VecDeque`) of [`Card`]s. pub(crate) struct Deck { content: VecDeque, } @@ -39,10 +47,14 @@ // * kgb: half the deck, take 1..4 cards sequentially from each // * grin: take 1..6 from front and put at bottom - // naive shuffle: exchange random cards - for _i in 1..=500 { + // naive shuffle: exchange random cards + for _ in 1..=500 { + // Generate two random positions of cards in Deck let c1 = rng.gen_range(0 .. self.content.len()); let c2 = rng.gen_range(0 .. self.content.len()); + // If it's not the same card we swap them in the `content` vector. + // The `swap()` funtion handles ownership and does not angry + // the borrow checker. if c1 != c2 { self.content.swap(c1, c2); } @@ -72,6 +84,8 @@ self.content.len() } + /// Take a card from inside of the Deck. + /// Using an illegal index will `panic!()`, so don't. fn _get_nth( &mut self, n: usize ) -> Card { if let Some(c) = self.content.remove(n) { c diff -r a2f0cb2b5c13 -r 0dd7f2c9fd81 src/main.rs --- 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_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(); // = - 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. 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>. 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 = 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 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 / >::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(), diff -r a2f0cb2b5c13 -r 0dd7f2c9fd81 src/player.rs --- a/src/player.rs Tue Jan 31 23:25:50 2023 +0100 +++ b/src/player.rs Sat Feb 04 22:46:13 2023 +0100 @@ -1,6 +1,7 @@ -/*** Player ****/ +/// This is the implementation of a Player. use std::collections::VecDeque; +// We use this to move variables out of a Vec with ownership. use std::mem; use crate::deck::*; @@ -8,6 +9,15 @@ use crate::row::*; #[derive(Debug)] +/// A `Player` is a person in the game. +/// # Structure members +/// * `name` - the name of the Player +/// * `hand` - the cards in the hand of Player, stored as a [`Deck`], so, for example, it could be shuffled or sorted. +/// * `pile` - the pile of busted [`Row`] [`Card`]s, counted at the end of the round. +/// * `game_point` - points in the current game round +/// * `total_point` - points in all the games total +/// * `rows_busted` - just counting how many rows the player have collected +/// * `wins` - how many games have the Player won pub(crate) struct Player { name: String, hand: Deck, @@ -19,6 +29,7 @@ } impl Player { + /// Creates a new [`Player`] with a given `name`. pub(crate) fn new(name: String)->Self { debug!("Player {} created", name); Player { @@ -32,13 +43,13 @@ } } - // get one card from th dealer + /// get one card from the dealer pub(crate) 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 + /// throw a card from hand to the table pub(crate) fn throw_card( &mut self )->Card { if let Some(c) = self.hand.pop() { trace!("Player {} throws a card {:?}", self.name, &c); @@ -48,7 +59,7 @@ } } - // get a busted row of cards + /// get a busted row of cards pub(crate) fn give_pile( &mut self, cards: VecDeque ) { for c in cards.into_iter() { self.game_point += c.points as i32; @@ -58,40 +69,49 @@ trace!("Player {} got busted, count {}", self.name, &self.rows_busted); } + /// Get the name of the Player pub fn get_name(&self) -> &String { &self.name } - // ask the player their score + /// ask the player their score pub(crate) fn get_points( &self ) -> i32 { self.game_point } + /// Get the number of games won pub fn get_wins(&self) -> i32 { self.wins } + /// Player won this game, increment counter pub(crate) fn inc_wins( &mut self ) { self.wins += 1; } + /// Get total points of all games pub fn get_total_points(&self) -> i32 { self.total_point } + /// Get the number of total busted rows pub fn get_rows_busted(&self) -> i32 { self.rows_busted } - // give back cards from the pile + /// give back cards from the player's pile ino the Deck pub(crate) fn get_pile( &mut self ) -> VecDeque { trace!("Player {} gives back their pile", self.name); + // Here we take the ownership of the `pile` VecDeque structure, + // and replace it with "the default" (an empty VecDeque); we can + // return it now without taking a mutable borrow of `self`. mem::take( &mut self.pile ) - // same effect: + + // same effect (`drain()` also transfers ownership): // self.pile.drain(..).collect() - // very cumbersome manual fiddling (also reverted...) -/* let mut throw: Vec = Vec::new(); + // very cumbersome manual fiddling (also in wrong/reverted order...) +/* let mut throw: Vec = Vec::new(); for _i in 0 .. self.pile.len() { throw.push( self.pile.pop().unwrap() ); } @@ -100,7 +120,7 @@ } // I can do this just because I *throw away* c! - // doesn't work if I want to use it. + // Doesn't work if I want to use/return it. /* fn _gimme_pile(self)->Self { for c in &self.pile { println!("Throw {} ", c); @@ -108,6 +128,8 @@ self } */ + /// End of round, "close" the Player and verify that we did all right. + /// These ought to be `assert()` calls, but... same thing. pub(crate) fn close_round( &mut self ) { if self.hand.len() > 0 { panic!("Closing round when {} has {} cards in hand", self.name, self.hand.len()); @@ -122,22 +144,27 @@ self.game_point = 0; } - // card too small: pick a row to collect from the rows + /// Card too small: pick a row to collect from the rows. + /// It selects the first row with the lowest `points`. + /// + /// # Arguments + /// * `rows` - A `Vec`tor of [`Row`]s to pick from + /// * `playercard` - The actual card the player just thrown, which could be used in decision (but it isn't now) pub(crate) 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::with_capacity(5); - // the smallest row score + // the smallest row score (we start by something larger than anything possible) let mut smallest = 999; // how many rows have the same smallest score let mut same_point = 0; - // the first smallest row_id + // the first smallest row_id (start by larger than max) let mut smallest_rowid = 255; for rowid in 0 .. rows.len() { - // DEBUG - // println!("pick_row_for_small_card: rowlen {}, rowid {}", rows.len(), rowid); + // debug!("pick_row_for_small_card: rowlen {}, rowid {}", rows.len(), rowid); + // As Row to calculate the summary points for itself and store it in a Vec row_points.push( rows[rowid].sum() ); if row_points[rowid] < smallest { @@ -154,10 +181,11 @@ if same_point < 1 { // we have one smallest row - smallest_rowid.try_into().unwrap() // it's tiny, will fit into u8 + smallest_rowid.try_into().unwrap() // it's tiny, will fit into u8, so unwrap() is safe } else { - // bored, we pick the first now anyway + // bored, we pick the first now anyway; this could be smarter, but possibly + // it would require peeking at other rows and the thrown cards of others. smallest_rowid.try_into().unwrap() } @@ -165,7 +193,7 @@ } -/*** PlayerCard ****/ +/// `PlayerCard` is a [`Card`] associated with a Player. #[derive(Debug)] pub(crate) struct PlayerCard { pub player_id: i32, @@ -173,17 +201,20 @@ } impl PlayerCard { + /// It isn't used because I was tired fighting with the borrow checker ;-) fn _get_player(&self) -> i32 { self.player_id } } +/// We use this to sort the [`Card`]s, using its own knowledge about equality. impl PartialEq for PlayerCard { fn eq(&self, other: &Self) -> bool { self.card == other.card } } +/// We use this to sort the [`Card`]s, using its own knowledge about ordering. impl PartialOrd for PlayerCard { fn partial_cmp(&self, other: &Self) -> Option { match self.card.partial_cmp(&other.card) { diff -r a2f0cb2b5c13 -r 0dd7f2c9fd81 src/row.rs --- a/src/row.rs Tue Jan 31 23:25:50 2023 +0100 +++ b/src/row.rs Sat Feb 04 22:46:13 2023 +0100 @@ -1,24 +1,37 @@ -/*** Row ****/ -// a row of cards on the table (max 5) +/// Implementation of a Row of Cards on the Table (max 5) +// We use std::mem::take to do tricks with vector members and ownership. use std::{collections::VecDeque, mem}; use crate::card::Card; #[derive(Debug)] +/// A `Row` of [`Card`]s, in a two-ended vector (`VecDeque`). +/// A Row is smart and can do a few things on herself. pub(crate) struct Row { cards: VecDeque, } impl Row { + /// The maximum length of a Row. More cards cause overflow and force collection. const MAX_LEN: usize = 5; + /// Create a new [`Row`] with capacity of 5 [`Card`]s. pub(crate) fn new() -> Self { Row { cards: VecDeque::with_capacity(5), } } + /// Push a [`Card`] to the end of the Row. + /// If it would make Row over maximum length the function + /// returns the content of the row, and then push the new + /// Card as the new Row head. + /// Otherwise return None. + /// # Arguments + /// - [`Card`] - the new card to put into the Row + /// # Returns + /// - `Option>` - None or Some(cards) to be collected from Row pub(crate) fn push_or_collect( &mut self, card: Card ) -> Option> { trace!("Called push_or_collect on row {:?}", &self); if self.cards.len() < Self::MAX_LEN { @@ -28,21 +41,28 @@ } else { trace!("Row is full, len {}, maxlen {}", self.cards.len(), Self::MAX_LEN); - // row overflow + // Row overflow. We take out `cards` from `self` with its ownership and + // leave the default (empty VecDeque) in its place, without disturbing + // `self` ownership. let row_cards = mem::take( &mut self.cards ); + // We put new card as new Row head. self.cards.push_back(card); if self.cards.len() != 1 { panic!("New row must have one card, not {}", self.cards.len()); } + // Return the collected old Row content Some(row_cards) } } + /// Take out all `cards` (and their ownership) and return them. pub(crate) fn take_row( &mut self ) -> VecDeque { // take cards and empty the row mem::take( &mut self.cards ) } + /// Return the `value` of the last card in the Row. + /// This is the largest value since the Row is always ordered. pub(crate) fn last_card_value(&self) -> i8 { // println!("last_card_value: cards {:?}, len {}", self.cards, self.cards.len()); self.cards.get( self.cards.len()-1 ).unwrap().value @@ -57,6 +77,10 @@ sum } + #[allow(dead_code)] + /// We could use this, but... borrow checker, you know. Needs more love. + /// I could have used `_len` to shut up the dead_code warning, so this is + /// another example to do it the other way. pub fn len(&self) -> usize { self.cards.len() } diff -r a2f0cb2b5c13 -r 0dd7f2c9fd81 src/table.rs --- a/src/table.rs Tue Jan 31 23:25:50 2023 +0100 +++ b/src/table.rs Sat Feb 04 22:46:13 2023 +0100 @@ -1,4 +1,4 @@ -/*** Table ****/ +/// Implemantation of the playing Table. use std::collections::VecDeque; @@ -7,67 +7,94 @@ use crate::player::PlayerCard; #[derive(Debug)] +/// A playing Table, containing 5 [`Row`]s and some [`PlayerCard`]s. +/// A Table can do some useful function with its cards, like picking +/// closest row or moving cards. pub(crate) struct Table { rows: Vec, player_cards: VecDeque, // owned by a player } impl Table { + /// Creates a new `Table`, filling Rows from `row_cards`. pub(crate) fn new(row_cards: VecDeque) -> Self { + // We have exactly 5 Rows. let mut rows = Vec::with_capacity(5); for card in row_cards { // create a new row then put a card into it let mut row = Row::new(); - if let Some(_c) = row.push_or_collect(card) { + if let Some(_) = row.push_or_collect(card) { + // Row should have one card, so... mustn't happen. panic!("Freshly created row overflowed"); } + // Put the new row onto the Table. rows.push( row ); } + // And return the newly created Table. Table { rows, player_cards: VecDeque::new(), } } + /// Gets a [`Card`] from a [`Player`] and put it into the `player_cards` area, + /// remembering whose (`player_id`) card it was. pub(crate) fn lay_player_card( &mut self, card: Card, player_id: i32 ) { self.player_cards.push_back( PlayerCard { player_id, card } ); } + /// Sort the [`Card`]s thrown by the [`Player`]s, since we'd need + /// to get them ordered by value later. pub(crate) fn sort_cards( &mut self ) { + // Sorting a normal VecDeque is not possible since it may contain + // holes, so we need to de-hole it first, then it's sortable (through + // a returned pointer slice). self.player_cards.make_contiguous().sort_by( |a,b| b.card.cmp(&a.card) ); } + /// Returns true if we have unprocessed Player cards on the table. pub(crate) fn has_player_cards( &self ) -> bool { self.player_cards.len() > 0 } + /// Return the smallest player card on the table. + /// FIXME: shall check whether it's ordered. pub(crate) fn get_smallest_player_card( &mut self ) -> PlayerCard { - // FIXME: check! + // FIXME: check orderedness! + // FIXME: check asking when empty! self.player_cards.pop_back().expect("out of player cards on table") } + /// Return the row which is closest to the `pcard` arg, or None if + /// all Row tails are larger. pub(crate) 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 row_tails = self.get_row_tails(); 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); + let mut diff = 127; // larger than any + // Check all the row tail cards + for i in 0..row_tails.len() { + if row_tails[i] < pcard.card.value && pcard.card.value - row_tails[i] < diff { + // it is smaller than pcard and closer than the old closest one: match! + // Store the row index + closest_val = Some(i); + diff = pcard.card.value - row_tails[i]; + // debug!("DEBUG: pcard {}, row {}, head {}, diff {}, closest {:?}", pcard.card.value, i, row_heads[i], diff, closest_val); } } closest_val } + /// Put a [`Card`] into the `row_id` Row (tail). + /// Returns `None` if ok or `Some(cards)` when the Row is full. pub(crate) fn put_card_into_row( &mut self, pcard: PlayerCard, row_id: usize ) -> Option> { - self.rows[row_id as usize].push_or_collect(pcard.card) + // We actually ask the Row to do it properly. + self.rows[row_id].push_or_collect(pcard.card) } - pub(crate) fn get_row_heads( &self ) -> Vec { + pub(crate) fn get_row_tails( &self ) -> Vec { let mut heads: Vec = Vec::new(); for i in 0..self.rows.len() { heads.push( self.rows[i].last_card_value() ); @@ -92,6 +119,7 @@ cards } + /// Return a non-mutable borrow of the rows to look at pub fn peek_rows(&self) -> &Vec { &self.rows }