[][src]Trait rubot::Game

pub trait Game: Clone {
    type Player: Copy;
    type Action: PartialEq + Clone;
    type Fitness: Ord + Copy;
    type Actions: IntoIterator<Item = Self::Action>;
    fn actions(&self, player: Self::Player) -> (bool, Self::Actions);
fn execute(
        &mut self,
        action: &Self::Action,
        player: Self::Player
    ) -> Self::Fitness; fn look_ahead(
        &self,
        action: &Self::Action,
        player: Self::Player
    ) -> Self::Fitness { ... }
fn is_upper_bound(
        &self,
        fitness: Self::Fitness,
        player: Self::Player
    ) -> bool { ... }
fn is_lower_bound(
        &self,
        fitness: Self::Fitness,
        player: Self::Player
    ) -> bool { ... } }

An interface required to interact with GameBots.

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.

Loading content...

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

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.

Loading content...

Provided methods

fn look_ahead(
    &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.

Loading content...

Implementors

impl Game for Node[src]

type Player = bool

type Action = usize

type Fitness = i8

type Actions = Range<usize>

Loading content...