#[cfg(doc)]
use crate::Game;
use rayon::ThreadPoolBuildError;
use std::error::Error;
use std::fmt::{Display, Error as FmtError, Formatter};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum GameError {
EmptyChance,
NonPositiveChance,
ProbabilitiesNotEqual,
ImperfectRecall,
EmptyPlayer,
ActionsNotEqual,
ActionsNotUnique,
}
impl Display for GameError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(fmt, "{:?}", self)
}
}
impl Error for GameError {}
#[cfg(test)]
mod game_errors {
use crate::{Game, GameError, GameNode, IntoGameNode, PlayerNum};
struct Node(GameNode<Node>);
impl IntoGameNode for Node {
type PlayerInfo = &'static str;
type Action = &'static str;
type ChanceInfo = &'static str;
type Outcomes = Vec<(f64, Node)>;
type Actions = Vec<(&'static str, Node)>;
fn into_game_node(self) -> GameNode<Self> {
self.0
}
}
#[test]
fn empty_chance() {
let err_game = Node(GameNode::Chance(None, [].into()));
let err = Game::from_root(err_game).unwrap_err();
assert_eq!(err, GameError::EmptyChance);
}
#[test]
fn non_positive_chance() {
let err_game = Node(GameNode::Chance(
None,
[(0.0, Node(GameNode::Terminal(0.0)))].into(),
));
let err = Game::from_root(err_game).unwrap_err();
assert_eq!(err, GameError::NonPositiveChance);
}
#[test]
fn probabilities_not_equal() {
let err_game = Node(GameNode::Chance(
None,
vec![
(
0.5,
Node(GameNode::Chance(
Some("x"),
vec![
(1.0, Node(GameNode::Terminal(0.0))),
(1.0, Node(GameNode::Terminal(0.0))),
],
)),
),
(
0.5,
Node(GameNode::Chance(
Some("x"),
vec![
(1.0, Node(GameNode::Terminal(0.0))),
(2.0, Node(GameNode::Terminal(0.0))),
],
)),
),
],
));
let err = Game::from_root(err_game).unwrap_err();
assert_eq!(err, GameError::ProbabilitiesNotEqual);
}
#[test]
fn imperfect_recall() {
let err_game = Node(GameNode::Chance(
None,
vec![
(
0.5,
Node(GameNode::Player(
PlayerNum::One,
"x",
vec![
("a", Node(GameNode::Terminal(0.0))),
("b", Node(GameNode::Terminal(0.0))),
],
)),
),
(
0.5,
Node(GameNode::Player(
PlayerNum::One,
"y",
vec![
(
"a",
Node(GameNode::Player(
PlayerNum::One,
"x",
vec![
("a", Node(GameNode::Terminal(0.0))),
("b", Node(GameNode::Terminal(0.0))),
],
)),
),
("b", Node(GameNode::Terminal(0.0))),
],
)),
),
],
));
let err = Game::from_root(err_game).unwrap_err();
assert_eq!(err, GameError::ImperfectRecall);
}
#[test]
fn empty_player() {
let err_game = Node(GameNode::Player(PlayerNum::One, "", [].into()));
let err = Game::from_root(err_game).unwrap_err();
assert_eq!(err, GameError::EmptyPlayer);
}
#[test]
fn actions_not_equal() {
let ord_game = Node(GameNode::Chance(
None,
vec![
(
0.5,
Node(GameNode::Player(
PlayerNum::One,
"x",
vec![
("a", Node(GameNode::Terminal(0.0))),
("b", Node(GameNode::Terminal(0.0))),
],
)),
),
(
0.5,
Node(GameNode::Player(
PlayerNum::One,
"x",
vec![
("b", Node(GameNode::Terminal(0.0))),
("a", Node(GameNode::Terminal(0.0))),
],
)),
),
],
));
let err = Game::from_root(ord_game).unwrap_err();
assert_eq!(err, GameError::ActionsNotEqual);
}
#[test]
fn actions_not_unique() {
let dup_game = Node(GameNode::Player(
PlayerNum::One,
"x",
vec![
("a", Node(GameNode::Terminal(0.0))),
("a", Node(GameNode::Terminal(0.0))),
],
));
let err = Game::from_root(dup_game).unwrap_err();
assert_eq!(err, GameError::ActionsNotUnique);
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum StratError {
InvalidInfoset,
InvalidAction,
InvalidProbability,
UninitializedInfoset,
}
impl Display for StratError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(fmt, "{:?}", self)
}
}
impl Error for StratError {}
#[cfg(test)]
mod strat_errors {
use crate::{Game, GameNode, IntoGameNode, PlayerNum, StratError};
struct Node(GameNode<Node>);
impl IntoGameNode for Node {
type PlayerInfo = &'static str;
type Action = &'static str;
type ChanceInfo = &'static str;
type Outcomes = Vec<(f64, Node)>;
type Actions = Vec<(&'static str, Node)>;
fn into_game_node(self) -> GameNode<Self> {
self.0
}
}
fn create_game() -> Game<&'static str, &'static str> {
let node = Node(GameNode::Player(
PlayerNum::One,
"x",
vec![(
"a",
Node(GameNode::Player(
PlayerNum::Two,
"z",
vec![
(
"b",
Node(GameNode::Player(
PlayerNum::One,
"y",
vec![
("c", Node(GameNode::Terminal(0.0))),
("d", Node(GameNode::Terminal(0.0))),
],
)),
),
("c", Node(GameNode::Terminal(0.0))),
],
)),
)],
));
Game::from_root(node).unwrap()
}
#[test]
fn invalid_infoset() {
let game = create_game();
let err = game
.from_named([vec![("a", vec![("b", 1.0)])], vec![]])
.unwrap_err();
assert_eq!(err, StratError::InvalidInfoset);
let err = game
.from_named_eq([vec![("a", vec![("b", 1.0)])], vec![]])
.unwrap_err();
assert_eq!(err, StratError::InvalidInfoset);
}
#[test]
fn invalid_infoset_action() {
let game = create_game();
let err = game
.from_named([vec![("x", vec![("b", 1.0)])], vec![]])
.unwrap_err();
assert_eq!(err, StratError::InvalidAction);
let err = game
.from_named_eq([vec![("x", vec![("b", 1.0)])], vec![]])
.unwrap_err();
assert_eq!(err, StratError::InvalidAction);
}
#[test]
fn invalid_probability() {
let game = create_game();
let err = game
.from_named([vec![("x", vec![("a", -1.0)])], vec![]])
.unwrap_err();
assert_eq!(err, StratError::InvalidProbability);
let err = game
.from_named_eq([vec![("x", vec![("a", -1.0)])], vec![]])
.unwrap_err();
assert_eq!(err, StratError::InvalidProbability);
}
#[test]
fn invalid_infoset_probability_normal() {
let game = create_game();
let err = game
.from_named([vec![("x", vec![("a", 1.0)])], vec![("z", vec![("c", 1.0)])]])
.unwrap_err();
assert_eq!(err, StratError::UninitializedInfoset);
let err = game
.from_named_eq([vec![("x", vec![("a", 1.0)])], vec![("z", vec![("c", 1.0)])]])
.unwrap_err();
assert_eq!(err, StratError::UninitializedInfoset);
}
#[test]
fn invalid_infoset_probability_single() {
let game = create_game();
let err = game
.from_named([vec![("y", vec![("d", 1.0)])], vec![("z", vec![("c", 1.0)])]])
.unwrap_err();
assert_eq!(err, StratError::UninitializedInfoset);
let err = game
.from_named_eq([vec![("y", vec![("d", 1.0)])], vec![("z", vec![("c", 1.0)])]])
.unwrap_err();
assert_eq!(err, StratError::UninitializedInfoset);
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum SolveError {
ThreadOverflow,
ThreadSpawnError,
}
impl Display for SolveError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(fmt, "{:?}", self)
}
}
impl Error for SolveError {}
impl From<ThreadPoolBuildError> for SolveError {
fn from(_: ThreadPoolBuildError) -> Self {
SolveError::ThreadSpawnError
}
}
#[cfg(test)]
mod solve_errors {
use crate::{Game, GameNode, IntoGameNode, SolveError, SolveMethod};
struct Node(GameNode<Node>);
impl IntoGameNode for Node {
type PlayerInfo = &'static str;
type Action = &'static str;
type ChanceInfo = &'static str;
type Outcomes = Vec<(f64, Node)>;
type Actions = Vec<(&'static str, Node)>;
fn into_game_node(self) -> GameNode<Self> {
self.0
}
}
#[test]
fn test_thread_overflow() {
let game = Game::from_root(Node(GameNode::Terminal(0.0))).unwrap();
let err = game
.solve(SolveMethod::Full, 0, 0.0, usize::MAX, None)
.unwrap_err();
assert_eq!(err, SolveError::ThreadOverflow);
}
}