Compile on rust 1.61, resolve warnings, fix some lints (#12)

* Fix getopts version

The pinned version does not compile anymore because of mutable aliasing:
* https://github.com/rust-lang/getopts/pull/61
* https://github.com/rust-lang/getopts/issues/110

This was achieved by temporarily setting the getopts version to "0.2.21"
in Cargo.toml, and running `cargo check`.

Note that this also converts the Cargo.lock to a new format.

* Fix warning: Instead of deprecated macro try!, use question mark operator

* Fix warning: Avoid anonymous parameters

* Fix warning: Use dyn on trait objects

* Fix warning: Avoid unneeded mutability

* Fix warning: Avoid redundant format in panic or assert

* Fix lint: Avoid redundant field names in initializers

* Fix lint: Avoid redundant clone

* Fix lint: Avoid literal cast

* Fix lint: Collapse if/else where applicable

I left some if/else branches in place, if there was a certain symmetry between the branches.

* Fix lint: Avoid needless borrow

I left some if/else branches in place, if there was a certain symmetry between the branches.

* Fix lint: Use cloned instead of custom closure

* Fix lint: Avoid unneeded trait bound

* Fix lint: Avoid unneeded trait bound (2) avoid redundant clone

* Fix lint: Use &[T] instead of &Vec<T>

* Fix lint: Avoid & on each pattern

* Fix lint: Avoid manual assign

* Fix lint: Use implicit return

* Fix lint: Merge if/else branches with same value

I left one complicated branch in place.

* Fix lint: Use is_empty instead of comparing len against 0

* Fix lint: Use sum instead of fold

* Fix lint: Avoid clone on Copy types
This commit is contained in:
phimuemue 2022-07-05 20:05:13 +02:00 committed by GitHub
parent cf65b0e158
commit c9b21fa047
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 198 additions and 204 deletions

45
Cargo.lock generated
View file

@ -1,61 +1,72 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "crossbeam" name = "crossbeam"
version = "0.2.8" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348228ce9f93d20ffc30c18e575f82fa41b9c8bf064806c65d41eba4771595a0"
[[package]] [[package]]
name = "float-ord" name = "float-ord"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
[[package]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.14" version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.7" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4870ef6725dde13394134e587e4ab4eca13cb92e916209a31c851b49131d3c75"
[[package]] [[package]]
name = "log" name = "log"
version = "0.3.5" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038b5d13189a14e5b6ac384fdb7c691a45ef0885f6d2dddbf422e6c3506b8234"
dependencies = [ dependencies = [
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
] ]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.3.14" version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
dependencies = [ dependencies = [
"libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc",
] ]
[[package]] [[package]]
name = "rust_hanabi" name = "rust_hanabi"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam",
"float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "float-ord",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "fnv",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "getopts",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand",
] ]
[metadata] [[package]]
"checksum crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "348228ce9f93d20ffc30c18e575f82fa41b9c8bf064806c65d41eba4771595a0" name = "unicode-width"
"checksum float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" version = "0.1.9"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
"checksum libc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4870ef6725dde13394134e587e4ab4eca13cb92e916209a31c851b49131d3c75"
"checksum log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "038b5d13189a14e5b6ac384fdb7c691a45ef0885f6d2dddbf422e6c3506b8234"
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"

View file

@ -19,7 +19,7 @@ pub fn get_count_for_value(value: Value) -> u32 {
1 => 3, 1 => 3,
2 | 3 | 4 => 2, 2 | 3 | 4 => 2,
5 => 1, 5 => 1,
_ => { panic!(format!("Unexpected value: {}", value)); } _ => { panic!("Unexpected value: {}", value); }
} }
} }
@ -30,7 +30,7 @@ pub struct Card {
} }
impl Card { impl Card {
pub fn new(color: Color, value: Value) -> Card { pub fn new(color: Color, value: Value) -> Card {
Card { color: color, value: value } Card { color, value }
} }
} }
impl fmt::Display for Card { impl fmt::Display for Card {
@ -57,7 +57,7 @@ impl CardCounts {
} }
} }
CardCounts { CardCounts {
counts: counts, counts,
} }
} }
@ -78,20 +78,20 @@ impl CardCounts {
impl fmt::Display for CardCounts { impl fmt::Display for CardCounts {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &color in COLORS.iter() { for &color in COLORS.iter() {
try!(f.write_str(&format!( f.write_str(&format!(
"{}: ", color, "{}: ", color,
))); ))?;
for &value in VALUES.iter() { for &value in VALUES.iter() {
let count = self.get_count(&Card::new(color, value)); let count = self.get_count(&Card::new(color, value));
let total = get_count_for_value(value); let total = get_count_for_value(value);
try!(f.write_str(&format!( f.write_str(&format!(
"{}/{} {}s", count, total, value "{}/{} {}s", count, total, value
))); ))?;
if value != FINAL_VALUE { if value != FINAL_VALUE {
try!(f.write_str(", ")); f.write_str(", ")?;
} }
} }
try!(f.write_str("\n")); f.write_str("\n")?;
} }
Ok(()) Ok(())
} }
@ -127,9 +127,9 @@ impl Discard {
} }
impl fmt::Display for Discard { impl fmt::Display for Discard {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// try!(f.write_str(&format!( // f.write_str(&format!(
// "{}", self.cards, // "{}", self.cards,
// ))); // ))?;
write!(f, "{}", self.counts) write!(f, "{}", self.counts)
} }
} }
@ -145,7 +145,7 @@ pub struct Firework {
impl Firework { impl Firework {
pub fn new(color: Color) -> Firework { pub fn new(color: Color) -> Firework {
Firework { Firework {
color: color, color,
top: 0, top: 0,
} }
} }
@ -192,8 +192,8 @@ pub enum Hinted {
impl fmt::Display for Hinted { impl fmt::Display for Hinted {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
&Hinted::Color(color) => { write!(f, "{}", color) } Hinted::Color(color) => { write!(f, "{}", color) }
&Hinted::Value(value) => { write!(f, "{}", value) } Hinted::Value(value) => { write!(f, "{}", value) }
} }
} }
} }
@ -274,9 +274,9 @@ impl BoardState {
}).collect::<FnvHashMap<_, _>>(); }).collect::<FnvHashMap<_, _>>();
BoardState { BoardState {
deck_size: deck_size, deck_size,
total_cards: deck_size, total_cards: deck_size,
fireworks: fireworks, fireworks,
discard: Discard::new(), discard: Discard::new(),
num_players: opts.num_players, num_players: opts.num_players,
hand_size: opts.hand_size, hand_size: opts.hand_size,
@ -332,7 +332,7 @@ impl BoardState {
return value - 1; return value - 1;
} }
} }
return FINAL_VALUE; FINAL_VALUE
} }
// is never going to play, based on discard + fireworks // is never going to play, based on discard + fireworks
@ -357,24 +357,20 @@ impl BoardState {
true true
} else { } else {
let needed = firework.needed_value().unwrap(); let needed = firework.needed_value().unwrap();
if card.value < needed { if card.value < needed || card.value > self.highest_attainable(card.color) {
true true
} else { } else {
if card.value > self.highest_attainable(card.color) { self.discard.remaining(card) != 1
true
} else {
self.discard.remaining(&card) != 1
}
} }
} }
} }
pub fn get_players(&self) -> Range<Player> { pub fn get_players(&self) -> Range<Player> {
(0..self.num_players) 0..self.num_players
} }
pub fn score(&self) -> Score { pub fn score(&self) -> Score {
self.fireworks.iter().map(|(_, firework)| firework.score()).fold(0, |a, b| a + b) self.fireworks.iter().map(|(_, firework)| firework.score()).sum()
} }
pub fn discard_size(&self) -> u32 { pub fn discard_size(&self) -> u32 {
@ -395,35 +391,35 @@ impl BoardState {
impl fmt::Display for BoardState { impl fmt::Display for BoardState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_over() { if self.is_over() {
try!(f.write_str(&format!( f.write_str(&format!(
"Turn {} (GAME ENDED):\n", self.turn "Turn {} (GAME ENDED):\n", self.turn
))); ))?;
} else { } else {
try!(f.write_str(&format!( f.write_str(&format!(
"Turn {} (Player {}'s turn):\n", self.turn, self.player "Turn {} (Player {}'s turn):\n", self.turn, self.player
))); ))?;
} }
try!(f.write_str(&format!( f.write_str(&format!(
"{} cards remaining in deck\n", self.deck_size "{} cards remaining in deck\n", self.deck_size
))); ))?;
if self.deck_size == 0 { if self.deck_size == 0 {
try!(f.write_str(&format!( f.write_str(&format!(
"Deck is empty. {} turns remaining in game\n", self.deckless_turns_remaining "Deck is empty. {} turns remaining in game\n", self.deckless_turns_remaining
))); ))?;
} }
try!(f.write_str(&format!( f.write_str(&format!(
"{}/{} hints remaining\n", self.hints_remaining, self.hints_total "{}/{} hints remaining\n", self.hints_remaining, self.hints_total
))); ))?;
try!(f.write_str(&format!( f.write_str(&format!(
"{}/{} lives remaining\n", self.lives_remaining, self.lives_total "{}/{} lives remaining\n", self.lives_remaining, self.lives_total
))); ))?;
try!(f.write_str("Fireworks:\n")); f.write_str("Fireworks:\n")?;
for &color in COLORS.iter() { for &color in COLORS.iter() {
try!(f.write_str(&format!(" {}\n", self.get_firework(color)))); f.write_str(&format!(" {}\n", self.get_firework(color)))?;
} }
try!(f.write_str("Discard:\n")); f.write_str("Discard:\n")?;
try!(f.write_str(&format!("{}\n", self.discard))); f.write_str(&format!("{}\n", self.discard))?;
Ok(()) Ok(())
} }
@ -432,7 +428,7 @@ impl fmt::Display for BoardState {
// complete game view of a given player // complete game view of a given player
pub trait GameView { pub trait GameView {
fn me(&self) -> Player; fn me(&self) -> Player;
fn get_hand(&self, &Player) -> &Cards; fn get_hand(&self, player: &Player) -> &Cards;
fn get_board(&self) -> &BoardState; fn get_board(&self) -> &BoardState;
fn my_hand_size(&self) -> usize; fn my_hand_size(&self) -> usize;
@ -459,13 +455,13 @@ pub trait GameView {
fn can_see(&self, card: &Card) -> bool { fn can_see(&self, card: &Card) -> bool {
self.get_other_players().iter().any(|player| { self.get_other_players().iter().any(|player| {
self.has_card(&player, card) self.has_card(player, card)
}) })
} }
fn someone_else_can_play(&self) -> bool { fn someone_else_can_play(&self) -> bool {
self.get_other_players().iter().any(|player| { self.get_other_players().iter().any(|player| {
self.get_hand(&player).iter().any(|card| { self.get_hand(player).iter().any(|card| {
self.get_board().is_playable(card) self.get_board().is_playable(card)
}) })
}) })
@ -518,9 +514,9 @@ impl OwnedGameView {
}).collect::<FnvHashMap<_, _>>(); }).collect::<FnvHashMap<_, _>>();
OwnedGameView { OwnedGameView {
player: borrowed_view.player.clone(), player: borrowed_view.player,
hand_size: borrowed_view.hand_size, hand_size: borrowed_view.hand_size,
other_hands: other_hands, other_hands,
board: (*borrowed_view.board).clone(), board: (*borrowed_view.board).clone(),
} }
} }
@ -550,22 +546,22 @@ pub struct GameState {
} }
impl fmt::Display for GameState { impl fmt::Display for GameState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write_str("\n")); f.write_str("\n")?;
try!(f.write_str("======\n")); f.write_str("======\n")?;
try!(f.write_str("Hands:\n")); f.write_str("Hands:\n")?;
try!(f.write_str("======\n")); f.write_str("======\n")?;
for player in self.board.get_players() { for player in self.board.get_players() {
let hand = &self.hands.get(&player).unwrap(); let hand = &self.hands.get(&player).unwrap();
try!(f.write_str(&format!("player {}:", player))); f.write_str(&format!("player {}:", player))?;
for card in hand.iter() { for card in hand.iter() {
try!(f.write_str(&format!(" {}", card))); f.write_str(&format!(" {}", card))?;
} }
try!(f.write_str(&"\n")); f.write_str("\n")?;
} }
try!(f.write_str("======\n")); f.write_str("======\n")?;
try!(f.write_str("Board:\n")); f.write_str("Board:\n")?;
try!(f.write_str("======\n")); f.write_str("======\n")?;
try!(f.write_str(&format!("{}", self.board))); f.write_str(&format!("{}", self.board))?;
Ok(()) Ok(())
} }
} }
@ -585,9 +581,9 @@ impl GameState {
}).collect::<FnvHashMap<_, _>>(); }).collect::<FnvHashMap<_, _>>();
GameState { GameState {
hands: hands, hands,
board: board, board,
deck: deck, deck,
} }
} }
@ -612,9 +608,9 @@ impl GameState {
} }
} }
BorrowedGameView { BorrowedGameView {
player: player, player,
hand_size: self.hands.get(&player).unwrap().len(), hand_size: self.hands.get(&player).unwrap().len(),
other_hands: other_hands, other_hands,
board: &self.board, board: &self.board,
} }
} }
@ -646,7 +642,7 @@ impl GameState {
debug!("Hint to player {}, about {}", hint.player, hint.hinted); debug!("Hint to player {}, about {}", hint.player, hint.hinted);
assert!(self.board.player != hint.player, assert!(self.board.player != hint.player,
format!("Player {} gave a hint to himself", hint.player)); "Player {} gave a hint to himself", hint.player);
let hand = self.hands.get(&hint.player).unwrap(); let hand = self.hands.get(&hint.player).unwrap();
let results = match hint.hinted { let results = match hint.hinted {
@ -703,9 +699,9 @@ impl GameState {
} }
}; };
let turn_record = TurnRecord { let turn_record = TurnRecord {
player: self.board.player.clone(), player: self.board.player,
result: turn_result, result: turn_result,
choice: choice, choice,
}; };
self.board.turn_history.push(turn_record.clone()); self.board.turn_history.push(turn_record.clone());

View file

@ -33,7 +33,7 @@ pub trait CardInfo {
// get probability weight for the card // get probability weight for the card
#[allow(unused_variables)] #[allow(unused_variables)]
fn get_weight(&self, card: &Card) -> f32 { fn get_weight(&self, card: &Card) -> f32 {
1 as f32 1.
} }
fn get_weighted_possibilities(&self) -> Vec<(Card, f32)> { fn get_weighted_possibilities(&self) -> Vec<(Card, f32)> {
@ -46,11 +46,11 @@ pub trait CardInfo {
fn total_weight(&self) -> f32 { fn total_weight(&self) -> f32 {
self.get_possibilities().iter() self.get_possibilities().iter()
.map(|card| self.get_weight(&card)) .map(|card| self.get_weight(card))
.fold(0.0, |a, b| a+b) .fold(0.0, |a, b| a+b)
} }
fn weighted_score<T>(&self, score_fn: &Fn(&Card) -> T) -> f32 fn weighted_score<T>(&self, score_fn: &dyn Fn(&Card) -> T) -> f32
where f32: From<T> where f32: From<T>
{ {
let mut total_score = 0.; let mut total_score = 0.;
@ -68,7 +68,7 @@ pub trait CardInfo {
self.weighted_score(&|card| card.value as f32 ) self.weighted_score(&|card| card.value as f32 )
} }
fn probability_of_predicate(&self, predicate: &Fn(&Card) -> bool) -> f32 { fn probability_of_predicate(&self, predicate: &dyn Fn(&Card) -> bool) -> f32 {
let f = |card: &Card| { let f = |card: &Card| {
if predicate(card) { 1.0 } else { 0.0 } if predicate(card) { 1.0 } else { 0.0 }
}; };
@ -126,7 +126,7 @@ pub trait CardInfo {
// Represents hinted information about possible values of type T // Represents hinted information about possible values of type T
pub trait Info<T> where T: Hash + Eq + Clone + Copy { pub trait Info<T> where T: Hash + Eq + Clone {
// get all a-priori possibilities // get all a-priori possibilities
fn get_all_possibilities() -> Vec<T>; fn get_all_possibilities() -> Vec<T>;
@ -137,7 +137,7 @@ pub trait Info<T> where T: Hash + Eq + Clone + Copy {
// get what is now possible // get what is now possible
fn get_possibilities(&self) -> Vec<T> { fn get_possibilities(&self) -> Vec<T> {
self.get_possibility_set().iter().map(|t| t.clone()).collect::<Vec<T>>() self.get_possibility_set().iter().cloned().collect::<Vec<T>>()
} }
fn is_possible(&self, value: T) -> bool { fn is_possible(&self, value: T) -> bool {
@ -146,13 +146,13 @@ pub trait Info<T> where T: Hash + Eq + Clone + Copy {
fn initialize() -> HashSet<T> { fn initialize() -> HashSet<T> {
Self::get_all_possibilities().iter() Self::get_all_possibilities().iter()
.map(|val| val.clone()).collect::<HashSet<_>>() .cloned().collect::<HashSet<_>>()
} }
fn mark_true(&mut self, value: T) { fn mark_true(&mut self, value: T) {
let possible = self.get_mut_possibility_set(); let possible = self.get_mut_possibility_set();
possible.clear(); possible.clear();
possible.insert(value.clone()); possible.insert(value);
} }
fn mark_false(&mut self, value: T) { fn mark_false(&mut self, value: T) {
@ -328,7 +328,7 @@ impl <'a> From<&'a CardCounts> for CardPossibilityTable {
} }
} }
CardPossibilityTable { CardPossibilityTable {
possible: possible, possible,
} }
} }
} }
@ -341,7 +341,7 @@ impl CardInfo for CardPossibilityTable {
self.possible.contains_key(card) self.possible.contains_key(card)
} }
fn get_possibilities(&self) -> Vec<Card> { fn get_possibilities(&self) -> Vec<Card> {
let mut cards = self.possible.keys().map(|card| {card.clone() }).collect::<Vec<_>>(); let mut cards = self.possible.keys().cloned().collect::<Vec<_>>();
cards.sort(); cards.sort();
cards cards
} }
@ -363,7 +363,7 @@ impl CardInfo for CardPossibilityTable {
impl fmt::Display for CardPossibilityTable { impl fmt::Display for CardPossibilityTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (card, weight) in &self.possible { for (card, weight) in &self.possible {
try!(f.write_str(&format!("{} {}, ", weight, card))); f.write_str(&format!("{} {}, ", weight, card))?;
} }
Ok(()) Ok(())
} }
@ -377,21 +377,21 @@ impl <T> HandInfo<T> where T: CardInfo {
pub fn new(hand_size: u32) -> Self { pub fn new(hand_size: u32) -> Self {
let hand_info = (0..hand_size).map(|_| T::new()).collect::<Vec<_>>(); let hand_info = (0..hand_size).map(|_| T::new()).collect::<Vec<_>>();
HandInfo { HandInfo {
hand_info: hand_info, hand_info,
} }
} }
// update for hint to me // update for hint to me
pub fn update_for_hint(&mut self, hinted: &Hinted, matches: &Vec<bool>) { pub fn update_for_hint(&mut self, hinted: &Hinted, matches: &[bool]) {
match hinted { match hinted {
&Hinted::Color(color) => { Hinted::Color(color) => {
for (card_info, &matched) in self.hand_info.iter_mut().zip(matches.iter()) { for (card_info, &matched) in self.hand_info.iter_mut().zip(matches.iter()) {
card_info.mark_color(color, matched); card_info.mark_color(*color, matched);
} }
} }
&Hinted::Value(value) => { Hinted::Value(value) => {
for (card_info, &matched) in self.hand_info.iter_mut().zip(matches.iter()) { for (card_info, &matched) in self.hand_info.iter_mut().zip(matches.iter()) {
card_info.mark_value(value, matched); card_info.mark_value(*value, matched);
} }
} }
} }

View file

@ -75,7 +75,7 @@ fn main() {
Ok(m) => { m } Ok(m) => { m }
Err(f) => { Err(f) => {
print_usage(&program, opts); print_usage(&program, opts);
panic!(f.to_string()) panic!("{}", f)
} }
}; };
if matches.opt_present("h") { if matches.opt_present("h") {
@ -131,27 +131,27 @@ fn sim_games(n_players: u32, strategy_str: &str, seed: Option<u32>, n_trials: u3
let game_opts = game::GameOptions { let game_opts = game::GameOptions {
num_players: n_players, num_players: n_players,
hand_size: hand_size, hand_size,
num_hints: 8, num_hints: 8,
num_lives: 3, num_lives: 3,
// hanabi rules are a bit ambiguous about whether you can give hints that match 0 cards // hanabi rules are a bit ambiguous about whether you can give hints that match 0 cards
allow_empty_hints: false, allow_empty_hints: false,
}; };
let strategy_config : Box<strategy::GameStrategyConfig + Sync> = match strategy_str { let strategy_config : Box<dyn strategy::GameStrategyConfig + Sync> = match strategy_str {
"random" => { "random" => {
Box::new(strategies::examples::RandomStrategyConfig { Box::new(strategies::examples::RandomStrategyConfig {
hint_probability: 0.4, hint_probability: 0.4,
play_probability: 0.2, play_probability: 0.2,
}) as Box<strategy::GameStrategyConfig + Sync> }) as Box<dyn strategy::GameStrategyConfig + Sync>
}, },
"cheat" => { "cheat" => {
Box::new(strategies::cheating::CheatingStrategyConfig::new()) Box::new(strategies::cheating::CheatingStrategyConfig::new())
as Box<strategy::GameStrategyConfig + Sync> as Box<dyn strategy::GameStrategyConfig + Sync>
}, },
"info" => { "info" => {
Box::new(strategies::information::InformationStrategyConfig::new()) Box::new(strategies::information::InformationStrategyConfig::new())
as Box<strategy::GameStrategyConfig + Sync> as Box<dyn strategy::GameStrategyConfig + Sync>
}, },
_ => { _ => {
panic!("Unexpected strategy argument {}", strategy_str); panic!("Unexpected strategy argument {}", strategy_str);
@ -176,7 +176,7 @@ fn get_results_table() -> String {
let dashes = String::from("---------"); let dashes = String::from("---------");
let dashes_long = String::from("------------------"); let dashes_long = String::from("------------------");
type TwoLines = (String, String); type TwoLines = (String, String);
fn make_twolines(player_nums: &Vec<u32>, head: TwoLines, make_block: &dyn Fn(u32) -> TwoLines) -> TwoLines { fn make_twolines(player_nums: &[u32], head: TwoLines, make_block: &dyn Fn(u32) -> TwoLines) -> TwoLines {
let mut blocks = player_nums.iter().cloned().map(make_block).collect::<Vec<_>>(); let mut blocks = player_nums.iter().cloned().map(make_block).collect::<Vec<_>>();
blocks.insert(0, head); blocks.insert(0, head);
fn combine(items: Vec<String>) -> String { fn combine(items: Vec<String>) -> String {
@ -189,7 +189,7 @@ fn get_results_table() -> String {
body.into_iter().fold(String::default(), |output, (a, b)| (output + &a + "\n" + &b + "\n")) body.into_iter().fold(String::default(), |output, (a, b)| (output + &a + "\n" + &b + "\n"))
} }
let header = make_twolines(&player_nums, let header = make_twolines(&player_nums,
(space.clone(), dashes.clone()), (space.clone(), dashes),
&|n_players| (format_players(n_players), dashes_long.clone())); &|n_players| (format_players(n_players), dashes_long.clone()));
let mut body = strategies.iter().map(|strategy| { let mut body = strategies.iter().map(|strategy| {
make_twolines(&player_nums, (format_name(strategy), space.clone()), &|n_players| { make_twolines(&player_nums, (format_name(strategy), space.clone()), &|n_players| {

View file

@ -24,7 +24,7 @@ fn new_deck(seed: u32) -> Cards {
pub fn simulate_once( pub fn simulate_once(
opts: &GameOptions, opts: &GameOptions,
game_strategy: Box<GameStrategy>, game_strategy: Box<dyn GameStrategy>,
seed: u32, seed: u32,
) -> GameState { ) -> GameState {
let deck = new_deck(seed); let deck = new_deck(seed);
@ -33,7 +33,7 @@ pub fn simulate_once(
let mut strategies = game.get_players().map(|player| { let mut strategies = game.get_players().map(|player| {
(player, game_strategy.initialize(player, &game.get_view(player))) (player, game_strategy.initialize(player, &game.get_view(player)))
}).collect::<FnvHashMap<Player, Box<PlayerStrategy>>>(); }).collect::<FnvHashMap<Player, Box<dyn PlayerStrategy>>>();
while !game.is_over() { while !game.is_over() {
let player = game.board.player; let player = game.board.player;
@ -46,14 +46,14 @@ pub fn simulate_once(
let choice = { let choice = {
let mut strategy = strategies.get_mut(&player).unwrap(); let strategy = strategies.get_mut(&player).unwrap();
strategy.decide(&game.get_view(player)) strategy.decide(&game.get_view(player))
}; };
let turn = game.process_choice(choice); let turn = game.process_choice(choice);
for player in game.get_players() { for player in game.get_players() {
let mut strategy = strategies.get_mut(&player).unwrap(); let strategy = strategies.get_mut(&player).unwrap();
strategy.update(&turn, &game.get_view(player)); strategy.update(&turn, &game.get_view(player));
} }
@ -89,7 +89,7 @@ impl Histogram {
self.insert_many(val, 1); self.insert_many(val, 1);
} }
pub fn get_count(&self, val: &Score) -> u32 { pub fn get_count(&self, val: &Score) -> u32 {
*self.hist.get(&val).unwrap_or(&0) *self.hist.get(val).unwrap_or(&0)
} }
pub fn percentage_with(&self, val: &Score) -> f32 { pub fn percentage_with(&self, val: &Score) -> f32 {
self.get_count(val) as f32 / self.total_count as f32 self.get_count(val) as f32 / self.total_count as f32
@ -119,9 +119,9 @@ impl fmt::Display for Histogram {
let mut keys = self.hist.keys().collect::<Vec<_>>(); let mut keys = self.hist.keys().collect::<Vec<_>>();
keys.sort(); keys.sort();
for val in keys { for val in keys {
try!(f.write_str(&format!( f.write_str(&format!(
"\n{}: {}", val, self.get_count(val), "\n{}: {}", val, self.get_count(val),
))); ))?;
} }
Ok(()) Ok(())
} }
@ -164,7 +164,7 @@ pub fn simulate<T: ?Sized>(
); );
} }
} }
let game = simulate_once(&opts, strat_config_ref.initialize(&opts), seed); let game = simulate_once(opts, strat_config_ref.initialize(opts), seed);
let score = game.score(); let score = game.score();
lives_histogram.insert(game.board.lives_remaining); lives_histogram.insert(game.board.lives_remaining);
score_histogram.insert(score); score_histogram.insert(score);

View file

@ -25,7 +25,7 @@ impl CheatingStrategyConfig {
} }
} }
impl GameStrategyConfig for CheatingStrategyConfig { impl GameStrategyConfig for CheatingStrategyConfig {
fn initialize(&self, _: &GameOptions) -> Box<GameStrategy> { fn initialize(&self, _: &GameOptions) -> Box<dyn GameStrategy> {
Box::new(CheatingStrategy::new()) Box::new(CheatingStrategy::new())
} }
} }
@ -42,7 +42,7 @@ impl CheatingStrategy {
} }
} }
impl GameStrategy for CheatingStrategy { impl GameStrategy for CheatingStrategy {
fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<dyn PlayerStrategy> {
for (&player, &hand) in &view.other_hands { for (&player, &hand) in &view.other_hands {
self.player_hands_cheat.borrow_mut().insert( self.player_hands_cheat.borrow_mut().insert(
player, hand.clone() player, hand.clone()
@ -93,7 +93,7 @@ impl CheatingPlayerStrategy {
// given a hand of cards, represents how badly it will need to play things // given a hand of cards, represents how badly it will need to play things
fn hand_play_value(&self, view: &BorrowedGameView, hand: &Cards) -> u32 { fn hand_play_value(&self, view: &BorrowedGameView, hand: &Cards) -> u32 {
hand.iter().map(|card| self.card_play_value(view, card)).fold(0, |a,b| a+b) hand.iter().map(|card| self.card_play_value(view, card)).sum()
} }
// how badly do we need to play a particular card // how badly do we need to play a particular card
@ -104,13 +104,11 @@ impl CheatingPlayerStrategy {
let my_hand_value = self.hand_play_value(view, my_hand); let my_hand_value = self.hand_play_value(view, my_hand);
for player in view.board.get_players() { for player in view.board.get_players() {
if player != self.me { if player != self.me && view.has_card(&player, card) {
if view.has_card(&player, card) { let their_hand_value = self.hand_play_value(view, hands.get(&player).unwrap());
let their_hand_value = self.hand_play_value(view, hands.get(&player).unwrap()); // they can play this card, and have less urgent plays than i do
// they can play this card, and have less urgent plays than i do if their_hand_value < my_hand_value {
if their_hand_value < my_hand_value { return 10 - (card.value as i32)
return 10 - (card.value as i32)
}
} }
} }
} }
@ -132,7 +130,7 @@ impl CheatingPlayerStrategy {
} }
set.insert(card.clone()); set.insert(card.clone());
} }
return None None
} }
} }
impl PlayerStrategy for CheatingPlayerStrategy { impl PlayerStrategy for CheatingPlayerStrategy {
@ -145,7 +143,7 @@ impl PlayerStrategy for CheatingPlayerStrategy {
view.board.is_playable(card) view.board.is_playable(card)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
if playable_cards.len() > 0 { if !playable_cards.is_empty() {
// play the best playable card // play the best playable card
// the higher the play_score, the better to play // the higher the play_score, the better to play
let mut index = 0; let mut index = 0;
@ -178,10 +176,8 @@ impl PlayerStrategy for CheatingPlayerStrategy {
// hinting is better than discarding dead cards // hinting is better than discarding dead cards
// (probably because it stalls the deck-drawing). // (probably because it stalls the deck-drawing).
if view.board.hints_remaining > 0 { if view.board.hints_remaining > 0 && view.someone_else_can_play() {
if view.someone_else_can_play() { return self.throwaway_hint(view);
return self.throwaway_hint(view);
}
} }
// if anything is totally useless, discard it // if anything is totally useless, discard it

View file

@ -10,7 +10,7 @@ pub struct RandomStrategyConfig {
} }
impl GameStrategyConfig for RandomStrategyConfig { impl GameStrategyConfig for RandomStrategyConfig {
fn initialize(&self, _: &GameOptions) -> Box<GameStrategy> { fn initialize(&self, _: &GameOptions) -> Box<dyn GameStrategy> {
Box::new(RandomStrategy { Box::new(RandomStrategy {
hint_probability: self.hint_probability, hint_probability: self.hint_probability,
play_probability: self.play_probability, play_probability: self.play_probability,
@ -23,7 +23,7 @@ pub struct RandomStrategy {
play_probability: f64, play_probability: f64,
} }
impl GameStrategy for RandomStrategy { impl GameStrategy for RandomStrategy {
fn initialize(&self, player: Player, _: &BorrowedGameView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, _: &BorrowedGameView) -> Box<dyn PlayerStrategy> {
Box::new(RandomStrategyPlayer { Box::new(RandomStrategyPlayer {
hint_probability: self.hint_probability, hint_probability: self.hint_probability,
play_probability: self.play_probability, play_probability: self.play_probability,
@ -44,7 +44,7 @@ impl PlayerStrategy for RandomStrategyPlayer {
if p < self.hint_probability { if p < self.hint_probability {
if view.board.hints_remaining > 0 { if view.board.hints_remaining > 0 {
let hint_player = view.board.player_to_left(&self.me); let hint_player = view.board.player_to_left(&self.me);
let hint_card = rand::thread_rng().choose(&view.get_hand(&hint_player)).unwrap(); let hint_card = rand::thread_rng().choose(view.get_hand(&hint_player)).unwrap();
let hinted = { let hinted = {
if rand::random() { if rand::random() {
// hint a color // hint a color
@ -55,7 +55,7 @@ impl PlayerStrategy for RandomStrategyPlayer {
}; };
TurnChoice::Hint(Hint { TurnChoice::Hint(Hint {
player: hint_player, player: hint_player,
hinted: hinted, hinted,
}) })
} else { } else {
TurnChoice::Discard(0) TurnChoice::Discard(0)

View file

@ -10,8 +10,8 @@ impl ModulusInformation {
pub fn new(modulus: u32, value: u32) -> Self { pub fn new(modulus: u32, value: u32) -> Self {
assert!(value < modulus); assert!(value < modulus);
ModulusInformation { ModulusInformation {
modulus: modulus, modulus,
value: value, value,
} }
} }
@ -21,7 +21,7 @@ impl ModulusInformation {
pub fn combine(&mut self, other: Self, max_modulus: u32) { pub fn combine(&mut self, other: Self, max_modulus: u32) {
assert!(other.modulus <= self.info_remaining(max_modulus)); assert!(other.modulus <= self.info_remaining(max_modulus));
self.value = self.value + self.modulus * other.value; self.value += self.modulus * other.value;
self.modulus = std::cmp::min(max_modulus, self.modulus * other.modulus); self.modulus = std::cmp::min(max_modulus, self.modulus * other.modulus);
assert!(self.value < self.modulus); assert!(self.value < self.modulus);
} }
@ -45,7 +45,7 @@ impl ModulusInformation {
let original_modulus = self.modulus; let original_modulus = self.modulus;
let original_value = self.value; let original_value = self.value;
let value = self.value % modulus; let value = self.value % modulus;
self.value = self.value / modulus; self.value /= modulus;
// `self.modulus` is the largest number such that // `self.modulus` is the largest number such that
// `value + (self.modulus - 1) * modulus < original_modulus`. // `value + (self.modulus - 1) * modulus < original_modulus`.
// TODO: find an explanation of why this makes everything work out // TODO: find an explanation of why this makes everything work out
@ -80,10 +80,10 @@ pub trait Question {
// how much info does this question ask for? // how much info does this question ask for?
fn info_amount(&self) -> u32; fn info_amount(&self) -> u32;
// get the answer to this question, given cards // get the answer to this question, given cards
fn answer(&self, &Cards, &BoardState) -> u32; fn answer(&self, hand: &Cards, board: &BoardState) -> u32;
// process the answer to this question, updating card info // process the answer to this question, updating card info
fn acknowledge_answer( fn acknowledge_answer(
&self, value: u32, &mut HandInfo<CardPossibilityTable>, &BoardState &self, value: u32, hand_info: &mut HandInfo<CardPossibilityTable>, board: &BoardState
); );
fn answer_info(&self, hand: &Cards, board: &BoardState) -> ModulusInformation { fn answer_info(&self, hand: &Cards, board: &BoardState) -> ModulusInformation {
@ -105,11 +105,11 @@ pub trait Question {
} }
pub trait PublicInformation: Clone { pub trait PublicInformation: Clone {
fn get_player_info(&self, &Player) -> HandInfo<CardPossibilityTable>; fn get_player_info(&self, player: &Player) -> HandInfo<CardPossibilityTable>;
fn set_player_info(&mut self, &Player, HandInfo<CardPossibilityTable>); fn set_player_info(&mut self, player: &Player, hand_info: HandInfo<CardPossibilityTable>);
fn new(&BoardState) -> Self; fn new(board: &BoardState) -> Self;
fn set_board(&mut self, &BoardState); fn set_board(&mut self, board: &BoardState);
/// If we store more state than just `HandInfo<CardPossibilityTable>`s, update it after `set_player_info` has been called. /// If we store more state than just `HandInfo<CardPossibilityTable>`s, update it after `set_player_info` has been called.
fn update_other_info(&mut self) { fn update_other_info(&mut self) {
@ -126,10 +126,10 @@ pub trait PublicInformation: Clone {
/// ///
/// Note that `self` does not reflect the answers to previous questions; it reflects the state /// Note that `self` does not reflect the answers to previous questions; it reflects the state
/// before the entire "hat value" calculation. /// before the entire "hat value" calculation.
fn ask_question(&self, &Player, &HandInfo<CardPossibilityTable>, total_info: u32) -> Option<Box<Question>>; fn ask_question(&self, player: &Player, hand_info: &HandInfo<CardPossibilityTable>, total_info: u32) -> Option<Box<dyn Question>>;
fn ask_question_wrapper(&self, player: &Player, hand_info: &HandInfo<CardPossibilityTable>, total_info: u32) fn ask_question_wrapper(&self, player: &Player, hand_info: &HandInfo<CardPossibilityTable>, total_info: u32)
-> Option<Box<Question>> -> Option<Box<dyn Question>>
{ {
assert!(total_info > 0); assert!(total_info > 0);
if total_info == 1 { if total_info == 1 {
@ -191,7 +191,7 @@ pub trait PublicInformation: Clone {
let (infos, new_player_hands): (Vec<_>, Vec<_>) = view.get_other_players().iter().map(|player| { let (infos, new_player_hands): (Vec<_>, Vec<_>) = view.get_other_players().iter().map(|player| {
let mut hand_info = self.get_player_info(player); let mut hand_info = self.get_player_info(player);
let info = self.get_hat_info_for_player(player, &mut hand_info, total_info, view); let info = self.get_hat_info_for_player(player, &mut hand_info, total_info, view);
(info, (player.clone(), hand_info)) (info, (*player, hand_info))
}).unzip(); }).unzip();
self.set_player_infos(new_player_hands); self.set_player_infos(new_player_hands);
infos.into_iter().fold( infos.into_iter().fold(
@ -213,7 +213,7 @@ pub trait PublicInformation: Clone {
}).map(|player| { }).map(|player| {
let mut hand_info = self.get_player_info(&player); let mut hand_info = self.get_player_info(&player);
let player_info = self.get_hat_info_for_player(&player, &mut hand_info, info.modulus, view); let player_info = self.get_hat_info_for_player(&player, &mut hand_info, info.modulus, view);
(player_info, (player.clone(), hand_info)) (player_info, (player, hand_info))
}).unzip(); }).unzip();
for other_info in other_infos { for other_info in other_infos {
info.subtract(&other_info); info.subtract(&other_info);

View file

@ -58,7 +58,7 @@ fn q_is_dead(index: usize) -> CardHasProperty {
/// It's named that way because the `info_amount` grows additively with the `info_amount`s of /// It's named that way because the `info_amount` grows additively with the `info_amount`s of
/// the questions in `l`. /// the questions in `l`.
struct AdditiveComboQuestion { struct AdditiveComboQuestion {
questions: Vec<Box<Question>>, questions: Vec<Box<dyn Question>>,
} }
impl Question for AdditiveComboQuestion { impl Question for AdditiveComboQuestion {
fn info_amount(&self) -> u32 { fn info_amount(&self) -> u32 {
@ -113,7 +113,7 @@ impl CardPossibilityPartition {
let mut partition = FnvHashMap::default(); let mut partition = FnvHashMap::default();
let mut n_partitions = 0; let mut n_partitions = 0;
let has_dead = card_table.probability_is_dead(&board) != 0.0; let has_dead = card_table.probability_is_dead(board) != 0.0;
// TODO: group things of different colors and values? // TODO: group things of different colors and values?
let mut effective_max = max_n_partitions; let mut effective_max = max_n_partitions;
@ -152,9 +152,9 @@ impl CardPossibilityPartition {
// debug!("{}", s); // debug!("{}", s);
CardPossibilityPartition { CardPossibilityPartition {
index: index, index,
n_partitions: n_partitions, n_partitions,
partition: partition, partition,
} }
} }
} }
@ -162,7 +162,7 @@ impl Question for CardPossibilityPartition {
fn info_amount(&self) -> u32 { self.n_partitions } fn info_amount(&self) -> u32 { self.n_partitions }
fn answer(&self, hand: &Cards, _: &BoardState) -> u32 { fn answer(&self, hand: &Cards, _: &BoardState) -> u32 {
let ref card = hand[self.index]; let ref card = hand[self.index];
*self.partition.get(&card).unwrap() *self.partition.get(card).unwrap()
} }
fn acknowledge_answer( fn acknowledge_answer(
&self, &self,
@ -220,7 +220,7 @@ impl MyPublicInformation {
}) })
}); });
return if !may_be_all_one_color && !may_be_all_one_number { 4 } else { 3 } if !may_be_all_one_color && !may_be_all_one_number { 4 } else { 3 }
} }
fn get_hint_index_score(&self, card_table: &CardPossibilityTable) -> i32 { fn get_hint_index_score(&self, card_table: &CardPossibilityTable) -> i32 {
@ -238,7 +238,7 @@ impl MyPublicInformation {
if !card_table.value_determined() { if !card_table.value_determined() {
score += 1; score += 1;
} }
return score; score
} }
fn get_index_for_hint(&self, player: &Player) -> usize { fn get_index_for_hint(&self, player: &Player) -> usize {
@ -355,12 +355,12 @@ impl MyPublicInformation {
hint_option_set.into_iter().collect::<FnvHashSet<_>>().into_iter().map(|hinted| { hint_option_set.into_iter().collect::<FnvHashSet<_>>().into_iter().map(|hinted| {
Hint { Hint {
player: hint_player, player: hint_player,
hinted: hinted, hinted,
} }
}).collect() }).collect()
} }
fn decode_hint_choice(&self, hint: &Hint, result: &Vec<bool>) -> ModulusInformation { fn decode_hint_choice(&self, hint: &Hint, result: &[bool]) -> ModulusInformation {
let hinter = self.board.player; let hinter = self.board.player;
let info_per_player: Vec<_> = self.get_other_players_starting_after(hinter).into_iter().map( let info_per_player: Vec<_> = self.get_other_players_starting_after(hinter).into_iter().map(
@ -372,7 +372,7 @@ impl MyPublicInformation {
let player_amt = (n + hint.player - hinter - 1) % n; let player_amt = (n + hint.player - hinter - 1) % n;
let amt_from_prev_players = info_per_player.iter().take(player_amt as usize).fold(0, |a, b| a + b); let amt_from_prev_players = info_per_player.iter().take(player_amt as usize).sum::<u32>();
let hint_info_we_can_give_to_this_player = info_per_player[player_amt as usize]; let hint_info_we_can_give_to_this_player = info_per_player[player_amt as usize];
let card_index = self.get_index_for_hint(&hint.player); let card_index = self.get_index_for_hint(&hint.player);
@ -405,12 +405,12 @@ impl MyPublicInformation {
ModulusInformation::new(total_info, hint_value) ModulusInformation::new(total_info, hint_value)
} }
fn update_from_hint_choice(&mut self, hint: &Hint, matches: &Vec<bool>, view: &OwnedGameView) { fn update_from_hint_choice(&mut self, hint: &Hint, matches: &[bool], view: &OwnedGameView) {
let info = self.decode_hint_choice(hint, matches); let info = self.decode_hint_choice(hint, matches);
self.update_from_hat_sum(info, view); self.update_from_hat_sum(info, view);
} }
fn update_from_hint_matches(&mut self, hint: &Hint, matches: &Vec<bool>) { fn update_from_hint_matches(&mut self, hint: &Hint, matches: &[bool]) {
let info = self.get_player_info_mut(&hint.player); let info = self.get_player_info_mut(&hint.player);
info.update_for_hint(&hint.hinted, matches); info.update_for_hint(&hint.hinted, matches);
} }
@ -424,10 +424,10 @@ impl MyPublicInformation {
fn someone_else_needs_hint(&self, view: &OwnedGameView) -> bool { fn someone_else_needs_hint(&self, view: &OwnedGameView) -> bool {
// Does another player have a playable card, but doesn't know it? // Does another player have a playable card, but doesn't know it?
view.get_other_players().iter().any(|player| { view.get_other_players().iter().any(|player| {
let has_playable_card = view.get_hand(&player).iter().any(|card| { let has_playable_card = view.get_hand(player).iter().any(|card| {
view.get_board().is_playable(card) view.get_board().is_playable(card)
}); });
has_playable_card && !self.knows_playable_card(&player) has_playable_card && !self.knows_playable_card(player)
}) })
} }
@ -466,7 +466,7 @@ impl MyPublicInformation {
info.remove(index); info.remove(index);
// push *before* incrementing public counts // push *before* incrementing public counts
if info.len() < new_view.hand_size(&player) { if info.len() < new_view.hand_size(player) {
info.push(new_card_table); info.push(new_card_table);
} }
} }
@ -491,7 +491,7 @@ impl PublicInformation for MyPublicInformation {
(player, hand_info) (player, hand_info)
}).collect::<FnvHashMap<_,_>>(); }).collect::<FnvHashMap<_,_>>();
MyPublicInformation { MyPublicInformation {
hand_info: hand_info, hand_info,
card_counts: CardCounts::new(), card_counts: CardCounts::new(),
board: board.clone(), board: board.clone(),
} }
@ -518,7 +518,7 @@ impl PublicInformation for MyPublicInformation {
_me: &Player, _me: &Player,
hand_info: &HandInfo<CardPossibilityTable>, hand_info: &HandInfo<CardPossibilityTable>,
total_info: u32, total_info: u32,
) -> Option<Box<Question>> { ) -> Option<Box<dyn Question>> {
// Changing anything inside this function will not break the information transfer // Changing anything inside this function will not break the information transfer
// mechanisms! // mechanisms!
@ -532,8 +532,7 @@ impl PublicInformation for MyPublicInformation {
// We don't need to find out anything about cards that are determined or dead. // We don't need to find out anything about cards that are determined or dead.
let augmented_hand_info = augmented_hand_info_raw.into_iter().filter(|&(i, _, p_dead)| { let augmented_hand_info = augmented_hand_info_raw.into_iter().filter(|&(i, _, p_dead)| {
if p_dead == 1.0 { false } if p_dead == 1.0 || hand_info[i].is_determined() { false }
else if hand_info[i].is_determined() { false }
else { true } else { true }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
@ -565,11 +564,11 @@ impl PublicInformation for MyPublicInformation {
// only matters if we find a playable/dead card, and conditional on that, it's better // only matters if we find a playable/dead card, and conditional on that, it's better
// to find out about as many non-playable/non-dead cards as possible. // to find out about as many non-playable/non-dead cards as possible.
to_ask.sort_by_key(|&(ask_dead, _, p_yes)| {(ask_dead, FloatOrd(p_yes))}); to_ask.sort_by_key(|&(ask_dead, _, p_yes)| {(ask_dead, FloatOrd(p_yes))});
let questions = to_ask.into_iter().map(|(ask_dead, i, _)| -> Box<Question> { let questions = to_ask.into_iter().map(|(ask_dead, i, _)| -> Box<dyn Question> {
if ask_dead { Box::new(q_is_dead(i)) } if ask_dead { Box::new(q_is_dead(i)) }
else { Box::new(q_is_playable(i)) } else { Box::new(q_is_playable(i)) }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
if questions.len() > 0 { if !questions.is_empty() {
return Some(Box::new(AdditiveComboQuestion { questions })) return Some(Box::new(AdditiveComboQuestion { questions }))
} }
} }
@ -607,7 +606,7 @@ impl InformationStrategyConfig {
} }
} }
impl GameStrategyConfig for InformationStrategyConfig { impl GameStrategyConfig for InformationStrategyConfig {
fn initialize(&self, _: &GameOptions) -> Box<GameStrategy> { fn initialize(&self, _: &GameOptions) -> Box<dyn GameStrategy> {
Box::new(InformationStrategy::new()) Box::new(InformationStrategy::new())
} }
} }
@ -620,7 +619,7 @@ impl InformationStrategy {
} }
} }
impl GameStrategy for InformationStrategy { impl GameStrategy for InformationStrategy {
fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<PlayerStrategy> { fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<dyn PlayerStrategy> {
Box::new(InformationPlayerStrategy { Box::new(InformationPlayerStrategy {
me: player, me: player,
public_info: MyPublicInformation::new(view.board), public_info: MyPublicInformation::new(view.board),
@ -650,10 +649,8 @@ impl InformationPlayerStrategy {
let mut num_with = 1; let mut num_with = 1;
if view.board.deck_size > 0 { if view.board.deck_size > 0 {
for player in view.board.get_players() { for player in view.board.get_players() {
if player != self.me { if player != self.me && view.has_card(&player, card) {
if view.has_card(&player, card) { num_with += 1;
num_with += 1;
}
} }
} }
} }
@ -667,21 +664,19 @@ impl InformationPlayerStrategy {
for (i, card_table) in hand.iter().enumerate() { for (i, card_table) in hand.iter().enumerate() {
if card_table.probability_is_dead(board) == 1.0 { if card_table.probability_is_dead(board) == 1.0 {
useless.insert(i); useless.insert(i);
} else { } else if let Some(card) = card_table.get_card() {
if let Some(card) = card_table.get_card() { if seen.contains_key(&card) {
if seen.contains_key(&card) { // found a duplicate card
// found a duplicate card useless.insert(i);
useless.insert(i); useless.insert(*seen.get(&card).unwrap());
useless.insert(*seen.get(&card).unwrap()); } else {
} else { seen.insert(card, i);
seen.insert(card, i);
}
} }
} }
} }
let mut useless_vec : Vec<usize> = useless.into_iter().collect(); let mut useless_vec : Vec<usize> = useless.into_iter().collect();
useless_vec.sort(); useless_vec.sort();
return useless_vec; useless_vec
} }
// how good is it to give this hint to this player? // how good is it to give this hint to this player?
@ -692,8 +687,8 @@ impl InformationPlayerStrategy {
let hint_player = &hint.player; let hint_player = &hint.player;
let hinted = &hint.hinted; let hinted = &hint.hinted;
let hand = view.get_hand(&hint_player); let hand = view.get_hand(hint_player);
let mut hand_info = self.public_info.get_player_info(&hint_player); let mut hand_info = self.public_info.get_player_info(hint_player);
let mut goodness = 1.0; let mut goodness = 1.0;
for (i, card_table) in hand_info.iter_mut().enumerate() { for (i, card_table) in hand_info.iter_mut().enumerate() {
@ -716,9 +711,7 @@ impl InformationPlayerStrategy {
let new_weight = card_table.total_weight(); let new_weight = card_table.total_weight();
assert!(new_weight <= old_weight); assert!(new_weight <= old_weight);
let bonus = { let bonus = {
if card_table.is_determined() { if card_table.is_determined() || card_table.probability_is_dead(&view.board) == 1.0 {
2
} else if card_table.probability_is_dead(&view.board) == 1.0 {
2 2
} else { } else {
1 1
@ -744,12 +737,10 @@ impl InformationPlayerStrategy {
h2.0.partial_cmp(&h1.0).unwrap_or(Ordering::Equal) h2.0.partial_cmp(&h1.0).unwrap_or(Ordering::Equal)
}); });
if hint_options.len() == 0 { if hint_options.is_empty() {
// NOTE: Technically possible, but never happens // NOTE: Technically possible, but never happens
} else { } else if hint_options.len() > 1 {
if hint_options.len() > 1 { debug!("Choosing amongst hint options: {:?}", hint_options);
debug!("Choosing amongst hint options: {:?}", hint_options);
}
} }
hint_options.remove(0).1 hint_options.remove(0).1
} }
@ -809,7 +800,7 @@ impl InformationPlayerStrategy {
(i, card_table, p) (i, card_table, p)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
if risky_playable_cards.len() > 0 { if !risky_playable_cards.is_empty() {
risky_playable_cards.sort_by(|c1, c2| { risky_playable_cards.sort_by(|c1, c2| {
c2.2.partial_cmp(&c1.2).unwrap_or(Ordering::Equal) c2.2.partial_cmp(&c1.2).unwrap_or(Ordering::Equal)
}); });
@ -827,7 +818,7 @@ impl InformationPlayerStrategy {
// NOTE When changing this, make sure to keep the "discard" branch of update() up to date! // NOTE When changing this, make sure to keep the "discard" branch of update() up to date!
let will_hint = let will_hint =
if view.board.hints_remaining > 0 && public_info.someone_else_needs_hint(view) { true } if view.board.hints_remaining > 0 && public_info.someone_else_needs_hint(view) { true }
else if view.board.discard_size() <= discard_threshold && useless_indices.len() > 0 { false } else if view.board.discard_size() <= discard_threshold && !useless_indices.is_empty() { false }
// hinting is better than discarding dead cards // hinting is better than discarding dead cards
// (probably because it stalls the deck-drawing). // (probably because it stalls the deck-drawing).
else if view.board.hints_remaining > 0 && view.someone_else_can_play() { true } else if view.board.hints_remaining > 0 && view.someone_else_can_play() { true }
@ -849,7 +840,7 @@ impl InformationPlayerStrategy {
if public_useless_indices.len() > 1 { if public_useless_indices.len() > 1 {
let info = public_info.get_hat_sum(public_useless_indices.len() as u32, view); let info = public_info.get_hat_sum(public_useless_indices.len() as u32, view);
return TurnChoice::Discard(public_useless_indices[info.value as usize]); return TurnChoice::Discard(public_useless_indices[info.value as usize]);
} else if useless_indices.len() > 0 { } else if !useless_indices.is_empty() {
// TODO: have opponents infer that i knew a card was useless // TODO: have opponents infer that i knew a card was useless
// TODO: after that, potentially prefer useless indices that arent public // TODO: after that, potentially prefer useless indices that arent public
return TurnChoice::Discard(useless_indices[0]); return TurnChoice::Discard(useless_indices[0]);

View file

@ -6,21 +6,21 @@ use game::*;
pub trait PlayerStrategy { pub trait PlayerStrategy {
// A function to decide what to do on the player's turn. // A function to decide what to do on the player's turn.
// Given a BorrowedGameView, outputs their choice. // Given a BorrowedGameView, outputs their choice.
fn decide(&mut self, &BorrowedGameView) -> TurnChoice; fn decide(&mut self, view: &BorrowedGameView) -> TurnChoice;
// A function to update internal state after other players' turns. // A function to update internal state after other players' turns.
// Given what happened last turn, and the new state. // Given what happened last turn, and the new state.
fn update(&mut self, &TurnRecord, &BorrowedGameView); fn update(&mut self, turn_record: &TurnRecord, view: &BorrowedGameView);
} }
// Represents the overall strategy for a game // Represents the overall strategy for a game
// Shouldn't do much, except store configuration parameters and // Shouldn't do much, except store configuration parameters and
// possibility initialize some shared randomness between players // possibility initialize some shared randomness between players
pub trait GameStrategy { pub trait GameStrategy {
fn initialize(&self, Player, &BorrowedGameView) -> Box<PlayerStrategy>; fn initialize(&self, player: Player, view: &BorrowedGameView) -> Box<dyn PlayerStrategy>;
} }
// Represents configuration for a strategy. // Represents configuration for a strategy.
// Acts as a factory for game strategies, so we can play many rounds // Acts as a factory for game strategies, so we can play many rounds
pub trait GameStrategyConfig { pub trait GameStrategyConfig {
fn initialize(&self, &GameOptions) -> Box<GameStrategy>; fn initialize(&self, opts: &GameOptions) -> Box<dyn GameStrategy>;
} }