[−][src]Trait rubot::Game
An interface required to interact with GameBot
s.
Examples
Implementing this trait for 21 flags
. The game has the following rules:
- at the beginning there are 21 flags.
- 2 players draw 1, 2 or 3 flags in alternating turns
- the player who removes the last flag wins
This example is really simplified and should be viewed as such.
For more realistic examples visit the /examples
folder of this project.
use std::{ ops::{Not, RangeInclusive}, time::Duration }; #[derive(Clone)] struct Game { flags: u32, active_player: Player } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Player { A, B } impl Not for Player { type Output = Player; fn not(self) -> Player { match self { Player::A => Player::B, Player::B => Player::A, } } } impl Game { fn remove_flags(&mut self, flags: u32) { self.flags -= flags; self.active_player = !self.active_player; } fn winner(&self) -> Player { assert_eq!(self.flags, 0); !self.active_player } } impl rubot::Game for Game { type Player = Player; type Action = u32; /// `true` if the player wins the game, `false` otherwise. type Fitness = bool; type Actions = RangeInclusive<u32>; fn actions(&self, player: Self::Player) -> (bool, Self::Actions) { (player == self.active_player, 1..=std::cmp::min(self.flags, 3)) } fn execute(&mut self, action: &Self::Action, player: Self::Player) -> Self::Fitness { (action, player, &self); self.remove_flags(*action); self.flags == 0 && player == self.winner() } /// The fitness is only `true` if the game is won. fn is_upper_bound(&self, fitness: Self::Fitness, player: Self::Player) -> bool { fitness } } fn main() { use rubot::{Bot, ToCompletion}; let mut player_a = Bot::new(Player::A); let mut player_b = Bot::new(Player::B); let mut game = Game { flags: 21, active_player: Player::A }; loop { game.remove_flags(player_a.select(&game, ToCompletion).unwrap()); if game.flags == 0 { break } game.remove_flags(player_b.select(&game, ToCompletion).unwrap()); if game.flags == 0 { break } } // In case both players play perfectly, the player who begins should always win. assert_eq!(game.winner(), Player::A, "players are not playing optimally"); }
Template
A template which can be used to implement this trait more quickly.
#[derive(Clone)] struct PlaceholderGame; #[derive(Clone, Copy)] struct PlaceholderPlayer; #[derive(Clone, PartialEq)] struct PlaceholderAction; #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] struct PlaceholderFitness; impl rubot::Game for PlaceholderGame { type Player = PlaceholderPlayer; type Action = PlaceholderAction; type Fitness = PlaceholderFitness; type Actions = Vec<Self::Action>; fn actions(&self, player: Self::Player) -> (bool, Self::Actions) { unimplemented!(); } fn execute(&mut self, action: &Self::Action, player: Self::Player) -> Self::Fitness { unimplemented!(); } }
Associated Types
type Player: Copy
The player type.
type Action: PartialEq + Clone
An executable action.
type Fitness: Ord + Copy
The fitness of a state.
type Actions: IntoIterator<Item = Self::Action>
The collection returned by actions
.
Required methods
fn actions(&self, player: Self::Player) -> (bool, Self::Actions)
Returns all currently possible actions and if they are executed by the given player
.
fn execute(
&mut self,
action: &Self::Action,
player: Self::Player
) -> Self::Fitness
&mut self,
action: &Self::Action,
player: Self::Player
) -> Self::Fitness
Execute a given action
, returning the new fitness
for the given player
.
The returned fitness is always from the perspective of player
,
even if the player
is not active.
A correctly implemented GameBot
will only call this function with
actions generated by actions
.
Provided methods
fn look_ahead(
&self,
action: &Self::Action,
player: Self::Player
) -> Self::Fitness
&self,
action: &Self::Action,
player: Self::Player
) -> Self::Fitness
Returns the fitness after action
is executed.
The returned fitness is always from the perspective of player
,
even if the player
is not active.
This function should always return the same Fitness
as calling execute
.
let mut state = GameState; let look_ahead = state.look_ahead(&action, player); let execute = state.execute(&action, player); assert_eq!(look_ahead, execute);
fn is_upper_bound(&self, fitness: Self::Fitness, player: Self::Player) -> bool
Returns true
if the given fitness
is one of the best currently possible outcomes for the given player
.
A good example is a checkmate in chess, as there does not exist a better game state than having won.
fn is_lower_bound(&self, fitness: Self::Fitness, player: Self::Player) -> bool
Returns true
if the given fitness
is one of the worst currently possible outcomes for the given player
.
A good example is a checkmate in chess, as there does not exist a worse game state than having lost.