// #[allow(dead_code, unused)]// vigyazz6! autplayer//usecore::{fmt,panic};usestd::{collections::VecDeque,time::Instant,i64::MAX};userand::Rng;usestd::mem;externcratepretty_env_logger;#[macro_use]externcratelog;constGAME_ROUNDS: i32=1_000_000;fnmain(){// RUST_LOG=debug cargo runpretty_env_logger::init();info!("Program starting.");game();info!("End of run.");}/*** Card ****/#[derive(Debug)]structCard{value: i8,points: i8,}implCard{fnnew(value: i8)->Self{letmutpoints=0;ifvalue%10==5{// ends with 5 = 2 pointpoints=2;// println!("*5 add 1, val={}, pt={}", value, points);}ifvalue%10==0{// ends with 0 = 3 pointpoints=3;// println!("*0 add 2, val={}, pt={}", value, points);}ifvalue%10==value/10{// same numbers = 5 points (55=7)points+=5;// println!("NN add 5, val={}, pt={}", value, points);}ifpoints==0{points=1;}Card{value,points,}}}implfmt::DisplayforCard{fnfmt(&self,f: &mutfmt::Formatter)-> fmt::Result{write!(f,"(Card {}, points {})",self.value,self.points)}}implPartialEqforCard{fneq(&self,other: &Self)-> bool{self.value==other.value}}implEqforCard{}implPartialOrdforCard{fnpartial_cmp(&self,other: &Self)-> Option<std::cmp::Ordering>{matchself.value.partial_cmp(&other.value){Some(core::cmp::Ordering::Equal)=>{None}ord=>returnord,}}}implOrdforCard{fncmp(&self,other: &Self)-> std::cmp::Ordering{self.value.cmp(&other.value)}}/*** Deck ****/#[derive(Debug)]structDeck{content: VecDeque<Card>,}implDeck{fnnew_empty()-> Self{debug!("Empty deck generated");Deck{content: VecDeque::new(),}}fnnew()-> Self{debug!("Full deck generated");letcontent=(1..=104).into_iter().map(|n|Card::new(n)).collect();Deck{content,}}fnshuffle(&mutself){letmutrng=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 cardsfor_iin1..=500{letc1=rng.gen_range(0..self.content.len());letc2=rng.gen_range(0..self.content.len());ifc1!=c2{self.content.swap(c1,c2);}}trace!("Deck after shuffle: len {}, {:?}",self.content.len(),self);}// get top card from deckfnpop(&mutself)-> Option<Card>{self.content.pop_front()}// put a card into the bottom of the deckfnpush(&mutself,c: Card){self.content.push_back(c);}fnpush_cards(&mutself,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());}fnlen(&self)-> usize{self.content.len()}fnget_nth(&mutself,n: usize)-> Card{ifletSome(c)=self.content.remove(n){c}else{panic!("get_nth: index {} out of bounds ({})!",n,self.content.len());}}}/*** Player ****/#[derive(Debug)]structPlayer{name: String,hand: Deck,pile: VecDeque<Card>,game_point: i32,total_point: i32,rows_busted: i32,wins: i32,}implPlayer{fnnew(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 dealerfnget_card(&mutself,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 tablefnthrow_card(&mutself)->Card{ifletSome(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 cardsfngive_pile(&mutself,cards: VecDeque<Card>){forcincards.into_iter(){self.game_point+=c.pointsasi32;self.pile.push_back(c);}self.rows_busted+=1;trace!("Player {} got busted, count {}",self.name,&self.rows_busted);}// ask the player their scorefntell_points(self)-> i32{self.game_point}fninc_wins(&mutself){self.wins+=1;}// give back cards from the pilefnget_pile(&mutself)-> VecDeque<Card>{trace!("Player {} gives back their pile",self.name);mem::take(&mutself.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 */}// 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 } */fnclose_round(&mutself){ifself.hand.len()>0{panic!("Closing round when {} has {} cards in hand",self.name,self.hand.len());}ifself.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 rowsfnpick_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 rowletmutrow_points=Vec::new();// the smallest row scoreletmutsmallest=999;// how many rows have the same smallest scoreletmutsame_point=0;// the first smallest row_idletmutsmallest_rowid=255;forrowidin0..rows.len(){// DEBUG// println!("pick_row_for_small_card: rowlen {}, rowid {}", rows.len(), rowid);row_points.push(rows[rowid].sum());ifrow_points[rowid]<smallest{// we have a new smallest rowsmallest=row_points[rowid];same_point=0;smallest_rowid=rowid;}elseifrow_points[rowid]==smallest{// we have another row with same point as smallestsame_point+=1;}}ifsame_point<1{// we have one smallest rowsmallest_rowid.try_into().unwrap()// it's tiny, will fit into u8}else{// bored, we pick the first now anywaysmallest_rowid.try_into().unwrap()}}}/*** Row ****/// a row of cards on the table (max 5)#[derive(Debug)]structRow{cards: VecDeque<Card>,}implRow{constMAX_LEN: usize=5;fnnew()-> Self{Row{cards: VecDeque::new(),}}fnpush_or_collect(&mutself,card: Card)-> Option<VecDeque<Card>>{trace!("Called push_or_collect on row {:?}",&self);ifself.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 overflowletrow_cards=mem::take(&mutself.cards);self.cards.push_back(card);ifself.cards.len()!=1{panic!("New row must have one card, not {}",self.cards.len());}Some(row_cards)}}fntake_row(&mutself)-> VecDeque<Card>{// take cards and empty the rowmem::take(&mutself.cards)}fnlast_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 pointsfnsum(&self)-> i32{letmutsum: i32=0;self.cards.iter().for_each(|card|{sum+=card.pointsasi32;});sum}}/*** PlayerCard ****/#[derive(Debug)]structPlayerCard{player_id: i32,card: Card,}implPlayerCard{fnget_player(&self)-> i32{self.player_id}}implPartialEqforPlayerCard{fneq(&self,other: &Self)-> bool{self.card==other.card}}implPartialOrdforPlayerCard{fnpartial_cmp(&self,other: &Self)-> Option<std::cmp::Ordering>{matchself.card.partial_cmp(&other.card){Some(core::cmp::Ordering::Equal)=>{None}ord=>returnord,}}}/*** Table ****/#[derive(Debug)]structTable{rows: Vec<Row>,player_cards: VecDeque<PlayerCard>,// owned by a player}implTable{fnnew(row_cards: VecDeque<Card>)-> Self{letmutrows=Vec::new();forcardinrow_cards{// create a new row then put a card into itletmutrow=Row::new();ifletSome(c)=row.push_or_collect(card){panic!("Freshly created row overflowed");}rows.push(row);}Table{rows,player_cards: VecDeque::new(),}}fnlay_player_card(&mutself,card: Card,player_id: i32){self.player_cards.push_back(PlayerCard{player_id,card});}fnsort_cards(&mutself){self.player_cards.make_contiguous().sort_by(|a,b|b.card.cmp(&a.card));}fnhas_player_cards(&self)-> bool{self.player_cards.len()>0}fnget_smallest_player_card(&mutself)-> PlayerCard{// FIXME: check!self.player_cards.pop_back().expect("out of player cards on table")}fnget_closest_row(&self,pcard: &PlayerCard)-> Option<usize>{// get the row id with last card closest smaller to players'letrow_heads=self.get_row_heads();letmutclosest_val=None;letmutdiff=127;foriin0..row_heads.len(){ifrow_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}fnput_card_into_row(&mutself,pcard: PlayerCard,row_id: usize)-> Option<VecDeque<Card>>{self.rows[row_idasusize].push_or_collect(pcard.card)}fnget_row_heads(&self)-> Vec<i8>{letmutheads: Vec<i8>=Vec::new();foriin0..self.rows.len(){heads.push(self.rows[i].last_card_value());}heads}// take a whole row and hand it overfntake_row(&mutself,row_id: usize)-> VecDeque<Card>{self.rows[row_id].take_row()}// collect remaining cards in the rows at the end of roundfncollect_rows(&mutself)-> VecDeque<Card>{letmutcards=VecDeque::new();forrowin0..self.rows.len(){self.rows[row].take_row().into_iter().for_each(|card|cards.push_back(card));}cards}}/*** Game ****/structGameStat{game_count: i64,shuffle_count: i64,start_time: Instant,// use start_time.elapsed().as_nanos() or .as_secs()}fngame(){letmutstats=GameStat{game_count:0,shuffle_count: 0,start_time: Instant::now()};letmutdeck=Deck::new();letplayer_names=vec!["grin","moni","icbalint","orsi","topi","kgb","zsu","csilla"];letmutplayers: Vec<Player>=player_names.iter().map(|n|Player::new(n.to_string())).collect();letplayer_count=players.len();// pc - 1forcnt_gamein1..=GAME_ROUNDS{debug!("Game round {} starts",cnt_game);deck.shuffle();stats.shuffle_count+=1;stats.game_count+=1;// dealingdebug!("Dealing.");foriin1..=10{forplayerin0..player_count{players[player].get_card(deck.pop().expect("Deck is empty while dealing to players"));}}// we need 5 crds from deckdebug!("Building the rows.");letmutcards=VecDeque::new();foriin1..=5{cards.push_back(deck.pop().expect("deck empty before starting the game"));}// println!("We push 5 cards to rows: {:?}\n", cards);letmuttable=Table::new(cards);// DEBUG/* println!("Table: {:?}\n", table); println!("PLayers: {:?}\n", players); println!("Deck: {:?}\n", deck); */debug!("We have the table ready: {:?}",table);// playingdebug!("Players start taking turns");forturnin1..=10{debug!("Turn {}!",turn);trace!("The table: {:?}",table);// everyone puts a card face downforplayerin0..player_count{letplayer_id: i32=player.try_into().unwrap();// get a card from the playerlettopcard=players[player].throw_card();// put it on the table ("turned face down")table.lay_player_card(topcard,player_id);}// process cardsdebug!("The Table process the throws.");table.sort_cards();whiletable.has_player_cards(){letsmallest=table.get_smallest_player_card();trace!("Take smallest card {:?}",&smallest);letclosest_row=table.get_closest_row(&smallest);trace!("Choose closest row: {:?}",closest_row);matchclosest_row{Some(rowid)=>{debug!("Putting down card into row {}",rowid);letplayer_id: usize=smallest.player_id.try_into().unwrap();letoverflow=table.put_card_into_row(smallest,rowid);ifletSome(cards)=overflow{// row is full, got piledebug!("Row is busted, {} collects",players[player_id].name);// player gets pile, card gets into row headplayers[player_id].give_pile(cards);}},None=>{// card too small, need to pick row!letplayer_id: usize=smallest.player_id.try_into().unwrap();debug!("Too small from {}, picking row",players[player_id].name);// pick any row to takeletrowid=players[player_id].pick_row_for_small_card(&table.rows,&smallest.card);trace!("Picked row {}",rowid);// take the row cardsletcards=table.take_row(rowid);trace!("Took cards: {:?}",cards);players[player_id].give_pile(cards);// put new card in the rowletoverflow=table.put_card_into_row(smallest,rowid);ifletSome(c)=overflow{panic!("Player took whole row and it's already full");}}}}}// end of roundinfo!("Round finished, len is {} ??sec",stats.start_time.elapsed().as_micros());debug!("End of round, counting and collecting back piles");letmutwinners: Vec<usize>=Vec::new();letmutwinscore: i32=i32::MAX-1;foriin0..player_count{info!("Player {} has {} points",players[i].name,players[i].game_point);ifplayers[i].game_point<winscore{trace!("New winner {} with score {}",players[i].name,players[i].game_point);winners.clear();winners.push(i);winscore=players[i].game_point;}elseifplayers[i].game_point==winscore{trace!("New co-winner {} with score {}",players[i].name,players[i].game_point);winners.push(i);}trace!("The list of winners is {:?}",winners);// get pile from playerletcards=players[i].get_pile();// and give it back to the deckdeck.push_cards(cards);// close player round and update statsplayers[i].close_round();}// collect cards from tabledeck.push_cards(table.collect_rows());trace!("Shall have full deck now, len {}",deck.content.len());letmutfinals=Vec::new();foriinwinners.iter(){players[*i].inc_wins();}foriinwinners{finals.push(&players[i].name);}info!("The winner(s): {:?}",&finals);/* players.iter().for_each(|player| { println!("Player {} has {} points", player.name, player.game_point); let cards = player.get_pile(); deck.push_cards(cards); player.close_round(); }); */}println!("Totals (game time {} ??s, or {} s), {} games played ({} shuffles):",stats.start_time.elapsed().as_micros(),stats.start_time.elapsed().as_secs(),stats.game_count,stats.shuffle_count,);players.sort_by(|a,b|a.total_point.partial_cmp(&b.total_point).unwrap());foriin0..players.len(){letp=&players[i];println!("Player {} has wins {}, score {} (busted {} times)",p.name,p.wins,p.total_point,p.rows_busted);}}#[cfg(test)]modtests{usecore::panic;usestd::collections::VecDeque;userand::Rng;usecrate::{Card,Player,Row,Table,PlayerCard};#[test]fncard_values(){letcard_values=vec![1,2,5,10,33,55,77];letcard_points=vec![1,1,2,3,5,7,5];foriin1..card_values.len(){letc=Card::new(card_values[i]);letp=c.points;assert!(p==card_points[i],"card={} card points={} i={} expected point={}",card_values[i],p,i,card_points[i]);}}#[test]fnplayer_take_pile(){// create a playerletmutp=Player::new("bob".to_string());// create a pileletmutpile=VecDeque::new();letmutrefpile=VecDeque::new();foriin5..10{letc=Card::new(i);pile.push_back(c);letc=Card::new(i);refpile.push_back(c);}// give the pile to playerp.give_pile(pile);assert!(p.rows_busted==1);// get back the pile from player// p = p.gimme_pile();letpile=p.get_pile();// the pile we got shall be same as the pile we gave// this check is O(n^2), doesn't matter for less than 100 itemsassert_eq!(pile,refpile);assert!(pile.iter().all(|item|refpile.contains(item)));letmutpile=VecDeque::new();foriin4..=9{letc=Card::new(i);pile.push_back(c);}p.give_pile(pile);assert!(p.rows_busted==2);}#[test]fnrow_push(){letmutrow=Row::new();letmutrefcard=VecDeque::new();// reference vec to checkforiin1..=7{letcval=i+5;letcard=Card::new(cval);// push a card into the rowifletSome(cards)=row.push_or_collect(card){// got the overflowprintln!("Got overflow row at {}!",i);assert!(i==6,"Overflow at wrong position: {} != 6",i);// we need to get the proper vecassert!(cards.iter().all(|item|refcard.contains(item)),"Got cards {:?}",cards);}else{println!("push success {}",i);}// remember the correct vec for checkingletcard=Card::new(cval);refcard.push_back(card);// check card valueassert!(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());}#[test]fnsort_cards(){letmutcards: VecDeque<Card>=VecDeque::new();letmutrng=rand::thread_rng();foriin1..50{letn=rng.gen_range(1..104);cards.push_back(Card::new(n));}cards.make_contiguous().sort();foriin1..cards.len(){assert!(cards[i-1].value<=cards[i].value,"Bad ordering: {} > {}",cards[i-1].value,cards[i].value);}}#[test]fncheck_closest_row(){lettable=generate_table();letpcard=PlayerCard{player_id: 42,card: Card::new(42)};letclosest=table.get_closest_row(&pcard);assert_eq!(closest,Some(3));// index from 0}#[test]fncheck_smallest_player_card(){letmuttable=generate_table();letmutplayer_id=1;vec![103,8,71,93,6].into_iter().for_each(|c|{table.lay_player_card(Card::new(c),player_id);player_id+=1;});letsmallest=table.get_smallest_player_card();assert_eq!(smallest,PlayerCard{player_id: 5,card: Card::new(6)});}fngenerate_table()-> Table{letmutrow_cards=VecDeque::new();vec![5,7,10,33,70].into_iter().for_each(|c|row_cards.push_back(Card::new(c)));lettable=Table::new(row_cards);table}}/*- 1-104 lap, - *5 - 2 - *0 - 3 - NN - 5 - 55 - 7- deck- jatekosok; kezben tartott lapok; elvitt lapok; pontszamok; counter: elvitt sorok, okrok, total pont-- keveres (keveresek szama)-- osztas: mindenki 10 lap-- start sorok: 5 kartya- jatek (jatekok szama)-- mindenki a felso lapot kiteszi-- szamsorrendben felkerulnek, aki viszi, viszi--- ha kisebb, akkor dontes---- ha van legkevesebb, viszi---- ha tobb min van, random valaszt- osszesites-- okrok, elvitt sorok-- keveresek szama, jatekok szama, eltelt ido- deck osszegyujtes*/