# HG changeset patch # User Peter Gervai # Date 1675203950 -3600 # Node ID a2f0cb2b5c1360606de78da044823606da3bd8d2 # Parent 3d9dba5b16e0f6cb7e526979f9a4d1820df4cee6 Split main into module files. diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Tue Jan 31 23:25:50 2023 +0100 @@ -0,0 +1,3 @@ +syntax: glob +target +Cargo.lock diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 src/card.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/card.rs Tue Jan 31 23:25:50 2023 +0100 @@ -0,0 +1,70 @@ +use core::fmt; + +/*** Card ****/ +#[derive(Debug)] +pub struct Card { + pub value: i8, + pub points: i8, +} + +impl Card { + pub fn new(value: i8)->Self { + + let mut points = 0; + if value % 10 == 5 { + // ends with 5 = 2 point + points = 2; + // println!("*5 add 1, val={}, pt={}", value, points); + } + + if value % 10 == 0 { + // ends with 0 = 3 point + points = 3; + // println!("*0 add 2, val={}, pt={}", value, points); + } + + if value % 10 == value / 10 { + // same numbers = 5 points (55=7) + points += 5; + // println!("NN add 5, val={}, pt={}", value, points); + } + + if points == 0 { + points = 1; + } + + Card { + value, + points, + } + } +} + +impl fmt::Display for Card { + fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result { + write!(f, "(Card {}, points {})", self.value, self.points) + } +} + +impl PartialEq for Card { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} + +impl Eq for Card {} + +impl PartialOrd for Card { + fn partial_cmp(&self, other: &Self) -> Option { + match self.value.partial_cmp(&other.value) { + Some(core::cmp::Ordering::Equal) => {None} + ord => return ord, + } + } +} + +impl Ord for Card { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.value.cmp(&other.value) + } +} diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 src/deck.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/deck.rs Tue Jan 31 23:25:50 2023 +0100 @@ -0,0 +1,82 @@ +/*** Deck ****/ +use std::collections::VecDeque; + +use rand::Rng; + +use crate::card::Card; + +#[derive(Debug)] +pub(crate) struct Deck { + content: VecDeque, +} + +impl Deck { + /// Creates an empty [`Deck`] of undefined size + pub(crate) fn new_empty() -> Self { + debug!("Empty deck generated"); + Deck { + content: VecDeque::new(), + } + } + + /// Creates a new full [`Deck`] with 104 cards. + pub(crate) fn new() -> Self { + debug!("Full deck generated"); + let content = (1..=104).into_iter().map( |n| Card::new(n) ).collect(); + Deck { + content, + } + } + + /// Shuffles this [`Deck`] (using its default method) + pub(crate) 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 + // * grin: take 1..6 from front and put at bottom + + // naive shuffle: exchange random cards + for _i in 1..=500 { + let c1 = rng.gen_range(0 .. self.content.len()); + let c2 = rng.gen_range(0 .. self.content.len()); + if c1 != c2 { + self.content.swap(c1, c2); + } + } + trace!("Deck after shuffle: len {}, {:?}", self.content.len(), self); + } + + /// Returns the top card of this [`Deck`]. + pub(crate) fn pop( &mut self ) -> Option { + self.content.pop_front() + } + + /// Put a card into the bottom of the [`Deck`] + pub(crate) fn push( &mut self, c: Card ) { + self.content.push_back(c); + } + + /// Push multiple cards to the bottom of the [`Deck`] + pub(crate) 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()); + } + + /// Returns the length of this [`Deck`]. + pub(crate) fn len( &self ) -> usize { + self.content.len() + } + + fn _get_nth( &mut self, n: usize ) -> Card { + if let Some(c) = self.content.remove(n) { + c + } else { + panic!("get_nth: index {} out of bounds ({})!", n, self.content.len()); + } + } +} diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 src/main.rs --- a/src/main.rs Tue Jan 31 22:16:26 2023 +0100 +++ b/src/main.rs Tue Jan 31 23:25:50 2023 +0100 @@ -2,15 +2,23 @@ // vigyazz6! autplayer // -use core::{fmt, panic}; +use core::panic; use std::{collections::VecDeque, time::Instant, cmp::Reverse}; -use rand::Rng; -use std::mem; + +use crate::{deck::Deck, player::Player, table::Table}; +// use rand::Rng; +// use std::mem; + +mod deck; +mod table; +mod player; +mod card; +mod row; extern crate pretty_env_logger; #[macro_use] extern crate log; -const GAME_ROUNDS: i32 = 1_000_000; +const GAME_ROUNDS: i32 = 100_000; fn main() { // RUST_LOG=debug cargo run @@ -20,459 +28,11 @@ info!("End of run."); } -/*** Card ****/ -#[derive(Debug)] -struct Card { - value: i8, - points: i8, -} - -impl Card { - fn new(value: i8)->Self { - - let mut points = 0; - if value % 10 == 5 { - // ends with 5 = 2 point - points = 2; - // println!("*5 add 1, val={}, pt={}", value, points); - } - - if value % 10 == 0 { - // ends with 0 = 3 point - points = 3; - // println!("*0 add 2, val={}, pt={}", value, points); - } - - if value % 10 == value / 10 { - // same numbers = 5 points (55=7) - points += 5; - // println!("NN add 5, val={}, pt={}", value, points); - } - - if points == 0 { - points = 1; - } - - Card { - value, - points, - } - } -} - -impl fmt::Display for Card { - fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result { - write!(f, "(Card {}, points {})", self.value, self.points) - } -} - -impl PartialEq for Card { - fn eq(&self, other: &Self) -> bool { - self.value == other.value - } -} - -impl Eq for Card {} - -impl PartialOrd for Card { - fn partial_cmp(&self, other: &Self) -> Option { - match self.value.partial_cmp(&other.value) { - Some(core::cmp::Ordering::Equal) => {None} - ord => return ord, - } - } -} - -impl Ord for Card { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.value.cmp(&other.value) - } -} - -/*** Deck ****/ -#[derive(Debug)] -struct Deck { - content: VecDeque, -} - -impl Deck { - fn new_empty() -> Self { - debug!("Empty deck generated"); - Deck { - content: VecDeque::new(), - } - } - - fn new() -> Self { - debug!("Full deck generated"); - let content = (1..=104).into_iter().map( |n| Card::new(n) ).collect(); - Deck { - content, - } - } - - 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 - // * grin: take 1..6 from front and put at bottom - - // naive shuffle: exchange random cards - for _i in 1..=500 { - let c1 = rng.gen_range(0 .. self.content.len()); - let c2 = rng.gen_range(0 .. self.content.len()); - if c1 != c2 { - self.content.swap(c1, c2); - } - } - trace!("Deck after shuffle: len {}, {:?}", self.content.len(), self); - } - - // get top card from deck - fn pop( &mut self ) -> Option { - self.content.pop_front() - } - - // put a card into the bottom of the deck - fn push( &mut self, c: Card ) { - 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() - } - - fn _get_nth( &mut self, n: usize ) -> Card { - if let Some(c) = self.content.remove(n) { - c - } else { - panic!("get_nth: index {} out of bounds ({})!", n, self.content.len()); - } - } -} - -/*** Player ****/ -#[derive(Debug)] -struct Player { - name: String, - hand: Deck, - 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: 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); - } - } - - // get a busted row of cards - fn give_pile( &mut self, cards: VecDeque ) { - for c in cards.into_iter() { - self.game_point += c.points as i32; - self.pile.push_back(c); - } - self.rows_busted += 1; - trace!("Player {} got busted, count {}", self.name, &self.rows_busted); - } - - // ask the player their score - fn _tell_points( self ) -> i32 { - self.game_point - } - - fn inc_wins( &mut self ) { - self.wins += 1; - } - - // give back cards from the pile - 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() - - // very cumbersome manual fiddling (also reverted...) -/* let mut throw: Vec = Vec::new(); - for _i in 0 .. self.pile.len() { - throw.push( self.pile.pop().unwrap() ); - } - throw - */ - } +// Card +// Deck +// Player +// Row - // I can do this just because I *throw away* c! - // doesn't work if I want to use it. -/* fn _gimme_pile(self)->Self { - for c in &self.pile { - println!("Throw {} ", c); - } - self - } - */ - fn close_round( &mut self ) { - if self.hand.len() > 0 { - panic!("Closing round when {} has {} cards in hand", self.name, self.hand.len()); - } - - if self.pile.len() > 0 { - 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::with_capacity(5); - // 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 { - cards: VecDeque, -} - -impl Row { - const MAX_LEN: usize = 5; - - fn new() -> Self { - Row { - cards: VecDeque::with_capacity(5), - } - } - - 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); - if self.cards.len() != 1 { - panic!("New row must have one card, not {}", self.cards.len()); - } - Some(row_cards) - } - } - - 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()); - 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, - card: Card, -} - -impl PlayerCard { - fn _get_player(&self) -> i32 { - self.player_id - } -} - -impl PartialEq for PlayerCard { - fn eq(&self, other: &Self) -> bool { - self.card == other.card - } -} - -impl PartialOrd for PlayerCard { - fn partial_cmp(&self, other: &Self) -> Option { - match self.card.partial_cmp(&other.card) { - Some(core::cmp::Ordering::Equal) => {None} - ord => return ord, - } - } -} - -/*** Table ****/ -#[derive(Debug)] -struct Table { - rows: Vec, - player_cards: VecDeque, // owned by a player -} - -impl Table { - fn new(row_cards: VecDeque) -> Self { - 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) { - panic!("Freshly created row overflowed"); - } - rows.push( row ); - } - - Table { - rows, - player_cards: VecDeque::new(), - } - } - - fn lay_player_card( &mut self, card: Card, player_id: i32 ) { - self.player_cards.push_back( PlayerCard { player_id, card } ); - } - - fn sort_cards( &mut self ) { - 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 { - // 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_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 - } - - // 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 ****/ @@ -556,7 +116,7 @@ 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); + debug!("Row is busted, {} collects", players[player_id].get_name()); // player gets pile, card gets into row head players[ player_id ].give_pile( cards ); } @@ -564,9 +124,10 @@ 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); + 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); + // let rowid = players[ player_id ].pick_row_for_small_card(&table.rows, &smallest.card); + let rowid = players[ player_id ].pick_row_for_small_card(table.peek_rows(), &smallest.card); trace!("Picked row {}", rowid); // take the row cards let cards = table.take_row(rowid); @@ -590,16 +151,16 @@ let mut winscore: i32 = i32::MAX-1; for i in 0..player_count { - info!("Player {} has {} points", players[i].name, players[i].game_point); + info!("Player {} has {} points", players[i].get_name(), players[i].get_points()); - if players[i].game_point < winscore { - trace!("New winner {} with score {}", players[i].name, players[i].game_point); + if players[i].get_points() < winscore { + trace!("New winner {} with score {}", players[i].get_name(), players[i].get_points()); winners.clear(); winners.push(i); - winscore = players[i].game_point; + winscore = players[i].get_points(); - } else if players[i].game_point == winscore { - trace!("New co-winner {} with score {}", players[i].name, players[i].game_point); + } else if players[i].get_points() == winscore { + trace!("New co-winner {} with score {}", players[i].get_name(), players[i].get_points()); winners.push(i); } trace!("The list of winners is {:?}", winners); @@ -614,7 +175,7 @@ // collect cards from table deck.push_cards( table.collect_rows() ); - trace!("Shall have full deck now, len {}", deck.content.len()); + trace!("Shall have full deck now, len {}", deck.len()); let mut finals = Vec::new(); for i in winners.iter() { @@ -622,7 +183,7 @@ } for i in winners { - finals.push( &players[i].name ); + finals.push( players[i].get_name() ); } info!("The winner(s): {:?}", &finals); @@ -635,9 +196,10 @@ */ } + // do the type conversions _very_ visibly let elapsed_micro: f64 = stats.start_time.elapsed().as_micros() as f64; let game_rounds: f64 = GAME_ROUNDS.into(); - + // 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):", @@ -650,22 +212,24 @@ // 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.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) 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); + println!("Player {} has wins {}, score {} (busted {} times)", + p.get_name(), + p.get_wins(), + p.get_total_points(), + p.get_rows_busted()); } } #[cfg(test)] mod tests { - // use core::panic; use std::collections::VecDeque; - use rand::Rng; - use crate::{Card, Player, Row, Table, PlayerCard}; + use crate::{card::Card, player::{Player, PlayerCard}, row::Row, table::Table}; #[test] fn card_values() { @@ -693,7 +257,7 @@ } // give the pile to player p.give_pile(pile); - assert!( p.rows_busted == 1 ); + assert!( p.get_rows_busted() == 1 ); // get back the pile from player // p = p.gimme_pile(); @@ -710,7 +274,7 @@ pile.push_back(c); } p.give_pile(pile); - assert!( p.rows_busted == 2 ); + assert!( p.get_rows_busted() == 2 ); } #[test] @@ -737,7 +301,7 @@ // check card value assert!( row.last_card_value() == cval, "Last card value mismatch: got {} vs expected {}", row.last_card_value(), cval ); } - assert!( row.cards.len() == 2, "Row contains wrong amount of cards: {}", row.cards.len() ); + assert!( row.len() == 2, "Row contains wrong amount of cards: {}", row.len() ); } #[test] diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 src/player.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/player.rs Tue Jan 31 23:25:50 2023 +0100 @@ -0,0 +1,196 @@ +/*** Player ****/ + +use std::collections::VecDeque; +use std::mem; + +use crate::deck::*; +use crate::card::*; +use crate::row::*; + +#[derive(Debug)] +pub(crate) struct Player { + name: String, + hand: Deck, + pile: VecDeque, + game_point: i32, + total_point: i32, + rows_busted: i32, + wins: i32, +} + +impl Player { + pub(crate) fn new(name: String)->Self { + debug!("Player {} created", name); + Player { + name, + hand: Deck::new_empty(), + pile: VecDeque::new(), + game_point: 0, + total_point: 0, + rows_busted: 0, + wins: 0, + } + } + + // get one card from th 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 + pub(crate) 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); + } + } + + // 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; + self.pile.push_back(c); + } + self.rows_busted += 1; + trace!("Player {} got busted, count {}", self.name, &self.rows_busted); + } + + pub fn get_name(&self) -> &String { + &self.name + } + + // ask the player their score + pub(crate) fn get_points( &self ) -> i32 { + self.game_point + } + + pub fn get_wins(&self) -> i32 { + self.wins + } + + pub(crate) fn inc_wins( &mut self ) { + self.wins += 1; + } + + pub fn get_total_points(&self) -> i32 { + self.total_point + } + + pub fn get_rows_busted(&self) -> i32 { + self.rows_busted + } + + // give back cards from the pile + pub(crate) 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() + + // very cumbersome manual fiddling (also reverted...) +/* let mut throw: Vec = Vec::new(); + for _i in 0 .. self.pile.len() { + throw.push( self.pile.pop().unwrap() ); + } + throw + */ + } + + // I can do this just because I *throw away* c! + // doesn't work if I want to use it. +/* fn _gimme_pile(self)->Self { + for c in &self.pile { + println!("Throw {} ", c); + } + self + } + */ + 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()); + } + + if self.pile.len() > 0 { + 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 + 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 + 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() + } + + } + +} + +/*** PlayerCard ****/ +#[derive(Debug)] +pub(crate) struct PlayerCard { + pub player_id: i32, + pub card: Card, +} + +impl PlayerCard { + fn _get_player(&self) -> i32 { + self.player_id + } +} + +impl PartialEq for PlayerCard { + fn eq(&self, other: &Self) -> bool { + self.card == other.card + } +} + +impl PartialOrd for PlayerCard { + fn partial_cmp(&self, other: &Self) -> Option { + match self.card.partial_cmp(&other.card) { + Some(core::cmp::Ordering::Equal) => {None} + ord => return ord, + } + } +} + + diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 src/row.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/row.rs Tue Jan 31 23:25:50 2023 +0100 @@ -0,0 +1,63 @@ +/*** Row ****/ +// a row of cards on the table (max 5) + +use std::{collections::VecDeque, mem}; + +use crate::card::Card; + +#[derive(Debug)] +pub(crate) struct Row { + cards: VecDeque, +} + +impl Row { + const MAX_LEN: usize = 5; + + pub(crate) fn new() -> Self { + Row { + cards: VecDeque::with_capacity(5), + } + } + + 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 { + 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); + if self.cards.len() != 1 { + panic!("New row must have one card, not {}", self.cards.len()); + } + Some(row_cards) + } + } + + pub(crate) fn take_row( &mut self ) -> VecDeque { + // take cards and empty the row + mem::take( &mut self.cards ) + } + + 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 + } + + // sum of row card points + pub(crate) fn sum(&self) -> i32 { + let mut sum: i32 = 0; + self.cards.iter().for_each(|card| { + sum += card.points as i32; + }); + sum + } + + pub fn len(&self) -> usize { + self.cards.len() + } +} diff -r 3d9dba5b16e0 -r a2f0cb2b5c13 src/table.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/table.rs Tue Jan 31 23:25:50 2023 +0100 @@ -0,0 +1,99 @@ +/*** Table ****/ + +use std::collections::VecDeque; + +use crate::card::Card; +use crate::row::Row; +use crate::player::PlayerCard; + +#[derive(Debug)] +pub(crate) struct Table { + rows: Vec, + player_cards: VecDeque, // owned by a player +} + +impl Table { + pub(crate) fn new(row_cards: VecDeque) -> Self { + 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) { + panic!("Freshly created row overflowed"); + } + rows.push( row ); + } + + Table { + rows, + player_cards: VecDeque::new(), + } + } + + pub(crate) fn lay_player_card( &mut self, card: Card, player_id: i32 ) { + self.player_cards.push_back( PlayerCard { player_id, card } ); + } + + pub(crate) fn sort_cards( &mut self ) { + self.player_cards.make_contiguous().sort_by( |a,b| b.card.cmp(&a.card) ); + } + + pub(crate) fn has_player_cards( &self ) -> bool { + self.player_cards.len() > 0 + } + + pub(crate) fn get_smallest_player_card( &mut self ) -> PlayerCard { + // FIXME: check! + self.player_cards.pop_back().expect("out of player cards on table") + } + + 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 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 + } + + 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) + } + + pub(crate) 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 + } + + // take a whole row and hand it over + pub(crate) 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 + pub(crate) 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 + } + + pub fn peek_rows(&self) -> &Vec { + &self.rows + } +} +