ai-hex 0.1.0

A small Hex rules and Monte Carlo tree search engine
Documentation
use crate::connectivity::neighbors;
use crate::{Cell, Color, Position};

pub(crate) fn bridge_responses(
    position: Position,
    color: Color,
    attacked: Cell,
) -> ([Cell; 6], usize) {
    let mut endpoints = [attacked; 6];
    let mut endpoint_count = 0;
    for cell in neighbors(attacked, position.size()).into_iter().flatten() {
        if position.color_at(cell) == Some(color) {
            endpoints[endpoint_count] = cell;
            endpoint_count += 1;
        }
    }
    let mut responses = [attacked; 6];
    let mut count = 0;

    for left in 0..endpoint_count {
        for right in left + 1..endpoint_count {
            if are_neighbors(endpoints[left], endpoints[right], position) {
                continue;
            }
            for candidate in neighbors(endpoints[left], position.size())
                .into_iter()
                .flatten()
            {
                if candidate == attacked
                    || position.color_at(candidate).is_some()
                    || !are_neighbors(candidate, endpoints[right], position)
                    || responses[..count].contains(&candidate)
                {
                    continue;
                }
                debug_assert!(count < responses.len());
                responses[count] = candidate;
                count += 1;
            }
        }
    }
    (responses, count)
}

fn are_neighbors(left: Cell, right: Cell, position: Position) -> bool {
    neighbors(left, position.size()).contains(&Some(right))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{BoardSize, Move, SwapRule};

    fn cell(name: &str) -> Cell {
        name.parse::<Move>().unwrap().cell().unwrap()
    }

    #[test]
    fn finds_the_other_carrier_of_an_interior_bridge() {
        let moves = ["d4", "a1", "e5", "e4"].map(|mv| mv.parse().expect("valid move"));
        let position =
            Position::from_moves(BoardSize::new(9).unwrap(), SwapRule::Disabled, &moves).unwrap();
        let attacked = cell("e4");

        let (responses, count) = bridge_responses(position, Color::Red, attacked);

        assert_eq!(&responses[..count], &[cell("d5")]);
    }
}