allow for probabilities in cardinfo
- helper functions for using probabilistic card info - move firework to cards, re-export everything in game.rs
This commit is contained in:
parent
9c580ecb88
commit
4fa72e030f
4 changed files with 147 additions and 90 deletions
48
src/cards.rs
48
src/cards.rs
|
@ -131,3 +131,51 @@ impl fmt::Display for Discard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Score = u32;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone)]
|
||||||
|
pub struct Firework {
|
||||||
|
pub color: Color,
|
||||||
|
pub top: Value,
|
||||||
|
}
|
||||||
|
impl Firework {
|
||||||
|
pub fn new(color: Color) -> Firework {
|
||||||
|
Firework {
|
||||||
|
color: color,
|
||||||
|
top: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn desired_value(&self) -> Option<Value> {
|
||||||
|
if self.complete() { None } else { Some(self.top + 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn score(&self) -> Score {
|
||||||
|
self.top
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete(&self) -> bool {
|
||||||
|
self.top == FINAL_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn place(&mut self, card: &Card) {
|
||||||
|
assert!(
|
||||||
|
card.color == self.color,
|
||||||
|
"Attempted to place card on firework of wrong color!"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
Some(card.value) == self.desired_value(),
|
||||||
|
"Attempted to place card of wrong value on firework!"
|
||||||
|
);
|
||||||
|
self.top = card.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Display for Firework {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
if self.complete() {
|
||||||
|
write!(f, "{} firework complete!", self.color)
|
||||||
|
} else {
|
||||||
|
write!(f, "{} firework at {}", self.color, self.top)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
84
src/game.rs
84
src/game.rs
|
@ -2,12 +2,11 @@ use rand::{self, Rng, SeedableRng};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use info::*;
|
pub use info::*;
|
||||||
use cards::*;
|
pub use cards::*;
|
||||||
|
|
||||||
pub type Player = u32;
|
pub type Player = u32;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(Debug,Clone)]
|
||||||
pub enum Hinted {
|
pub enum Hinted {
|
||||||
Color(Color),
|
Color(Color),
|
||||||
|
@ -292,6 +291,35 @@ impl BoardState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn probability_of_predicate<T>(
|
||||||
|
&self,
|
||||||
|
card_info: &T,
|
||||||
|
predicate: &Fn(&Self, &Card) -> bool
|
||||||
|
) -> f32 where T: CardInfo {
|
||||||
|
let mut total_weight = 0;
|
||||||
|
let mut playable_weight = 0;
|
||||||
|
for card in card_info.get_possibilities() {
|
||||||
|
let weight = card_info.get_weight(&card);
|
||||||
|
if predicate(&self, &card) {
|
||||||
|
playable_weight += weight;
|
||||||
|
}
|
||||||
|
total_weight += weight;
|
||||||
|
}
|
||||||
|
(playable_weight as f32) / (total_weight as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probability_is_playable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
||||||
|
self.probability_of_predicate(card_info, &Self::is_playable)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probability_is_dead<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
||||||
|
self.probability_of_predicate(card_info, &Self::is_dead)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn probability_is_dispensable<T>(&self, card_info: &T) -> f32 where T: CardInfo {
|
||||||
|
self.probability_of_predicate(card_info, &Self::is_dispensable)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_players(&self) -> Vec<Player> {
|
pub fn get_players(&self) -> Vec<Player> {
|
||||||
(0..self.num_players).collect::<Vec<_>>()
|
(0..self.num_players).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
@ -422,8 +450,6 @@ impl fmt::Display for GameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Score = u32;
|
|
||||||
|
|
||||||
impl GameState {
|
impl GameState {
|
||||||
pub fn new(opts: &GameOptions, seed: u32) -> GameState {
|
pub fn new(opts: &GameOptions, seed: u32) -> GameState {
|
||||||
let mut board = BoardState::new(opts, seed);
|
let mut board = BoardState::new(opts, seed);
|
||||||
|
@ -569,51 +595,3 @@ impl GameState {
|
||||||
turn
|
turn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
|
||||||
pub struct Firework {
|
|
||||||
pub color: Color,
|
|
||||||
pub top: Value,
|
|
||||||
}
|
|
||||||
impl Firework {
|
|
||||||
pub fn new(color: Color) -> Firework {
|
|
||||||
Firework {
|
|
||||||
color: color,
|
|
||||||
top: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn desired_value(&self) -> Option<Value> {
|
|
||||||
if self.complete() { None } else { Some(self.top + 1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn score(&self) -> Score {
|
|
||||||
self.top
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complete(&self) -> bool {
|
|
||||||
self.top == FINAL_VALUE
|
|
||||||
}
|
|
||||||
|
|
||||||
fn place(&mut self, card: &Card) {
|
|
||||||
assert!(
|
|
||||||
card.color == self.color,
|
|
||||||
"Attempted to place card on firework of wrong color!"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
Some(card.value) == self.desired_value(),
|
|
||||||
"Attempted to place card of wrong value on firework!"
|
|
||||||
);
|
|
||||||
self.top = card.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl fmt::Display for Firework {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
if self.complete() {
|
|
||||||
write!(f, "{} firework complete!", self.color)
|
|
||||||
} else {
|
|
||||||
write!(f, "{} firework at {}", self.color, self.top)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
104
src/info.rs
104
src/info.rs
|
@ -5,6 +5,7 @@ use std::hash::Hash;
|
||||||
|
|
||||||
use cards::*;
|
use cards::*;
|
||||||
|
|
||||||
|
// trait representing information about a card
|
||||||
pub trait CardInfo {
|
pub trait CardInfo {
|
||||||
// get all a-priori possibilities
|
// get all a-priori possibilities
|
||||||
fn get_all_possibilities(&self) -> Vec<Card> {
|
fn get_all_possibilities(&self) -> Vec<Card> {
|
||||||
|
@ -16,8 +17,37 @@ pub trait CardInfo {
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
// whether the card is possible
|
||||||
|
fn is_possible(&self, card: &Card) -> bool;
|
||||||
|
|
||||||
|
// TODO: have a borrow_possibilities to allow for more efficiency?
|
||||||
|
|
||||||
// mark all current possibilities for the card
|
// mark all current possibilities for the card
|
||||||
fn get_possibilities(&self) -> Vec<Card>;
|
fn get_possibilities(&self) -> Vec<Card> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for &color in COLORS.iter() {
|
||||||
|
for &value in VALUES.iter() {
|
||||||
|
let card = Card::new(color, value);
|
||||||
|
if self.is_possible(&card) {
|
||||||
|
v.push(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
// get probability weight for the card
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn get_weight(&self, card: &Card) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
fn get_weighted_possibilities(&self) -> Vec<(Card, u32)> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
for card in self.get_possibilities() {
|
||||||
|
let weight = self.get_weight(&card);
|
||||||
|
v.push((card, weight));
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
@ -64,47 +94,34 @@ pub trait Info<T> where T: Hash + Eq + Clone {
|
||||||
|
|
||||||
// get map from values to whether it's possible
|
// get map from values to whether it's possible
|
||||||
// true means maybe, false means no
|
// true means maybe, false means no
|
||||||
fn get_possibility_map(&self) -> &HashMap<T, bool>;
|
fn get_possibility_set(&self) -> &HashSet<T>;
|
||||||
fn get_mut_possibility_map(&mut self) -> &mut HashMap<T, bool>;
|
fn get_mut_possibility_set(&mut self) -> &mut HashSet<T>;
|
||||||
|
|
||||||
// get what is now possible
|
// get what is now possible
|
||||||
fn get_possibilities(&self) -> Vec<T> {
|
fn get_possibilities(&self) -> Vec<T> {
|
||||||
let mut v = Vec::new();
|
self.get_possibility_set().iter().map(|t| t.clone()).collect::<Vec<T>>()
|
||||||
let map = self.get_possibility_map();
|
|
||||||
for (value, is_possible) in map {
|
|
||||||
if *is_possible {
|
|
||||||
v.push(value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_possible(&self, value: &T) -> bool {
|
fn is_possible(&self, value: &T) -> bool {
|
||||||
// self.get_possibility_map().contains_key(value)
|
self.get_possibility_set().contains(value)
|
||||||
*self.get_possibility_map().get(value).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize() -> HashMap<T, bool> {
|
fn initialize() -> HashSet<T> {
|
||||||
let mut possible_map : HashMap<T, bool> = HashMap::new();
|
let mut possible_map : HashSet<T> = HashSet::new();
|
||||||
for value in Self::get_all_possibilities().iter() {
|
for value in Self::get_all_possibilities().iter() {
|
||||||
possible_map.insert(value.clone(), true);
|
possible_map.insert(value.clone());
|
||||||
}
|
}
|
||||||
possible_map
|
possible_map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_true(&mut self, value: &T) {
|
fn mark_true(&mut self, value: &T) {
|
||||||
// mark everything else as definitively impossible
|
let possible = self.get_mut_possibility_set();
|
||||||
for (other_value, possible) in self.get_mut_possibility_map().iter_mut() {
|
possible.clear();
|
||||||
if other_value != value {
|
possible.insert(value.clone());
|
||||||
*possible = false;
|
|
||||||
} else {
|
|
||||||
assert_eq!(*possible, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_false(&mut self, value: &T) {
|
fn mark_false(&mut self, value: &T) {
|
||||||
self.get_mut_possibility_map().insert(value.clone(), false);
|
self.get_mut_possibility_set().remove(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark(&mut self, value: &T, info: bool) {
|
fn mark(&mut self, value: &T, info: bool) {
|
||||||
|
@ -117,25 +134,25 @@ pub trait Info<T> where T: Hash + Eq + Clone {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ColorInfo(HashMap<Color, bool>);
|
pub struct ColorInfo(HashSet<Color>);
|
||||||
impl ColorInfo {
|
impl ColorInfo {
|
||||||
pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) }
|
pub fn new() -> ColorInfo { ColorInfo(ColorInfo::initialize()) }
|
||||||
}
|
}
|
||||||
impl Info<Color> for ColorInfo {
|
impl Info<Color> for ColorInfo {
|
||||||
fn get_all_possibilities() -> Vec<Color> { COLORS.to_vec() }
|
fn get_all_possibilities() -> Vec<Color> { COLORS.to_vec() }
|
||||||
fn get_possibility_map(&self) -> &HashMap<Color, bool> { &self.0 }
|
fn get_possibility_set(&self) -> &HashSet<Color> { &self.0 }
|
||||||
fn get_mut_possibility_map(&mut self) -> &mut HashMap<Color, bool> { &mut self.0 }
|
fn get_mut_possibility_set(&mut self) -> &mut HashSet<Color> { &mut self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ValueInfo(HashMap<Value, bool>);
|
pub struct ValueInfo(HashSet<Value>);
|
||||||
impl ValueInfo {
|
impl ValueInfo {
|
||||||
pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) }
|
pub fn new() -> ValueInfo { ValueInfo(ValueInfo::initialize()) }
|
||||||
}
|
}
|
||||||
impl Info<Value> for ValueInfo {
|
impl Info<Value> for ValueInfo {
|
||||||
fn get_all_possibilities() -> Vec<Value> { VALUES.to_vec() }
|
fn get_all_possibilities() -> Vec<Value> { VALUES.to_vec() }
|
||||||
fn get_possibility_map(&self) -> &HashMap<Value, bool> { &self.0 }
|
fn get_possibility_set(&self) -> &HashSet<Value> { &self.0 }
|
||||||
fn get_mut_possibility_map(&mut self) -> &mut HashMap<Value, bool> { &mut self.0 }
|
fn get_mut_possibility_set(&mut self) -> &mut HashSet<Value> { &mut self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
// represents information only of the form:
|
// represents information only of the form:
|
||||||
|
@ -163,6 +180,11 @@ impl CardInfo for SimpleCardInfo {
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
fn is_possible(&self, card: &Card) -> bool {
|
||||||
|
self.color_info.is_possible(&card.color) &&
|
||||||
|
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);
|
||||||
|
|
||||||
|
@ -193,16 +215,20 @@ impl fmt::Display for SimpleCardInfo {
|
||||||
|
|
||||||
// Can represent information of the form:
|
// Can represent information of the form:
|
||||||
// this card is/isn't possible
|
// this card is/isn't possible
|
||||||
|
// also, maintains weights for the cards
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CardPossibilityTable {
|
pub struct CardPossibilityTable {
|
||||||
possible: HashSet<Card>,
|
possible: HashMap<Card, u32>,
|
||||||
}
|
}
|
||||||
impl CardPossibilityTable {
|
impl CardPossibilityTable {
|
||||||
pub fn new() -> CardPossibilityTable {
|
pub fn new() -> CardPossibilityTable {
|
||||||
let mut possible = HashSet::new();
|
let mut possible = HashMap::new();
|
||||||
for &color in COLORS.iter() {
|
for &color in COLORS.iter() {
|
||||||
for &value in VALUES.iter() {
|
for &value in VALUES.iter() {
|
||||||
possible.insert(Card::new(color, value));
|
possible.insert(
|
||||||
|
Card::new(color, value),
|
||||||
|
get_count_for_value(&value)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CardPossibilityTable {
|
CardPossibilityTable {
|
||||||
|
@ -216,8 +242,11 @@ impl CardPossibilityTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl CardInfo for CardPossibilityTable {
|
impl CardInfo for CardPossibilityTable {
|
||||||
|
fn is_possible(&self, card: &Card) -> bool {
|
||||||
|
self.possible.contains_key(card)
|
||||||
|
}
|
||||||
fn get_possibilities(&self) -> Vec<Card> {
|
fn get_possibilities(&self) -> Vec<Card> {
|
||||||
let mut cards = self.possible.iter().map(|card| {card.clone() }).collect::<Vec<_>>();
|
let mut cards = self.possible.keys().map(|card| {card.clone() }).collect::<Vec<_>>();
|
||||||
cards.sort();
|
cards.sort();
|
||||||
cards
|
cards
|
||||||
}
|
}
|
||||||
|
@ -232,6 +261,9 @@ impl CardInfo for CardPossibilityTable {
|
||||||
self.mark_false(&Card::new(color, value.clone()));
|
self.mark_false(&Card::new(color, value.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn get_weight(&self, card: &Card) -> u32 {
|
||||||
|
*self.possible.get(card).unwrap_or(&0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use simulator::*;
|
use simulator::*;
|
||||||
use game::*;
|
use game::*;
|
||||||
use cards::*;
|
|
||||||
|
|
||||||
// strategy that explicitly cheats by using Rc/RefCell
|
// strategy that explicitly cheats by using Rc/RefCell
|
||||||
// serves as a reference point for other strategies
|
// serves as a reference point for other strategies
|
||||||
|
|
Loading…
Reference in a new issue