src/main.rs
changeset 4 a2f0cb2b5c13
parent 3 3d9dba5b16e0
child 5 0dd7f2c9fd81
--- 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<std::cmp::Ordering> {
-        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<Card>,
-}
-
-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<Card> {
-        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<Card> ) {
-        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<Card>,
-    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<Card> ) {
-        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<Card> {
-        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<Card> = 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<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
-        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<Card>,
-}
-
-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<VecDeque<Card>> {
-        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<Card> {
-        // 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<std::cmp::Ordering> {
-        match self.card.partial_cmp(&other.card) {
-            Some(core::cmp::Ordering::Equal) => {None}
-            ord => return ord,
-        }
-    }
-}
-
-/*** Table ****/
-#[derive(Debug)]
-struct Table {
-    rows: Vec<Row>,
-    player_cards: VecDeque<PlayerCard>, // owned by a player
-}
-
-impl Table {
-    fn new(row_cards: VecDeque<Card>) -> 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<usize> {
-        // 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<VecDeque<Card>> {
-        self.rows[row_id as usize].push_or_collect(pcard.card)
-    }
-
-    fn get_row_heads( &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() );
-        }
-        heads
-    }
-
-    // take a whole row and hand it over
-    fn take_row( &mut self, row_id: usize ) -> VecDeque<Card> {
-        self.rows[row_id].take_row()
-    }
-
-    // collect remaining cards in the rows at the end of round
-    fn collect_rows( &mut self ) -> VecDeque<Card> {
-        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 / <i32 as Into<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]