make color = char
This commit is contained in:
parent
58c881130a
commit
ec1fd2eb07
5 changed files with 105 additions and 97 deletions
25
README.md
25
README.md
|
@ -13,7 +13,7 @@ This strategy achieves the best results I am aware of for n > 2 (see below).
|
||||||
|
|
||||||
Please contact me if:
|
Please contact me if:
|
||||||
- You know of other interesting/good strategy ideas!
|
- You know of other interesting/good strategy ideas!
|
||||||
- Have questions about the framework
|
- Have questions about the framework or existing strategies
|
||||||
|
|
||||||
Some similar projects I am aware of:
|
Some similar projects I am aware of:
|
||||||
- https://github.com/rjtobin/HanSim (written for the paper mentioned above)
|
- https://github.com/rjtobin/HanSim (written for the paper mentioned above)
|
||||||
|
@ -30,20 +30,33 @@ Usage: target/debug/rust_hanabi [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-l, --loglevel LOGLEVEL
|
-l, --loglevel LOGLEVEL
|
||||||
Log level, one of 'trace', 'debug', 'info', 'warn', and 'error'
|
Log level, one of 'trace', 'debug', 'info', 'warn',
|
||||||
|
and 'error'
|
||||||
-n, --ntrials NTRIALS
|
-n, --ntrials NTRIALS
|
||||||
Number of games to simulate
|
Number of games to simulate (default 1)
|
||||||
-t, --nthreads NTHREADS
|
-t, --nthreads NTHREADS
|
||||||
Number of threads to use for simulation
|
Number of threads to use for simulation (default 1)
|
||||||
-s, --seed SEED Seed for PRNG
|
-s, --seed SEED Seed for PRNG (default random)
|
||||||
-p, --nplayers NPLAYERS
|
-p, --nplayers NPLAYERS
|
||||||
Number of players
|
Number of players
|
||||||
|
-g, --strategy STRATEGY
|
||||||
|
Which strategy to use. One of 'random', 'cheat', and
|
||||||
|
'info'
|
||||||
-h, --help Print this help menu
|
-h, --help Print this help menu
|
||||||
```
|
```
|
||||||
|
|
||||||
For example,
|
For example,
|
||||||
|
|
||||||
`cargo run -- -n 10000 -s 0 -t 2 -p 3`
|
```
|
||||||
|
cargo run -- -n 10000 -s 0 -t 2 -p 5 -g cheat
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if the simulation is slow (as the info strategy is),
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo build --release
|
||||||
|
time ./target/release/rust_hanabi -n 10000 -s 0 -t 2 -p 5 -g info
|
||||||
|
```
|
||||||
|
|
||||||
## Results
|
## Results
|
||||||
|
|
||||||
|
|
31
src/cards.rs
31
src/cards.rs
|
@ -1,19 +1,16 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub type Color = &'static str;
|
pub type Color = char;
|
||||||
pub const COLORS: [Color; 5] = ["red", "yellow", "green", "blue", "white"];
|
pub const COLORS: [Color; 5] = ['r', 'y', 'g', 'b', 'w'];
|
||||||
pub fn display_color(color: Color) -> char {
|
|
||||||
color.chars().next().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Value = u32;
|
pub type Value = u32;
|
||||||
// list of values, assumed to be small to large
|
// list of values, assumed to be small to large
|
||||||
pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5];
|
pub const VALUES : [Value; 5] = [1, 2, 3, 4, 5];
|
||||||
pub const FINAL_VALUE : Value = 5;
|
pub const FINAL_VALUE : Value = 5;
|
||||||
|
|
||||||
pub fn get_count_for_value(value: &Value) -> u32 {
|
pub fn get_count_for_value(value: Value) -> u32 {
|
||||||
match *value {
|
match value {
|
||||||
1 => 3,
|
1 => 3,
|
||||||
2 | 3 | 4 => 2,
|
2 | 3 | 4 => 2,
|
||||||
5 => 1,
|
5 => 1,
|
||||||
|
@ -33,7 +30,7 @@ impl Card {
|
||||||
}
|
}
|
||||||
impl fmt::Display for Card {
|
impl fmt::Display for Card {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}{}", display_color(self.color), self.value)
|
write!(f, "{}{}", self.color, self.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +43,9 @@ pub struct CardCounts {
|
||||||
impl CardCounts {
|
impl CardCounts {
|
||||||
pub fn new() -> CardCounts {
|
pub fn new() -> CardCounts {
|
||||||
let mut counts = HashMap::new();
|
let mut counts = HashMap::new();
|
||||||
for color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
for value in VALUES.iter() {
|
for &value in VALUES.iter() {
|
||||||
counts.insert(Card::new(*color, *value), 0);
|
counts.insert(Card::new(color, value), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CardCounts {
|
CardCounts {
|
||||||
|
@ -62,7 +59,7 @@ impl CardCounts {
|
||||||
|
|
||||||
pub fn remaining(&self, card: &Card) -> u32 {
|
pub fn remaining(&self, card: &Card) -> u32 {
|
||||||
let count = self.get_count(card);
|
let count = self.get_count(card);
|
||||||
get_count_for_value(&card.value) - count
|
get_count_for_value(card.value) - count
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn increment(&mut self, card: &Card) {
|
pub fn increment(&mut self, card: &Card) {
|
||||||
|
@ -72,17 +69,17 @@ 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!(
|
try!(f.write_str(&format!(
|
||||||
"{}: ", display_color(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!(
|
try!(f.write_str(&format!(
|
||||||
"{}/{} {}s", count, total, value
|
"{}/{} {}s", count, total, value
|
||||||
)));
|
)));
|
||||||
if *value != FINAL_VALUE {
|
if value != FINAL_VALUE {
|
||||||
try!(f.write_str(", "));
|
try!(f.write_str(", "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
src/game.rs
48
src/game.rs
|
@ -116,16 +116,16 @@ impl PlayerState {
|
||||||
|
|
||||||
pub fn reveal(&mut self, hinted: &Hinted) -> Vec<bool> {
|
pub fn reveal(&mut self, hinted: &Hinted) -> Vec<bool> {
|
||||||
match hinted {
|
match hinted {
|
||||||
&Hinted::Color(ref color) => {
|
&Hinted::Color(color) => {
|
||||||
self.hand_info_iter_mut().map(|(card, info)| {
|
self.hand_info_iter_mut().map(|(card, info)| {
|
||||||
let matches = card.color == *color;
|
let matches = card.color == color;
|
||||||
info.mark_color(color, matches);
|
info.mark_color(color, matches);
|
||||||
matches
|
matches
|
||||||
}).collect::<Vec<_>>()
|
}).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
&Hinted::Value(ref value) => {
|
&Hinted::Value(value) => {
|
||||||
self.hand_info_iter_mut().map(|(card, info)| {
|
self.hand_info_iter_mut().map(|(card, info)| {
|
||||||
let matches = card.value == *value;
|
let matches = card.value == value;
|
||||||
info.mark_value(value, matches);
|
info.mark_value(value, matches);
|
||||||
matches
|
matches
|
||||||
}).collect::<Vec<_>>()
|
}).collect::<Vec<_>>()
|
||||||
|
@ -137,11 +137,11 @@ impl PlayerState {
|
||||||
fn new_deck(seed: u32) -> Cards {
|
fn new_deck(seed: u32) -> Cards {
|
||||||
let mut deck: Cards = Cards::new();
|
let mut deck: Cards = Cards::new();
|
||||||
|
|
||||||
for color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
for value in VALUES.iter() {
|
for &value in VALUES.iter() {
|
||||||
let count = get_count_for_value(value);
|
let count = get_count_for_value(value);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
deck.push(Card::new(color, value.clone()));
|
deck.push(Card::new(color, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -181,7 +181,7 @@ pub struct BoardState {
|
||||||
impl BoardState {
|
impl BoardState {
|
||||||
pub fn new(opts: &GameOptions, seed: u32) -> BoardState {
|
pub fn new(opts: &GameOptions, seed: u32) -> BoardState {
|
||||||
let mut fireworks : HashMap<Color, Firework> = HashMap::new();
|
let mut fireworks : HashMap<Color, Firework> = HashMap::new();
|
||||||
for color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
fireworks.insert(color, Firework::new(color));
|
fireworks.insert(color, Firework::new(color));
|
||||||
}
|
}
|
||||||
let deck = new_deck(seed);
|
let deck = new_deck(seed);
|
||||||
|
@ -213,34 +213,34 @@ impl BoardState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_firework(&self, color: &Color) -> &Firework {
|
pub fn get_firework(&self, color: Color) -> &Firework {
|
||||||
self.fireworks.get(color).unwrap()
|
self.fireworks.get(&color).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_firework_mut(&mut self, color: &Color) -> &mut Firework {
|
fn get_firework_mut(&mut self, color: Color) -> &mut Firework {
|
||||||
self.fireworks.get_mut(color).unwrap()
|
self.fireworks.get_mut(&color).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns whether a card would place on a firework
|
// returns whether a card would place on a firework
|
||||||
pub fn is_playable(&self, card: &Card) -> bool {
|
pub fn is_playable(&self, card: &Card) -> bool {
|
||||||
Some(card.value) == self.get_firework(&card.color).desired_value()
|
Some(card.value) == self.get_firework(card.color).desired_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
// best possible value we can get for firework of that color,
|
// best possible value we can get for firework of that color,
|
||||||
// based on looking at discard + fireworks
|
// based on looking at discard + fireworks
|
||||||
fn highest_attainable(&self, color: &Color) -> Value {
|
fn highest_attainable(&self, color: Color) -> Value {
|
||||||
let firework = self.fireworks.get(color).unwrap();
|
let firework = self.fireworks.get(&color).unwrap();
|
||||||
if firework.complete() {
|
if firework.complete() {
|
||||||
return FINAL_VALUE;
|
return FINAL_VALUE;
|
||||||
}
|
}
|
||||||
let desired = firework.desired_value().unwrap();
|
let desired = firework.desired_value().unwrap();
|
||||||
|
|
||||||
for value in VALUES.iter() {
|
for &value in VALUES.iter() {
|
||||||
if *value < desired {
|
if value < desired {
|
||||||
// already have these cards
|
// already have these cards
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let needed_card = Card::new(color, value.clone());
|
let needed_card = Card::new(color, value);
|
||||||
if self.discard.has_all(&needed_card) {
|
if self.discard.has_all(&needed_card) {
|
||||||
// already discarded all of these
|
// already discarded all of these
|
||||||
return value - 1;
|
return value - 1;
|
||||||
|
@ -251,7 +251,7 @@ impl BoardState {
|
||||||
|
|
||||||
// is never going to play, based on discard + fireworks
|
// is never going to play, based on discard + fireworks
|
||||||
pub fn is_dead(&self, card: &Card) -> bool {
|
pub fn is_dead(&self, card: &Card) -> bool {
|
||||||
let firework = self.fireworks.get(card.color).unwrap();
|
let firework = self.fireworks.get(&card.color).unwrap();
|
||||||
if firework.complete() {
|
if firework.complete() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -259,14 +259,14 @@ impl BoardState {
|
||||||
if card.value < desired {
|
if card.value < desired {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
card.value > self.highest_attainable(&card.color)
|
card.value > self.highest_attainable(card.color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// can be discarded without necessarily sacrificing score, based on discard + fireworks
|
// can be discarded without necessarily sacrificing score, based on discard + fireworks
|
||||||
pub fn is_dispensable(&self, card: &Card) -> bool {
|
pub fn is_dispensable(&self, card: &Card) -> bool {
|
||||||
let firework = self.fireworks.get(card.color).unwrap();
|
let firework = self.fireworks.get(&card.color).unwrap();
|
||||||
if firework.complete() {
|
if firework.complete() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,7 +274,7 @@ impl BoardState {
|
||||||
if card.value < desired {
|
if card.value < desired {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
if card.value > self.highest_attainable(&card.color) {
|
if card.value > self.highest_attainable(card.color) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
self.discard.remaining(&card) != 1
|
self.discard.remaining(&card) != 1
|
||||||
|
@ -342,7 +342,7 @@ impl fmt::Display for BoardState {
|
||||||
"{}/{} lives remaining\n", self.lives_remaining, self.lives_total
|
"{}/{} lives remaining\n", self.lives_remaining, self.lives_total
|
||||||
)));
|
)));
|
||||||
try!(f.write_str("Fireworks:\n"));
|
try!(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))));
|
try!(f.write_str(&format!(" {}\n", self.get_firework(color))));
|
||||||
}
|
}
|
||||||
try!(f.write_str("Discard:\n"));
|
try!(f.write_str("Discard:\n"));
|
||||||
|
@ -597,7 +597,7 @@ impl GameState {
|
||||||
let playable = self.board.is_playable(&card);
|
let playable = self.board.is_playable(&card);
|
||||||
if playable {
|
if playable {
|
||||||
{
|
{
|
||||||
let firework = self.board.get_firework_mut(&card.color);
|
let firework = self.board.get_firework_mut(card.color);
|
||||||
debug!("Successfully played {}!", card);
|
debug!("Successfully played {}!", card);
|
||||||
firework.place(&card);
|
firework.place(&card);
|
||||||
}
|
}
|
||||||
|
|
61
src/info.rs
61
src/info.rs
|
@ -9,20 +9,11 @@ use game::BoardState;
|
||||||
|
|
||||||
// trait representing information about a card
|
// trait representing information about a card
|
||||||
pub trait CardInfo {
|
pub trait CardInfo {
|
||||||
// get all a-priori possibilities
|
|
||||||
fn get_all_possibilities(&self) -> Vec<Card> {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
for &color in COLORS.iter() {
|
|
||||||
for &value in VALUES.iter() {
|
|
||||||
v.push(Card::new(color, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
// whether the card is possible
|
// whether the card is possible
|
||||||
fn is_possible(&self, card: &Card) -> bool;
|
fn is_possible(&self, card: &Card) -> bool;
|
||||||
|
|
||||||
// mark all current possibilities for the card
|
// mark all current possibilities for the card
|
||||||
|
// this should generally be overridden, for efficiency
|
||||||
fn get_possibilities(&self) -> Vec<Card> {
|
fn get_possibilities(&self) -> Vec<Card> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
for &color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
|
@ -89,16 +80,16 @@ pub trait CardInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark a whole color as false
|
// mark a whole color as false
|
||||||
fn mark_color_false(&mut self, color: &Color);
|
fn mark_color_false(&mut self, color: Color);
|
||||||
// mark a color as correct
|
// mark a color as correct
|
||||||
fn mark_color_true(&mut self, color: &Color) {
|
fn mark_color_true(&mut self, color: Color) {
|
||||||
for other_color in COLORS.iter() {
|
for &other_color in COLORS.iter() {
|
||||||
if other_color != color {
|
if other_color != color {
|
||||||
self.mark_color_false(other_color);
|
self.mark_color_false(other_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn mark_color(&mut self, color: &Color, is_color: bool) {
|
fn mark_color(&mut self, color: Color, is_color: bool) {
|
||||||
if is_color {
|
if is_color {
|
||||||
self.mark_color_true(color);
|
self.mark_color_true(color);
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,16 +98,16 @@ pub trait CardInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark a whole value as false
|
// mark a whole value as false
|
||||||
fn mark_value_false(&mut self, value: &Value);
|
fn mark_value_false(&mut self, value: Value);
|
||||||
// mark a value as correct
|
// mark a value as correct
|
||||||
fn mark_value_true(&mut self, value: &Value) {
|
fn mark_value_true(&mut self, value: Value) {
|
||||||
for other_value in VALUES.iter() {
|
for &other_value in VALUES.iter() {
|
||||||
if other_value != value {
|
if other_value != value {
|
||||||
self.mark_value_false(other_value);
|
self.mark_value_false(other_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn mark_value(&mut self, value: &Value, is_value: bool) {
|
fn mark_value(&mut self, value: Value, is_value: bool) {
|
||||||
if is_value {
|
if is_value {
|
||||||
self.mark_value_true(value);
|
self.mark_value_true(value);
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,7 +118,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 {
|
pub trait Info<T> where T: Hash + Eq + Clone + Copy {
|
||||||
// get all a-priori possibilities
|
// get all a-priori possibilities
|
||||||
fn get_all_possibilities() -> Vec<T>;
|
fn get_all_possibilities() -> Vec<T>;
|
||||||
|
|
||||||
|
@ -141,8 +132,8 @@ pub trait Info<T> where T: Hash + Eq + Clone {
|
||||||
self.get_possibility_set().iter().map(|t| t.clone()).collect::<Vec<T>>()
|
self.get_possibility_set().iter().map(|t| t.clone()).collect::<Vec<T>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_possible(&self, value: &T) -> bool {
|
fn is_possible(&self, value: T) -> bool {
|
||||||
self.get_possibility_set().contains(value)
|
self.get_possibility_set().contains(&value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize() -> HashSet<T> {
|
fn initialize() -> HashSet<T> {
|
||||||
|
@ -153,17 +144,17 @@ pub trait Info<T> where T: Hash + Eq + Clone {
|
||||||
possible_map
|
possible_map
|
||||||
}
|
}
|
||||||
|
|
||||||
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.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_false(&mut self, value: &T) {
|
fn mark_false(&mut self, value: T) {
|
||||||
self.get_mut_possibility_set().remove(value);
|
self.get_mut_possibility_set().remove(&value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark(&mut self, value: &T, info: bool) {
|
fn mark(&mut self, value: T, info: bool) {
|
||||||
if info {
|
if info {
|
||||||
self.mark_true(value);
|
self.mark_true(value);
|
||||||
} else {
|
} else {
|
||||||
|
@ -220,30 +211,30 @@ impl CardInfo for SimpleCardInfo {
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
fn is_possible(&self, card: &Card) -> bool {
|
fn is_possible(&self, card: &Card) -> bool {
|
||||||
self.color_info.is_possible(&card.color) &&
|
self.color_info.is_possible(card.color) &&
|
||||||
self.value_info.is_possible(&card.value)
|
self.value_info.is_possible(card.value)
|
||||||
|
|
||||||
}
|
}
|
||||||
fn mark_color_false(&mut self, color: &Color) {
|
fn mark_color_false(&mut self, color: Color) {
|
||||||
self.color_info.mark_false(color);
|
self.color_info.mark_false(color);
|
||||||
|
|
||||||
}
|
}
|
||||||
fn mark_value_false(&mut self, value: &Value) {
|
fn mark_value_false(&mut self, value: Value) {
|
||||||
self.value_info.mark_false(value);
|
self.value_info.mark_false(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for SimpleCardInfo {
|
impl fmt::Display for SimpleCardInfo {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut string = String::new();
|
let mut string = String::new();
|
||||||
for color in &COLORS {
|
for &color in &COLORS {
|
||||||
if self.color_info.is_possible(color) {
|
if self.color_info.is_possible(color) {
|
||||||
string.push(display_color(color));
|
string.push(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// while string.len() < COLORS.len() + 1 {
|
// while string.len() < COLORS.len() + 1 {
|
||||||
string.push(' ');
|
string.push(' ');
|
||||||
//}
|
//}
|
||||||
for value in &VALUES {
|
for &value in &VALUES {
|
||||||
if self.value_info.is_possible(value) {
|
if self.value_info.is_possible(value) {
|
||||||
string.push_str(&format!("{}", value));
|
string.push_str(&format!("{}", value));
|
||||||
}
|
}
|
||||||
|
@ -346,15 +337,15 @@ impl CardInfo for CardPossibilityTable {
|
||||||
cards.sort();
|
cards.sort();
|
||||||
cards
|
cards
|
||||||
}
|
}
|
||||||
fn mark_color_false(&mut self, color: &Color) {
|
fn mark_color_false(&mut self, color: Color) {
|
||||||
for &value in VALUES.iter() {
|
for &value in VALUES.iter() {
|
||||||
self.mark_false(&Card::new(color, value));
|
self.mark_false(&Card::new(color, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
fn mark_value_false(&mut self, value: &Value) {
|
fn mark_value_false(&mut self, value: Value) {
|
||||||
for &color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
self.mark_false(&Card::new(color, value.clone()));
|
self.mark_false(&Card::new(color, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_weight(&self, card: &Card) -> f32 {
|
fn get_weight(&self, card: &Card) -> f32 {
|
||||||
|
|
|
@ -27,7 +27,7 @@ impl ModulusInformation {
|
||||||
self.modulus = self.modulus * other.modulus;
|
self.modulus = self.modulus * other.modulus;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit(&mut self, modulus: u32) -> Self {
|
pub fn split(&mut self, modulus: u32) -> Self {
|
||||||
assert!(self.modulus >= modulus);
|
assert!(self.modulus >= modulus);
|
||||||
assert!(self.modulus % modulus == 0);
|
assert!(self.modulus % modulus == 0);
|
||||||
let original_modulus = self.modulus;
|
let original_modulus = self.modulus;
|
||||||
|
@ -68,16 +68,17 @@ trait Question {
|
||||||
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, &OwnedGameView) -> u32;
|
fn answer(&self, &Cards, &OwnedGameView) -> u32;
|
||||||
|
// process the answer to this question, updating card info
|
||||||
|
fn acknowledge_answer(
|
||||||
|
&self, value: u32, &mut Vec<CardPossibilityTable>, &OwnedGameView
|
||||||
|
);
|
||||||
|
|
||||||
fn answer_info(&self, hand: &Cards, view: &OwnedGameView) -> ModulusInformation {
|
fn answer_info(&self, hand: &Cards, view: &OwnedGameView) -> ModulusInformation {
|
||||||
ModulusInformation::new(
|
ModulusInformation::new(
|
||||||
self.info_amount(),
|
self.info_amount(),
|
||||||
self.answer(hand, view)
|
self.answer(hand, view)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// process the answer to this question, updating card info
|
|
||||||
fn acknowledge_answer(
|
|
||||||
&self, value: u32, &mut Vec<CardPossibilityTable>, &OwnedGameView
|
|
||||||
);
|
|
||||||
|
|
||||||
fn acknowledge_answer_info(
|
fn acknowledge_answer_info(
|
||||||
&self,
|
&self,
|
||||||
|
@ -353,7 +354,7 @@ impl InformationPlayerStrategy {
|
||||||
{
|
{
|
||||||
let view = &self.last_view;
|
let view = &self.last_view;
|
||||||
for question in questions {
|
for question in questions {
|
||||||
let answer_info = hint.emit(question.info_amount());
|
let answer_info = hint.split(question.info_amount());
|
||||||
question.acknowledge_answer_info(answer_info, &mut hand_info, view);
|
question.acknowledge_answer_info(answer_info, &mut hand_info, view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,12 +488,12 @@ impl InformationPlayerStrategy {
|
||||||
let mut info = self.get_player_public_info_mut(&hint.player);
|
let mut info = self.get_player_public_info_mut(&hint.player);
|
||||||
let zip_iter = info.iter_mut().zip(matches);
|
let zip_iter = info.iter_mut().zip(matches);
|
||||||
match hint.hinted {
|
match hint.hinted {
|
||||||
Hinted::Color(ref color) => {
|
Hinted::Color(color) => {
|
||||||
for (card_info, matched) in zip_iter {
|
for (card_info, matched) in zip_iter {
|
||||||
card_info.mark_color(color, *matched);
|
card_info.mark_color(color, *matched);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Hinted::Value(ref value) => {
|
Hinted::Value(value) => {
|
||||||
for (card_info, matched) in zip_iter {
|
for (card_info, matched) in zip_iter {
|
||||||
card_info.mark_value(value, *matched);
|
card_info.mark_value(value, *matched);
|
||||||
}
|
}
|
||||||
|
@ -520,6 +521,9 @@ impl InformationPlayerStrategy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: decrement weight counts for fully determined cards,
|
||||||
|
// ahead of time
|
||||||
|
|
||||||
// note: other_player could be player, as well
|
// note: other_player could be player, as well
|
||||||
// in particular, we will decrement the newly drawn card
|
// in particular, we will decrement the newly drawn card
|
||||||
for other_player in view.board.get_players() {
|
for other_player in view.board.get_players() {
|
||||||
|
@ -574,6 +578,16 @@ impl InformationPlayerStrategy {
|
||||||
|
|
||||||
fn get_hint(&self) -> TurnChoice {
|
fn get_hint(&self) -> TurnChoice {
|
||||||
let view = &self.last_view;
|
let view = &self.last_view;
|
||||||
|
|
||||||
|
// Can give up to 3(n-1) hints
|
||||||
|
// For any given player with at least 4 cards, and index i, there are at least 3 hints that can be given.
|
||||||
|
// 0. a value hint on card i
|
||||||
|
// 1. a color hint on card i
|
||||||
|
// 2. any hint not involving card i
|
||||||
|
|
||||||
|
// TODO: make it so space of hints is larger when there is
|
||||||
|
// knowledge about the cards?
|
||||||
|
|
||||||
let total_info = 3 * (view.board.num_players - 1);
|
let total_info = 3 * (view.board.num_players - 1);
|
||||||
|
|
||||||
let hint_info = self.get_hint_sum_info(total_info, view);
|
let hint_info = self.get_hint_sum_info(total_info, view);
|
||||||
|
@ -587,13 +601,6 @@ impl InformationPlayerStrategy {
|
||||||
let card_index = self.get_index_for_hint(self.get_player_public_info(&hint_player), view);
|
let card_index = self.get_index_for_hint(self.get_player_public_info(&hint_player), view);
|
||||||
let hint_card = &hand[card_index];
|
let hint_card = &hand[card_index];
|
||||||
|
|
||||||
// For any given player with at least 4 cards, and index i, there are at least 3 hints that can be given.
|
|
||||||
// 0. a value hint on card i
|
|
||||||
// 1. a color hint on card i
|
|
||||||
// 2. any hint not involving card i
|
|
||||||
//
|
|
||||||
// for 4 players, can give 6 distinct hints
|
|
||||||
|
|
||||||
|
|
||||||
let hinted = match hint_type {
|
let hinted = match hint_type {
|
||||||
0 => {
|
0 => {
|
||||||
|
|
Loading…
Reference in a new issue