Comments. Lot of Comments.
--- 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<std::cmp::Ordering> {
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)
--- 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<Card>,
}
@@ -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 <n> 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
--- 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> = 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(); // = <number of players> - 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<usize>.
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<VecDeque<Card>>.
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<usize> = 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<usize> 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 / <i32 as Into<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(),
--- 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<Card> ) {
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<Card> {
trace!("Player {} gives back their pile", self.name);
+ // Here we take the ownership of the `pile` VecDeque<Card> 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<Card> = Vec::new();
+ // very cumbersome manual fiddling (also in wrong/reverted order...)
+/* let mut throw: Vec<Card> = 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<Row>, 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<std::cmp::Ordering> {
match self.card.partial_cmp(&other.card) {
--- 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<Card>,
}
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<VecDequeue<Card>>` - None or Some(cards) to be collected from Row
pub(crate) fn push_or_collect( &mut self, card: Card ) -> Option<VecDeque<Card>> {
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<Card> {
// 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()
}
--- 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<Row>,
player_cards: VecDeque<PlayerCard>, // owned by a player
}
impl Table {
+ /// Creates a new `Table`, filling Rows from `row_cards`.
pub(crate) fn new(row_cards: VecDeque<Card>) -> 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<usize> {
// 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<VecDeque<Card>> {
- 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<i8> {
+ pub(crate) fn get_row_tails( &self ) -> Vec<i8> {
let mut heads: Vec<i8> = 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<Row> {
&self.rows
}