arimaa-engine 0.1.0

A bitboard-based Arimaa game engine: move generation, game state, and Zobrist hashing.
Documentation
use anyhow::anyhow;
use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;

#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
#[derive(Default)]
pub enum Direction {
    #[default]
    Up,
    Right,
    Down,
    Left,
}


#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
#[derive(Default)]
pub enum MoveDirection {
    #[default]
    N,
    E,
    S,
    W,
    NN,
    NE,
    NW,
    EE,
    ES,
    SS,
    SW,
    WW,
    NNN,
    NNE,
    NNW,
    NEE,
    NWW,
    EEE,
    EES,
    ESS,
    SSS,
    SSW,
    SWW,
    WWW,
    NNNN,
    NNNE,
    NNNW,
    NNEE,
    NNWW,
    NEEE,
    NWWW,
    EEEE,
    EEES,
    EESS,
    ESSS,
    SSSS,
    SSSW,
    SSWW,
    SWWW,
    WWWW,
}

impl MoveDirection {
    #[rustfmt::skip]
    pub fn directions(&self) -> Vec<Direction> {
        match self {
            MoveDirection::N    => vec![Direction::Up   ],
            MoveDirection::E    => vec![Direction::Right],
            MoveDirection::S    => vec![Direction::Down ],
            MoveDirection::W    => vec![Direction::Left ],
            MoveDirection::NN   => vec![Direction::Up,    Direction::Up   ],
            MoveDirection::NE   => vec![Direction::Up,    Direction::Right],
            MoveDirection::NW   => vec![Direction::Up,    Direction::Left ],
            MoveDirection::EE   => vec![Direction::Right, Direction::Right],
            MoveDirection::ES   => vec![Direction::Right, Direction::Down ],
            MoveDirection::SS   => vec![Direction::Down,  Direction::Down ],
            MoveDirection::SW   => vec![Direction::Down,  Direction::Left ],
            MoveDirection::WW   => vec![Direction::Left,  Direction::Left ],
            MoveDirection::NNN  => vec![Direction::Up,    Direction::Up,    Direction::Up   ],
            MoveDirection::NNE  => vec![Direction::Up,    Direction::Up,    Direction::Right],
            MoveDirection::NNW  => vec![Direction::Up,    Direction::Up,    Direction::Left ],
            MoveDirection::NEE  => vec![Direction::Up,    Direction::Right, Direction::Right],
            MoveDirection::NWW  => vec![Direction::Up,    Direction::Left,  Direction::Left ],
            MoveDirection::EEE  => vec![Direction::Right, Direction::Right, Direction::Right],
            MoveDirection::EES  => vec![Direction::Right, Direction::Right, Direction::Down ],
            MoveDirection::ESS  => vec![Direction::Right, Direction::Down,  Direction::Down ],
            MoveDirection::SSS  => vec![Direction::Down,  Direction::Down,  Direction::Down ],
            MoveDirection::SSW  => vec![Direction::Down,  Direction::Down,  Direction::Left ],
            MoveDirection::SWW  => vec![Direction::Down,  Direction::Left,  Direction::Left ],
            MoveDirection::WWW  => vec![Direction::Left,  Direction::Left,  Direction::Left ],
            MoveDirection::NNNN => vec![Direction::Up,    Direction::Up,    Direction::Up,    Direction::Up   ],
            MoveDirection::NNNE => vec![Direction::Up,    Direction::Up,    Direction::Up,    Direction::Right],
            MoveDirection::NNNW => vec![Direction::Up,    Direction::Up,    Direction::Up,    Direction::Left ],
            MoveDirection::NNEE => vec![Direction::Up,    Direction::Up,    Direction::Right, Direction::Right],
            MoveDirection::NNWW => vec![Direction::Up,    Direction::Up,    Direction::Left,  Direction::Left ],
            MoveDirection::NEEE => vec![Direction::Up,    Direction::Right, Direction::Right, Direction::Right],
            MoveDirection::NWWW => vec![Direction::Up,    Direction::Left,  Direction::Left,  Direction::Left ],
            MoveDirection::EEEE => vec![Direction::Right, Direction::Right, Direction::Right, Direction::Right],
            MoveDirection::EEES => vec![Direction::Right, Direction::Right, Direction::Right, Direction::Down ],
            MoveDirection::EESS => vec![Direction::Right, Direction::Right, Direction::Down,  Direction::Down ],
            MoveDirection::ESSS => vec![Direction::Right, Direction::Down,  Direction::Down,  Direction::Down ],
            MoveDirection::SSSS => vec![Direction::Down,  Direction::Down,  Direction::Down,  Direction::Down ],
            MoveDirection::SSSW => vec![Direction::Down,  Direction::Down,  Direction::Down,  Direction::Left ],
            MoveDirection::SSWW => vec![Direction::Down,  Direction::Down,  Direction::Left,  Direction::Left ],
            MoveDirection::SWWW => vec![Direction::Down,  Direction::Left,  Direction::Left,  Direction::Left ],
            MoveDirection::WWWW => vec![Direction::Left,  Direction::Left,  Direction::Left,  Direction::Left ],
        }
    }

    pub fn move_directions() -> Vec<MoveDirection> {
        vec![
            MoveDirection::N,
            MoveDirection::E,
            MoveDirection::S,
            MoveDirection::W,
            MoveDirection::NN,
            MoveDirection::NE,
            MoveDirection::NW,
            MoveDirection::EE,
            MoveDirection::ES,
            MoveDirection::SS,
            MoveDirection::SW,
            MoveDirection::WW,
            MoveDirection::NNN,
            MoveDirection::NNE,
            MoveDirection::NNW,
            MoveDirection::NEE,
            MoveDirection::NWW,
            MoveDirection::EEE,
            MoveDirection::EES,
            MoveDirection::ESS,
            MoveDirection::SSS,
            MoveDirection::SSW,
            MoveDirection::SWW,
            MoveDirection::WWW,
            MoveDirection::NNNN,
            MoveDirection::NNNE,
            MoveDirection::NNNW,
            MoveDirection::NNEE,
            MoveDirection::NNWW,
            MoveDirection::NEEE,
            MoveDirection::NWWW,
            MoveDirection::EEEE,
            MoveDirection::EEES,
            MoveDirection::EESS,
            MoveDirection::ESSS,
            MoveDirection::SSSS,
            MoveDirection::SSSW,
            MoveDirection::SSWW,
            MoveDirection::SWWW,
            MoveDirection::WWWW,
        ]
    }
}

impl TryFrom<&[Direction]> for MoveDirection {
    type Error = anyhow::Error;

    fn try_from(dirs: &[Direction]) -> Result<Self, Self::Error> {
        let vector = |dirs: &[Direction]| -> (i8, i8) {
            dirs.iter().fold((0, 0), |(mut x, mut y), dir| {
                match dir {
                    Direction::Up => y += 1,
                    Direction::Right => x += 1,
                    Direction::Down => y -= 1,
                    Direction::Left => x -= 1,
                };
                (x, y)
            })
        };

        let dir_vector = vector(dirs);

        MoveDirection::iter()
            .find(|move_direction| vector(&move_direction.directions()) == dir_vector)
            .ok_or_else(|| anyhow!("No matching MoveDirection for {:?}", dirs))
    }
}


impl Direction {
    pub fn invert(&self) -> Self {
        match self {
            Direction::Up => Direction::Down,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
            Direction::Left => Direction::Right,
        }
    }

    pub fn vertical_symmetry(&self) -> Self {
        match self {
            Direction::Up => Direction::Up,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Down,
            Direction::Left => Direction::Right,
        }
    }
}

impl TryFrom<char> for Direction {
    type Error = anyhow::Error;

    fn try_from(c: char) -> Result<Direction, anyhow::Error> {
        match c {
            'n' => Ok(Direction::Up),
            'e' => Ok(Direction::Right),
            's' => Ok(Direction::Down),
            'w' => Ok(Direction::Left),
            _ => Err(anyhow!("Invalid character for Direction")),
        }
    }
}

impl fmt::Display for Direction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let dir = match self {
            Direction::Up => 'n',
            Direction::Right => 'e',
            Direction::Down => 's',
            Direction::Left => 'w',
        };

        write!(f, "{}", dir)
    }
}

impl FromStr for Direction {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut chars = s.chars();
        match chars.next() {
            Some(c) => match chars.next() {
                None => Direction::try_from(c),
                Some(_) => Err(anyhow!("More than one char found for Direction: {}", s)),
            },
            None => Err(anyhow!("No char found for Direction: {}", s)),
        }
    }
}