<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use model::{Color, FieldCoord, HexCoord};

#[derive(Clone, Copy)]
pub struct Board {
    /*
                ____
               /    \
          ____/  +y  \____
         /    \      /    \
        /      \____/  +x  \
        \      /    \      /
         \____/      \____/
         /    \      /    \
        /  -x  \____/      \
        \      /    \      /
         \____/  -y  \____/
              \      /
               \____/

        The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
        up to the right, and the y-axis goes up and down. The board is stored as a dense 2D array,
        because a ragged array won't work (since each row would have to be a different type).

        See http://www.redblobgames.com/grids/hexagons/#coordinates-axial for more info.
    */
    board: [[Option<Hex>; 5]; 5],
    turn: Color,
    black_pieces: u32,
    black_hexes: u32,
    white_pieces: u32,
    white_hexes: u32,
}

// Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
type Hex = [Field; 6];

// There's no need to store the color of the piece on this field, since only white pieces can be on
// white fields, and vice versa.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Field {
    Piece,
    Empty,
}

// Public methods
impl Board {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Board {
        let mut board = Board {
            board: [[None; 5]; 5],
            turn: Color::White,
            black_pieces: 18,
            black_hexes: 0,
            white_pieces: 18,
            white_hexes: 0,
        };

        // (0, 0) is the only empty hex.
        board.set_hex(&HexCoord::new(0, 0), Some([Field::Empty; 6]));

        // All other hexes have exactly two pieces on them in the starting position.
        let piece_locations = [
            (-2,  2, 0, 4),
            (-2,  1, 0, 3),
            (-2,  0, 3, 5),
            (-1,  2, 1, 4),
            (-1,  1, 0, 4),
            (-1,  0, 3, 5),
            (-1, -1, 2, 5),
            ( 0,  2, 1, 5),
            ( 0,  1, 1, 5),
            ( 0, -1, 2, 4),
            ( 0, -2, 2, 4),
            ( 1,  1, 2, 5),
            ( 1,  0, 0, 2),
            ( 1, -1, 1, 3),
            ( 1, -2, 1, 4),
            ( 2,  0, 0, 2),
            ( 2, -1, 0, 3),
            ( 2, -2, 1, 3),
        ];

        for &(x, y, f1, f2) in &piece_locations {
            let mut hex = [Field::Empty; 6];
            hex[f1] = Field::Piece;
            hex[f2] = Field::Piece;
            board.set_hex(&HexCoord::new(x, y), Some(hex));
        }

        board
    }
    pub fn can_move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) -> bool {
        from.color() == self.turn && self.is_piece_on_field(from) && !self.is_piece_on_field(to)
            && self.get_field_vertex_neighbors(from).contains(to)
    }
    pub fn move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) {
        assert!(self.can_move_piece(from, to));

        self.set_field(from, Field::Empty);
        self.set_field(to, Field::Piece);

        let (capture_count, mut fields_to_check) = self.check_hexes(&from.to_hex());
        fields_to_check.append(&mut self.get_field_edge_neighbors(to));
        self.check_captures(&fields_to_check);

        match self.turn {
            Color::White => self.white_hexes += capture_count,
            Color::Black => self.black_hexes += capture_count,
        }
        self.turn = self.turn.switch();
    }
    pub fn get_available_moves(&self, field: &FieldCoord) -> Vec<FieldCoord> {
        if self.is_piece_on_field(field) {
            self.get_field_vertex_neighbors(field)
                .into_iter()
                .filter(|c| !self.is_piece_on_field(c))
                .collect()
        } else {
            vec![]
        }
    }
    pub fn can_exchange(&self) -> bool {
        2 <= match self.turn {
            Color::Black => self.black_hexes,
            Color::White => self.white_hexes,
        }
    }
    pub fn can_exchange_piece(&self, coord: &FieldCoord) -> bool {
        self.can_exchange() && coord.color() != self.turn && self.is_piece_on_field(coord)
    }
    pub fn exchange_piece(&mut self, coord: &FieldCoord) {
        assert!(self.can_exchange_piece(coord));

        self.remove_piece(coord);
        match self.turn {
            Color::White => self.white_hexes -= 2,
            Color::Black => self.black_hexes -= 2,
        }

        // Players don't collect hexes removed due to an exchange
        let (_, fields_to_check) = self.check_hexes(&coord.to_hex());
        self.check_captures(&fields_to_check);
        self.turn = self.turn.switch();
    }
    pub fn is_piece_on_field(&self, coord: &FieldCoord) -> bool {
        self.get_field(coord) == &Field::Piece
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Return the coordinates of the hexes that have not been removed yet.
    pub fn extant_hexes(&self) -> Vec<HexCoord> {
        let mut coords = vec![];
        for x in -2..3 {
            for y in -2..3 {
                coords.push((x, y));
            }
        }
        coords
            .iter()
            .filter_map(|&coord| self.try_hex(coord))
            .collect()
    }
    pub fn turn(&self) -> Color {
        self.turn
    }
    pub fn black_pieces(&self) -> u32 {
        self.black_pieces
    }
    pub fn black_hexes(&self) -> u32 {
        self.black_hexes
    }
    pub fn white_pieces(&self) -> u32 {
        self.white_pieces
    }
    pub fn white_hexes(&self) -> u32 {
        self.white_hexes
    }
}

// Field and piece methods
impl Board {
    fn get_field(&self, coord: &FieldCoord) -> &Field {
        match *self.get_hex(&coord.to_hex()) {
            Some(ref hex) => &hex[coord.f as usize],
            None => panic!(
                "Cannot get field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    fn set_field(&mut self, coord: &FieldCoord, field: Field) {
        match *self.get_hex(&coord.to_hex()) {
            Some(mut hex) => {
                hex[coord.f as usize] = field;
                self.set_hex(&coord.to_hex(), Some(hex));
            }
            None => panic!(
                "Cannot set field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    /// Return fields that share an edge with the given field. These fields are always the opposite
    /// color of the given field. If all of a piece's edge neighbors are occupied, that piece might
    /// be capturable.
    fn get_field_edge_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![
            // There are always two edge neighbors on the same hex as the given field
            FieldCoord::new(coord.x, coord.y, (coord.f + 1) % 6),
            FieldCoord::new(coord.x, coord.y, (coord.f + 5) % 6),
        ];

        if let Some(hex) = self.get_hex_neighbor(&coord.to_hex(), coord.f) {
            neighbors.push(hex.to_field((coord.f + 3) % 6));
        }

        neighbors
    }
    /// Return fields that share a vertex with the given field and have the same color as the given
    /// field. Pieces can move to fields that are vertex neighbors of the field they are on.
    fn get_field_vertex_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        // A field's vertex neighbors can be defined as the edge neighbors of its edge neighbors
        let mut neighbors = vec![];
        for field in self.get_field_edge_neighbors(coord) {
            neighbors.append(&mut self.get_field_edge_neighbors(&field));
        }
        // A field is not its own neighbor
        neighbors.into_iter().filter(|n| n != coord).collect()
    }
    fn remove_piece(&mut self, coord: &FieldCoord) {
        assert!(
            self.is_piece_on_field(coord),
            "There is no piece at {:?} to remove",
            coord
        );
        self.set_field(coord, Field::Empty);
        match coord.color() {
            Color::Black => self.black_pieces -= 1,
            Color::White => self.white_pieces -= 1,
        }
    }
    fn check_captures(&mut self, fields_to_check: &[FieldCoord]) {
        for field in fields_to_check {
            if field.color() != self.turn && self.is_piece_on_field(field)
                && self.get_field_edge_neighbors(field)
                    .iter()
                    .all(|coord| self.is_piece_on_field(coord))
            {
                self.remove_piece(field);
            }
        }
    }
}

// Hex methods
impl Board {
    fn get_hex(&self, coord: &HexCoord) -> &Option<Hex> {
        &self.board[(coord.x + 2) as usize][(coord.y + 2) as usize]
    }
    fn set_hex(&mut self, coord: &HexCoord, hex: Option<Hex>) {
        self.board[(coord.x + 2) as usize][(coord.y + 2) as usize] = hex;
    }
    fn get_hex_neighbor(&self, coord: &HexCoord, direction: u32) -> Option<HexCoord> {
        assert!(direction < 6);

        let neighbors = [
            (coord.x, coord.y + 1),
            (coord.x + 1, coord.y),
            (coord.x + 1, coord.y - 1),
            (coord.x, coord.y - 1),
            (coord.x - 1, coord.y),
            (coord.x - 1, coord.y + 1),
        ];

        self.try_hex(neighbors[direction as usize])
    }
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    fn is_hex_removable(&self, coord: &HexCoord) -> bool {
        match *self.get_hex(coord) {
            Some(hex) => if hex != [Field::Empty; 6] {
                return false;
            },
            None => return false,
        }

        let has_neighbor = |f| self.get_hex_neighbor(coord, f).is_some();
        let is_adjacent = |f| has_neighbor((f + 1) % 6) || has_neighbor((f + 5) % 6);

        let mut neighbor_count = 0;
        for f in 0..6 {
            if has_neighbor(f) {
                if is_adjacent(f) {
                    neighbor_count += 1;
                } else {
                    return false;
                }
            }
        }

        match neighbor_count {
            0 => panic!("A hex at {:?} is empty and has no neighbors", coord),
            1 | 2 | 3 => true,
            _ => false,
        }
    }
    fn remove_hex(&mut self, coord: &HexCoord) -> bool {
        let removable = self.is_hex_removable(coord);

        if removable {
            self.set_hex(coord, None);
        }
        removable
    }
    fn try_hex(&self, coord: (i32, i32)) -> Option<HexCoord> {
        if let Some(coord) = HexCoord::try_new(coord.0, coord.1) {
            if self.get_hex(&coord).is_some() {
                return Some(coord);
            }
        }
        None
    }
    fn check_hexes(&mut self, coord: &HexCoord) -> (u32, Vec<FieldCoord>) {
        let mut remove_count = 0;
        let mut fields = vec![];

        if self.remove_hex(coord) {
            remove_count += 1;
            for f in 0..6 {
                if let Some(neighbor) = self.get_hex_neighbor(coord, f) {
                    let (new_remove_count, mut new_fields) = self.check_hexes(&neighbor);
                    if new_remove_count == 0 {
                        fields.push(neighbor.to_field((f + 3) % 6));
                    } else {
                        remove_count += new_remove_count;
                        fields.append(&mut new_fields);
                    }
                }
            }
        }
        (remove_count, fields)
    }
}
<commit_msg>Fix can_move_piece to take &self, not &mut self<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use model::{Color, FieldCoord, HexCoord};

#[derive(Clone, Copy)]
pub struct Board {
    /*
                ____
               /    \
          ____/  +y  \____
         /    \      /    \
        /      \____/  +x  \
        \      /    \      /
         \____/      \____/
         /    \      /    \
        /  -x  \____/      \
        \      /    \      /
         \____/  -y  \____/
              \      /
               \____/

        The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
        up to the right, and the y-axis goes up and down. The board is stored as a dense 2D array,
        because a ragged array won't work (since each row would have to be a different type).

        See http://www.redblobgames.com/grids/hexagons/#coordinates-axial for more info.
    */
    board: [[Option<Hex>; 5]; 5],
    turn: Color,
    black_pieces: u32,
    black_hexes: u32,
    white_pieces: u32,
    white_hexes: u32,
}

// Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
type Hex = [Field; 6];

// There's no need to store the color of the piece on this field, since only white pieces can be on
// white fields, and vice versa.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Field {
    Piece,
    Empty,
}

// Public methods
impl Board {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Board {
        let mut board = Board {
            board: [[None; 5]; 5],
            turn: Color::White,
            black_pieces: 18,
            black_hexes: 0,
            white_pieces: 18,
            white_hexes: 0,
        };

        // (0, 0) is the only empty hex.
        board.set_hex(&HexCoord::new(0, 0), Some([Field::Empty; 6]));

        // All other hexes have exactly two pieces on them in the starting position.
        let piece_locations = [
            (-2,  2, 0, 4),
            (-2,  1, 0, 3),
            (-2,  0, 3, 5),
            (-1,  2, 1, 4),
            (-1,  1, 0, 4),
            (-1,  0, 3, 5),
            (-1, -1, 2, 5),
            ( 0,  2, 1, 5),
            ( 0,  1, 1, 5),
            ( 0, -1, 2, 4),
            ( 0, -2, 2, 4),
            ( 1,  1, 2, 5),
            ( 1,  0, 0, 2),
            ( 1, -1, 1, 3),
            ( 1, -2, 1, 4),
            ( 2,  0, 0, 2),
            ( 2, -1, 0, 3),
            ( 2, -2, 1, 3),
        ];

        for &(x, y, f1, f2) in &piece_locations {
            let mut hex = [Field::Empty; 6];
            hex[f1] = Field::Piece;
            hex[f2] = Field::Piece;
            board.set_hex(&HexCoord::new(x, y), Some(hex));
        }

        board
    }
    pub fn can_move_piece(&self, from: &FieldCoord, to: &FieldCoord) -> bool {
        from.color() == self.turn && self.is_piece_on_field(from) && !self.is_piece_on_field(to)
            && self.get_field_vertex_neighbors(from).contains(to)
    }
    pub fn move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) {
        assert!(self.can_move_piece(from, to));

        self.set_field(from, Field::Empty);
        self.set_field(to, Field::Piece);

        let (capture_count, mut fields_to_check) = self.check_hexes(&from.to_hex());
        fields_to_check.append(&mut self.get_field_edge_neighbors(to));
        self.check_captures(&fields_to_check);

        match self.turn {
            Color::White => self.white_hexes += capture_count,
            Color::Black => self.black_hexes += capture_count,
        }
        self.turn = self.turn.switch();
    }
    pub fn get_available_moves(&self, field: &FieldCoord) -> Vec<FieldCoord> {
        if self.is_piece_on_field(field) {
            self.get_field_vertex_neighbors(field)
                .into_iter()
                .filter(|c| !self.is_piece_on_field(c))
                .collect()
        } else {
            vec![]
        }
    }
    pub fn can_exchange(&self) -> bool {
        2 <= match self.turn {
            Color::Black => self.black_hexes,
            Color::White => self.white_hexes,
        }
    }
    pub fn can_exchange_piece(&self, coord: &FieldCoord) -> bool {
        self.can_exchange() && coord.color() != self.turn && self.is_piece_on_field(coord)
    }
    pub fn exchange_piece(&mut self, coord: &FieldCoord) {
        assert!(self.can_exchange_piece(coord));

        self.remove_piece(coord);
        match self.turn {
            Color::White => self.white_hexes -= 2,
            Color::Black => self.black_hexes -= 2,
        }

        // Players don't collect hexes removed due to an exchange
        let (_, fields_to_check) = self.check_hexes(&coord.to_hex());
        self.check_captures(&fields_to_check);
        self.turn = self.turn.switch();
    }
    pub fn is_piece_on_field(&self, coord: &FieldCoord) -> bool {
        self.get_field(coord) == &Field::Piece
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Return the coordinates of the hexes that have not been removed yet.
    pub fn extant_hexes(&self) -> Vec<HexCoord> {
        let mut coords = vec![];
        for x in -2..3 {
            for y in -2..3 {
                coords.push((x, y));
            }
        }
        coords
            .iter()
            .filter_map(|&coord| self.try_hex(coord))
            .collect()
    }
    pub fn turn(&self) -> Color {
        self.turn
    }
    pub fn black_pieces(&self) -> u32 {
        self.black_pieces
    }
    pub fn black_hexes(&self) -> u32 {
        self.black_hexes
    }
    pub fn white_pieces(&self) -> u32 {
        self.white_pieces
    }
    pub fn white_hexes(&self) -> u32 {
        self.white_hexes
    }
}

// Field and piece methods
impl Board {
    fn get_field(&self, coord: &FieldCoord) -> &Field {
        match *self.get_hex(&coord.to_hex()) {
            Some(ref hex) => &hex[coord.f as usize],
            None => panic!(
                "Cannot get field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    fn set_field(&mut self, coord: &FieldCoord, field: Field) {
        match *self.get_hex(&coord.to_hex()) {
            Some(mut hex) => {
                hex[coord.f as usize] = field;
                self.set_hex(&coord.to_hex(), Some(hex));
            }
            None => panic!(
                "Cannot set field {} on removed hex at {:?}",
                coord.f,
                coord.to_hex()
            ),
        }
    }
    /// Return fields that share an edge with the given field. These fields are always the opposite
    /// color of the given field. If all of a piece's edge neighbors are occupied, that piece might
    /// be capturable.
    fn get_field_edge_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![
            // There are always two edge neighbors on the same hex as the given field
            FieldCoord::new(coord.x, coord.y, (coord.f + 1) % 6),
            FieldCoord::new(coord.x, coord.y, (coord.f + 5) % 6),
        ];

        if let Some(hex) = self.get_hex_neighbor(&coord.to_hex(), coord.f) {
            neighbors.push(hex.to_field((coord.f + 3) % 6));
        }

        neighbors
    }
    /// Return fields that share a vertex with the given field and have the same color as the given
    /// field. Pieces can move to fields that are vertex neighbors of the field they are on.
    fn get_field_vertex_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        // A field's vertex neighbors can be defined as the edge neighbors of its edge neighbors
        let mut neighbors = vec![];
        for field in self.get_field_edge_neighbors(coord) {
            neighbors.append(&mut self.get_field_edge_neighbors(&field));
        }
        // A field is not its own neighbor
        neighbors.into_iter().filter(|n| n != coord).collect()
    }
    fn remove_piece(&mut self, coord: &FieldCoord) {
        assert!(
            self.is_piece_on_field(coord),
            "There is no piece at {:?} to remove",
            coord
        );
        self.set_field(coord, Field::Empty);
        match coord.color() {
            Color::Black => self.black_pieces -= 1,
            Color::White => self.white_pieces -= 1,
        }
    }
    fn check_captures(&mut self, fields_to_check: &[FieldCoord]) {
        for field in fields_to_check {
            if field.color() != self.turn && self.is_piece_on_field(field)
                && self.get_field_edge_neighbors(field)
                    .iter()
                    .all(|coord| self.is_piece_on_field(coord))
            {
                self.remove_piece(field);
            }
        }
    }
}

// Hex methods
impl Board {
    fn get_hex(&self, coord: &HexCoord) -> &Option<Hex> {
        &self.board[(coord.x + 2) as usize][(coord.y + 2) as usize]
    }
    fn set_hex(&mut self, coord: &HexCoord, hex: Option<Hex>) {
        self.board[(coord.x + 2) as usize][(coord.y + 2) as usize] = hex;
    }
    fn get_hex_neighbor(&self, coord: &HexCoord, direction: u32) -> Option<HexCoord> {
        assert!(direction < 6);

        let neighbors = [
            (coord.x, coord.y + 1),
            (coord.x + 1, coord.y),
            (coord.x + 1, coord.y - 1),
            (coord.x, coord.y - 1),
            (coord.x - 1, coord.y),
            (coord.x - 1, coord.y + 1),
        ];

        self.try_hex(neighbors[direction as usize])
    }
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    fn is_hex_removable(&self, coord: &HexCoord) -> bool {
        match *self.get_hex(coord) {
            Some(hex) => if hex != [Field::Empty; 6] {
                return false;
            },
            None => return false,
        }

        let has_neighbor = |f| self.get_hex_neighbor(coord, f).is_some();
        let is_adjacent = |f| has_neighbor((f + 1) % 6) || has_neighbor((f + 5) % 6);

        let mut neighbor_count = 0;
        for f in 0..6 {
            if has_neighbor(f) {
                if is_adjacent(f) {
                    neighbor_count += 1;
                } else {
                    return false;
                }
            }
        }

        match neighbor_count {
            0 => panic!("A hex at {:?} is empty and has no neighbors", coord),
            1 | 2 | 3 => true,
            _ => false,
        }
    }
    fn remove_hex(&mut self, coord: &HexCoord) -> bool {
        let removable = self.is_hex_removable(coord);

        if removable {
            self.set_hex(coord, None);
        }
        removable
    }
    fn try_hex(&self, coord: (i32, i32)) -> Option<HexCoord> {
        if let Some(coord) = HexCoord::try_new(coord.0, coord.1) {
            if self.get_hex(&coord).is_some() {
                return Some(coord);
            }
        }
        None
    }
    fn check_hexes(&mut self, coord: &HexCoord) -> (u32, Vec<FieldCoord>) {
        let mut remove_count = 0;
        let mut fields = vec![];

        if self.remove_hex(coord) {
            remove_count += 1;
            for f in 0..6 {
                if let Some(neighbor) = self.get_hex_neighbor(coord, f) {
                    let (new_remove_count, mut new_fields) = self.check_hexes(&neighbor);
                    if new_remove_count == 0 {
                        fields.push(neighbor.to_field((f + 3) % 6));
                    } else {
                        remove_count += new_remove_count;
                        fields.append(&mut new_fields);
                    }
                }
            }
        }
        (remove_count, fields)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;

use model::bitboard::{BitBoard, BitBoardIter};
use model::constants::*;
use model::{Color, ColorMap, FieldCoord, HexCoord, Move};

#[derive(Clone, Copy)]
pub struct Board {
    /*
    Board layout:
    The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
    up and to the right, and the y-axis goes up and down.
    See http://www.redblobgames.com/grids/hexagons/#coordinates-axial for more info.
                                             ____
                                            /    \
                                       ____/  +y  \____
                                      /    \      /    \
                                     /      \____/  +x  \
                                     \      /    \      /
                                      \____/      \____/
                                      /    \      /    \
                                     /  -x  \____/      \
                                     \      /    \      /
                                      \____/  -y  \____/
                                           \      /
                                            \____/

    Field layout:
    Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
                                          _________
                                         / \     / \
                                        /   \ 0 /   \
                                       /  5  \ /  1  \
                                      (-------*-------)
                                       \  4  / \  2  /
                                        \   / 3 \   /
                                         \_/_____\_/
    u64 Bitboard layout:

     MSB                                                                              LSB

     7 bits                            57 bits (19 groups of 3)
     +-----+  +-------------------------------------------------------------------------+
    [0000000][000 111 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 010 000]
     Unused   -+- -+-                 ||+-- Field 0           ||+-- Field 1       |+-- Piece on field
               |   +-- Extant hex     |+--- Field 2           |+--- Field 3       +--- No piece on field
               +------ Removed hex    +---- Field 4           +---- Field 5            (Field bitboards)
                      (Hex bitboard)  (Black field bitboard)  (White field bitboard)
    */
    fields: ColorMap<BitBoard>,
    hexes: BitBoard,
    extant_hex_count: u8,
    turn: Color,
    vitals: ColorMap<PlayerVitals>,
    outcome: Outcome,
}

/// A struct tracking a player's piece and captured hex count. So named because these two numbers are
/// essential to a player's survival (i.e. vital signs).
#[derive(Clone, Copy)]
struct PlayerVitals {
    pieces: u8,
    hexes: u8,
}

impl PlayerVitals {
    fn new() -> Self {
        Self {
            pieces: 18,
            hexes: 0,
        }
    }
}

/// The outcome of a game. Wins or draws caused by a resignation or an offered and accepted draw are
/// not differentiated from wins and draws by capturing all of an opponent's pieces, running out of
/// moves, being unable to capture any of the opponent's pieces, or reaching threefold repetition.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Outcome {
    InProgress,
    Win(Color),
    Draw,
}

lazy_static! {
    static ref HEX_MASK: [BitBoard; 19] = generate_hex_mask();
    static ref REMOVABLE_HEX_COMBS: [BitBoard; 342] = generate_removable_hex_combs();
}

// Public methods
impl Board {
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Self {
        Self {
            fields: generate_laurentius(),
            hexes: HEX_STARTING_POSITION,
            extant_hex_count: 19,
            turn: Color::White,
            vitals: ColorMap::new(PlayerVitals::new(), PlayerVitals::new()),
            outcome: Outcome::InProgress,
        }
    }
    pub fn apply_move(&mut self, mv: &Move) {
        assert!(self.can_apply_move(mv), "Cannot apply {:?}", mv);
        match *mv {
            Move::Move(from, to, color) => {
                self.toggle_field(from | to, color);

                let (capture_count, mut fields_to_check) =
                    self.check_hexes(from.trailing_zeros() as usize / 3);
                fields_to_check |= EDGE_NEIGHBORS.bb_get(to, color);
                self.check_captures(fields_to_check);

                self.vitals.get_mut(self.turn).hexes += capture_count;
            }
            Move::Exchange(bb, color) => {
                self.remove_piece(bb, color);
                self.vitals.get_mut(self.turn).hexes -= 2;

                // Players don't collect hexes removed due to an exchange
                let (_, fields_to_check) = self.check_hexes(bb.trailing_zeros() as usize / 3);
                self.check_captures(fields_to_check);
            }
        }
        self.turn = self.turn.switch();
        self.update_outcome();
    }
    pub fn can_apply_move(&self, mv: &Move) -> bool {
        match *mv {
            Move::Move(from, to, color) => {
                let vertex_neighbors = VERTEX_NEIGHBORS.bb_get(from, color);
                color == self.turn
                    && (to & vertex_neighbors != 0)
                    && self.is_piece_on_bitboard(from, color)
                    && !self.is_piece_on_bitboard(to, color)
            }
            Move::Exchange(bb, color) => {
                self.can_exchange() && color != self.turn && self.is_piece_on_bitboard(bb, color)
            }
        }
    }
    pub fn generate_moves(&self) -> impl Iterator<Item = Move> {
        let turn = self.turn;
        let fields = self.fields.get(turn);

        assert_ne!(fields, 0);

        let hexes = self.hexes;
        let opp_color = turn.switch();
        let opp_fields = if self.can_exchange() {
            self.fields.get(opp_color)
        } else {
            // impl Trait requires that we return a single, concrete type. So, if there are no
            // fields to exchange, we create an empty BitBoardIter and chain it on anyways. This
            // way, the type of the resulting iterator is always Chain.
            0
        };

        BitBoardIter::new(fields)
            .flat_map(move |origin| {
                let vertex_neighbors = VERTEX_NEIGHBORS.bb_get(origin, turn) & (!fields & hexes);
                BitBoardIter::new(vertex_neighbors).map(move |dest| Move::Move(origin, dest, turn))
            })
            .chain(
                BitBoardIter::new(opp_fields)
                    .map(move |exchanged| Move::Exchange(exchanged, opp_color)),
            )
    }
    pub fn available_moves_for_piece(&self, field: &FieldCoord) -> Vec<FieldCoord> {
        if self.is_piece_on_field(field) {
            let color = field.color();
            let vertex_neighbors = self.hexes & VERTEX_NEIGHBORS.bb_get(field.to_bitboard(), color);
            let mut moves = Vec::with_capacity(3);

            for dest in BitBoardIter::new(vertex_neighbors) {
                moves.push(FieldCoord::from_bitboard(dest, color));
            }
            moves
        } else {
            vec![]
        }
    }
    pub fn can_exchange(&self) -> bool {
        self.vitals.get(self.turn).hexes >= 2
    }
    pub fn is_piece_on_field(&self, coord: &FieldCoord) -> bool {
        self.is_piece_on_bitboard(coord.to_bitboard(), coord.color())
    }
    pub fn is_piece_on_bitboard(&self, bb: BitBoard, color: Color) -> bool {
        assert!(
            bb & self.hexes != 0,
            "Cannot cannot check if piece is on {:?}. Hex was removed.",
            FieldCoord::from_bitboard(bb, color),
        );

        bb & self.fields.get(color) != 0
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Return the coordinates of the hexes that have not been removed yet.
    pub fn extant_hexes(&self) -> Vec<HexCoord> {
        let mut coords = Vec::with_capacity(19);
        let try_coord = |coords: &mut Vec<HexCoord>, x, y| {
            if let Some(hex) = self.try_hex((x, y)) {
                coords.push(hex);
            }
        };
        try_coord(&mut coords, 1, 1);
        try_coord(&mut coords, -1, -1);

        for x in 0..3 {
            for y in -2..1 {
                try_coord(&mut coords, x, y);
                if x != y {
                    try_coord(&mut coords, -x, -y);
                }
            }
        }
        coords
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Returns true if a hex has not been removed yet.
    pub fn is_hex_extant(&self, index: usize) -> bool {
        self.hexes & HEX_MASK[index] != 0
    }
    pub fn resign(&mut self) {
        assert_eq!(self.outcome, Outcome::InProgress);
        self.outcome = Outcome::Win(self.turn.switch());
    }
    pub fn turn(&self) -> Color {
        self.turn
    }
    pub fn pieces(&self, color: Color) -> u8 {
        self.vitals.get(color).pieces
    }
    pub fn hexes(&self, color: Color) -> u8 {
        self.vitals.get(color).hexes
    }
    pub fn outcome(&self) -> Outcome {
        self.outcome
    }
}

impl Board {
    // TODO: check for threefold repetition
    fn update_outcome(&mut self) {
        if self.pieces(self.turn) == 0 {
            self.outcome = Outcome::Win(self.turn.switch());
        } else if !self.can_move() {
            self.outcome = Outcome::Draw;
        } else {
            use model::Color::*;

            let wp = self.pieces(White);
            let bp = self.pieces(Black);
            let wh = self.hexes(White);
            let bh = self.hexes(Black);

            // If neither side can capture the other's pieces, the game is drawn
            if wp == 1 && bp == 1 && (self.extant_hex_count + cmp::max(wh, bh) - 1 < 2) {
                self.outcome = Outcome::Draw;
            }
        }
    }
    fn can_move(&self) -> bool {
        if self.can_exchange() {
            return true;
        }

        for bb in BitBoardIter::new(self.fields.get(self.turn)) {
            let vertex_neighbors = VERTEX_NEIGHBORS.bb_get(bb, self.turn);
            if vertex_neighbors & self.fields.get(self.turn) != vertex_neighbors {
                return true;
            }
        }
        false
    }
}

// Field and piece methods
impl Board {
    fn toggle_field(&mut self, bb: BitBoard, color: Color) {
        assert_ne!(
            bb & self.hexes,
            0,
            "Trying to toggle field(s) on removed hex(es)"
        );

        *self.fields.get_mut(color) ^= bb;
    }
    fn remove_piece(&mut self, bb: BitBoard, color: Color) {
        assert!(
            self.is_piece_on_bitboard(bb, color),
            "There is no piece at {:?} to remove",
            FieldCoord::from_bitboard(bb, color)
        );
        self.toggle_field(bb, color);
        self.vitals.get_mut(color).pieces -= 1;
    }
    fn check_captures(&mut self, mut fields_to_check: BitBoard) {
        // fields_to_check must be a BitBoard for the opponent player (i.e. opposite of current turn)
        let us = self.turn;
        let them = us.switch();
        fields_to_check &= self.hexes & self.fields.get(them);
        for bb in BitBoardIter::new(fields_to_check) {
            let neighbors = self.hexes & EDGE_NEIGHBORS.bb_get(bb, them);
            if !self.fields.get(us) & neighbors == 0 {
                self.remove_piece(bb, them);
            }
        }
    }
}

// Hex methods
impl Board {
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    fn is_hex_removable(&self, index: usize) -> bool {
        debug_assert!(self.is_hex_extant(index));

        if (self.fields.white | self.fields.black) & HEX_MASK[index] != 0 {
            return false;
        }

        // Combining colors here is okay because there won't be overlaps
        let hex = self.hexes
            & (HEX_FIELD_NEIGHBORS.index_get(index, Color::White)
                | HEX_FIELD_NEIGHBORS.index_get(index, Color::Black));
        // There are 18 combinations to check for each hex
        REMOVABLE_HEX_COMBS
            .iter()
            .skip(index * 18)
            .take(18)
            .any(|&comb| hex == comb)
    }
    fn remove_hex(&mut self, index: usize) -> bool {
        let removable = self.is_hex_removable(index);

        if removable {
            self.hexes &= !HEX_MASK[index];
            self.extant_hex_count -= 1;
        }
        removable
    }
    fn try_hex(&self, coord: (i8, i8)) -> Option<HexCoord> {
        if let Some(coord) = HexCoord::try_new(coord.0, coord.1) {
            if self.is_hex_extant(coord.to_index()) {
                return Some(coord);
            }
        }
        None
    }
    fn check_hexes(&mut self, index: usize) -> (u8, BitBoard) {
        let mut remove_count = 0;
        let mut fields = 0;

        if self.remove_hex(index) {
            remove_count += 1;

            let our_neighbors = self.hexes & HEX_FIELD_NEIGHBORS.index_get(index, self.turn);
            let their_neighbors =
                self.hexes & HEX_FIELD_NEIGHBORS.index_get(index, self.turn.switch());

            for neighbor in BitBoardIter::new(our_neighbors | their_neighbors) {
                let check_result = self.check_hexes(neighbor.trailing_zeros() as usize / 3);
                remove_count += check_result.0;
                fields |= check_result.1;
            }

            // Add in the opponent's neighbors, excluding those on hexes that have been removed
            fields |= their_neighbors;
            fields &= self.hexes;
        }
        (remove_count, fields)
    }
}
<commit_msg>Improve "can move" check<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;

use model::bitboard::{BitBoard, BitBoardIter};
use model::constants::*;
use model::{Color, ColorMap, FieldCoord, HexCoord, Move};

#[derive(Clone, Copy)]
pub struct Board {
    /*
    Board layout:
    The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
    up and to the right, and the y-axis goes up and down.
    See http://www.redblobgames.com/grids/hexagons/#coordinates-axial for more info.
                                             ____
                                            /    \
                                       ____/  +y  \____
                                      /    \      /    \
                                     /      \____/  +x  \
                                     \      /    \      /
                                      \____/      \____/
                                      /    \      /    \
                                     /  -x  \____/      \
                                     \      /    \      /
                                      \____/  -y  \____/
                                           \      /
                                            \____/

    Field layout:
    Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
                                          _________
                                         / \     / \
                                        /   \ 0 /   \
                                       /  5  \ /  1  \
                                      (-------*-------)
                                       \  4  / \  2  /
                                        \   / 3 \   /
                                         \_/_____\_/
    u64 Bitboard layout:

     MSB                                                                              LSB

     7 bits                            57 bits (19 groups of 3)
     +-----+  +-------------------------------------------------------------------------+
    [0000000][000 111 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 010 000]
     Unused   -+- -+-                 ||+-- Field 0           ||+-- Field 1       |+-- Piece on field
               |   +-- Extant hex     |+--- Field 2           |+--- Field 3       +--- No piece on field
               +------ Removed hex    +---- Field 4           +---- Field 5            (Field bitboards)
                      (Hex bitboard)  (Black field bitboard)  (White field bitboard)
    */
    fields: ColorMap<BitBoard>,
    hexes: BitBoard,
    extant_hex_count: u8,
    turn: Color,
    vitals: ColorMap<PlayerVitals>,
    outcome: Outcome,
}

/// A struct tracking a player's piece and captured hex count. So named because these two numbers are
/// essential to a player's survival (i.e. vital signs).
#[derive(Clone, Copy)]
struct PlayerVitals {
    pieces: u8,
    hexes: u8,
}

impl PlayerVitals {
    fn new() -> Self {
        Self {
            pieces: 18,
            hexes: 0,
        }
    }
}

/// The outcome of a game. Wins or draws caused by a resignation or an offered and accepted draw are
/// not differentiated from wins and draws by capturing all of an opponent's pieces, running out of
/// moves, being unable to capture any of the opponent's pieces, or reaching threefold repetition.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Outcome {
    InProgress,
    Win(Color),
    Draw,
}

lazy_static! {
    static ref HEX_MASK: [BitBoard; 19] = generate_hex_mask();
    static ref REMOVABLE_HEX_COMBS: [BitBoard; 342] = generate_removable_hex_combs();
}

// Public methods
impl Board {
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Self {
        Self {
            fields: generate_laurentius(),
            hexes: HEX_STARTING_POSITION,
            extant_hex_count: 19,
            turn: Color::White,
            vitals: ColorMap::new(PlayerVitals::new(), PlayerVitals::new()),
            outcome: Outcome::InProgress,
        }
    }
    pub fn apply_move(&mut self, mv: &Move) {
        assert!(self.can_apply_move(mv), "Cannot apply {:?}", mv);
        match *mv {
            Move::Move(from, to, color) => {
                self.toggle_field(from | to, color);

                let (capture_count, mut fields_to_check) =
                    self.check_hexes(from.trailing_zeros() as usize / 3);
                fields_to_check |= EDGE_NEIGHBORS.bb_get(to, color);
                self.check_captures(fields_to_check);

                self.vitals.get_mut(self.turn).hexes += capture_count;
            }
            Move::Exchange(bb, color) => {
                self.remove_piece(bb, color);
                self.vitals.get_mut(self.turn).hexes -= 2;

                // Players don't collect hexes removed due to an exchange
                let (_, fields_to_check) = self.check_hexes(bb.trailing_zeros() as usize / 3);
                self.check_captures(fields_to_check);
            }
        }
        self.turn = self.turn.switch();
        self.update_outcome();
    }
    pub fn can_apply_move(&self, mv: &Move) -> bool {
        match *mv {
            Move::Move(from, to, color) => {
                let vertex_neighbors = VERTEX_NEIGHBORS.bb_get(from, color);
                color == self.turn
                    && (to & vertex_neighbors != 0)
                    && self.is_piece_on_bitboard(from, color)
                    && !self.is_piece_on_bitboard(to, color)
            }
            Move::Exchange(bb, color) => {
                self.can_exchange() && color != self.turn && self.is_piece_on_bitboard(bb, color)
            }
        }
    }
    pub fn generate_moves(&self) -> impl Iterator<Item = Move> {
        let turn = self.turn;
        let fields = self.fields.get(turn);

        assert_ne!(fields, 0);

        let hexes = self.hexes;
        let opp_color = turn.switch();
        let opp_fields = if self.can_exchange() {
            self.fields.get(opp_color)
        } else {
            // impl Trait requires that we return a single, concrete type. So, if there are no
            // fields to exchange, we create an empty BitBoardIter and chain it on anyways. This
            // way, the type of the resulting iterator is always Chain.
            0
        };

        BitBoardIter::new(fields)
            .flat_map(move |origin| {
                let vertex_neighbors = VERTEX_NEIGHBORS.bb_get(origin, turn) & (!fields & hexes);
                BitBoardIter::new(vertex_neighbors).map(move |dest| Move::Move(origin, dest, turn))
            })
            .chain(
                BitBoardIter::new(opp_fields)
                    .map(move |exchanged| Move::Exchange(exchanged, opp_color)),
            )
    }
    pub fn available_moves_for_piece(&self, field: &FieldCoord) -> Vec<FieldCoord> {
        if self.is_piece_on_field(field) {
            let color = field.color();
            let vertex_neighbors = self.hexes & VERTEX_NEIGHBORS.bb_get(field.to_bitboard(), color);
            let mut moves = Vec::with_capacity(3);

            for dest in BitBoardIter::new(vertex_neighbors) {
                moves.push(FieldCoord::from_bitboard(dest, color));
            }
            moves
        } else {
            vec![]
        }
    }
    pub fn can_exchange(&self) -> bool {
        self.vitals.get(self.turn).hexes >= 2
    }
    pub fn is_piece_on_field(&self, coord: &FieldCoord) -> bool {
        self.is_piece_on_bitboard(coord.to_bitboard(), coord.color())
    }
    pub fn is_piece_on_bitboard(&self, bb: BitBoard, color: Color) -> bool {
        assert!(
            bb & self.hexes != 0,
            "Cannot cannot check if piece is on {:?}. Hex was removed.",
            FieldCoord::from_bitboard(bb, color),
        );

        bb & self.fields.get(color) != 0
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Return the coordinates of the hexes that have not been removed yet.
    pub fn extant_hexes(&self) -> Vec<HexCoord> {
        let mut coords = Vec::with_capacity(19);
        let try_coord = |coords: &mut Vec<HexCoord>, x, y| {
            if let Some(hex) = self.try_hex((x, y)) {
                coords.push(hex);
            }
        };
        try_coord(&mut coords, 1, 1);
        try_coord(&mut coords, -1, -1);

        for x in 0..3 {
            for y in -2..1 {
                try_coord(&mut coords, x, y);
                if x != y {
                    try_coord(&mut coords, -x, -y);
                }
            }
        }
        coords
    }
    /// > extant (adj.): Still in existence; not destroyed, lost, or extinct (The Free Dictionary)
    ///
    /// Returns true if a hex has not been removed yet.
    pub fn is_hex_extant(&self, index: usize) -> bool {
        self.hexes & HEX_MASK[index] != 0
    }
    pub fn resign(&mut self) {
        assert_eq!(self.outcome, Outcome::InProgress);
        self.outcome = Outcome::Win(self.turn.switch());
    }
    pub fn turn(&self) -> Color {
        self.turn
    }
    pub fn pieces(&self, color: Color) -> u8 {
        self.vitals.get(color).pieces
    }
    pub fn hexes(&self, color: Color) -> u8 {
        self.vitals.get(color).hexes
    }
    pub fn outcome(&self) -> Outcome {
        self.outcome
    }
}

impl Board {
    // TODO: check for threefold repetition
    fn update_outcome(&mut self) {
        let fields = self.fields.get(self.turn);

        if fields == 0 {
            // No more pieces left
            self.outcome = Outcome::Win(self.turn.switch());
        } else if fields == self.hexes || !self.can_exchange() {
            // There are no empty fields to move to and we can't exchange
            self.outcome = Outcome::Draw;
        } else {
            use model::Color::*;

            let wp = self.pieces(White);
            let bp = self.pieces(Black);
            let wh = self.hexes(White);
            let bh = self.hexes(Black);

            // If neither side can capture the other's pieces, the game is drawn
            if wp == 1 && bp == 1 && (self.extant_hex_count + cmp::max(wh, bh) - 1 < 2) {
                self.outcome = Outcome::Draw;
            }
        }
    }
}

// Field and piece methods
impl Board {
    fn toggle_field(&mut self, bb: BitBoard, color: Color) {
        assert_ne!(
            bb & self.hexes,
            0,
            "Trying to toggle field(s) on removed hex(es)"
        );

        *self.fields.get_mut(color) ^= bb;
    }
    fn remove_piece(&mut self, bb: BitBoard, color: Color) {
        assert!(
            self.is_piece_on_bitboard(bb, color),
            "There is no piece at {:?} to remove",
            FieldCoord::from_bitboard(bb, color)
        );
        self.toggle_field(bb, color);
        self.vitals.get_mut(color).pieces -= 1;
    }
    fn check_captures(&mut self, mut fields_to_check: BitBoard) {
        // fields_to_check must be a BitBoard for the opponent player (i.e. opposite of current turn)
        let us = self.turn;
        let them = us.switch();
        fields_to_check &= self.hexes & self.fields.get(them);
        for bb in BitBoardIter::new(fields_to_check) {
            let neighbors = self.hexes & EDGE_NEIGHBORS.bb_get(bb, them);
            if !self.fields.get(us) & neighbors == 0 {
                self.remove_piece(bb, them);
            }
        }
    }
}

// Hex methods
impl Board {
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    fn is_hex_removable(&self, index: usize) -> bool {
        debug_assert!(self.is_hex_extant(index));

        if (self.fields.white | self.fields.black) & HEX_MASK[index] != 0 {
            return false;
        }

        // Combining colors here is okay because there won't be overlaps
        let hex = self.hexes
            & (HEX_FIELD_NEIGHBORS.index_get(index, Color::White)
                | HEX_FIELD_NEIGHBORS.index_get(index, Color::Black));
        // There are 18 combinations to check for each hex
        REMOVABLE_HEX_COMBS
            .iter()
            .skip(index * 18)
            .take(18)
            .any(|&comb| hex == comb)
    }
    fn remove_hex(&mut self, index: usize) -> bool {
        let removable = self.is_hex_removable(index);

        if removable {
            self.hexes &= !HEX_MASK[index];
            self.extant_hex_count -= 1;
        }
        removable
    }
    fn try_hex(&self, coord: (i8, i8)) -> Option<HexCoord> {
        if let Some(coord) = HexCoord::try_new(coord.0, coord.1) {
            if self.is_hex_extant(coord.to_index()) {
                return Some(coord);
            }
        }
        None
    }
    fn check_hexes(&mut self, index: usize) -> (u8, BitBoard) {
        let mut remove_count = 0;
        let mut fields = 0;

        if self.remove_hex(index) {
            remove_count += 1;

            let our_neighbors = self.hexes & HEX_FIELD_NEIGHBORS.index_get(index, self.turn);
            let their_neighbors =
                self.hexes & HEX_FIELD_NEIGHBORS.index_get(index, self.turn.switch());

            for neighbor in BitBoardIter::new(our_neighbors | their_neighbors) {
                let check_result = self.check_hexes(neighbor.trailing_zeros() as usize / 3);
                remove_count += check_result.0;
                fields |= check_result.1;
            }

            // Add in the opponent's neighbors, excluding those on hexes that have been removed
            fields |= their_neighbors;
            fields &= self.hexes;
        }
        (remove_count, fields)
    }
}
<|endoftext|>


<commit_before>use std::num::{Round};
use num::Integer;
use collections::hashmap::HashSet;

use cgmath::vector::{Vector, Vec2};

use area::Area;
use area::{Location, DIRECTIONS6, DIRECTIONS8};

pub struct Fov(HashSet<Location>);

#[deriving(Eq)]
struct Angle {
    pos: f32,
    radius: uint
}

impl Angle {
    pub fn new(pos: f32, radius: uint) -> Angle { Angle { pos: pos, radius: radius } }
    pub fn winding_index(self) -> int { (self.pos + 0.5).floor() as int }
    pub fn end_index(self) -> int { (self.pos + 0.5).ceil() as int }
    pub fn is_below(self, other: Angle) -> bool { self.winding_index() < other.end_index() }
    pub fn to_vec(self) -> Vec2<int> {
        if self.radius == 0 {
            return Vec2::new(0, 0);
        }

        let index = self.winding_index();

        let sector = index.mod_floor(&(self.radius as int * 6)) / self.radius as int;
        let offset = index.mod_floor(&(self.radius as int)) as int;
        let rod = DIRECTIONS6[sector].mul_s(self.radius as int);
        let tangent = DIRECTIONS6[(sector + 2) % 6].mul_s(offset);
        rod.add_v(&tangent)
    }

    pub fn further(self) -> Angle {
        Angle::new(
            self.pos * (self.radius + 1) as f32 / self.radius as f32,
            self.radius + 1)
    }

    pub fn next(self) -> Angle {
        Angle::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}

impl Fov {
    pub fn new() -> Fov { Fov(HashSet::new()) }

    pub fn add(&mut self, other: ~Fov) {
        let &Fov(ref mut h) = self;
        let ~Fov(o) = other;
        h.extend(&mut o.move_iter());
    }

    pub fn contains(&self, loc: Location) -> bool {
        let &Fov(ref h) = self;
        h.contains(&loc)
    }

    fn insert(&mut self, loc: Location) {
        let Fov(ref mut h) = *self;
        h.insert(loc);
    }
}

pub fn fov(a: &Area, center: Location, range: uint) -> Fov {
    let mut ret = Fov::new();
    // Dummy fov, just cover the immediate surrounding tiles.
    // TODO: Proper fov
    ret.insert(center);
    // Use dir8 to make walls look nice.

    // XXX: Should only show the degenerate directions (-1, 1) and (1, -1) if
    // there's a wall there.
    for &v in DIRECTIONS8.iter() {
        ret.insert(center + v);
    }

    process(a, &mut ret, range, center, Angle::new(0.0, 1), Angle::new(6.0, 1));

    fn process(
        a: &Area, f: &mut Fov, range: uint,
        center: Location, begin: Angle, end: Angle) {
        if begin.radius > range { return; }

        let mut angle = begin;
        let group_opaque = a.is_opaque(center + angle.to_vec());
        while angle.is_below(end) {
            let loc = center + angle.to_vec();
            if a.is_opaque(loc) != group_opaque {
                process(a, f, range, center, angle, end);
                // Terrain opaquity has changed, time to recurse.
                if !group_opaque {
                    process(a, f, range, center, begin.further(), angle.further());
                }
                return;
            }
            f.insert(loc);

            angle = angle.next();
        }

        if !group_opaque {
            process(a, f, range, center, begin.further(), end.further());
        }
    }

    ret
}
<commit_msg>Fov hack for nicer fake isometry<commit_after>use std::num::{Round};
use num::Integer;
use collections::hashmap::HashSet;

use cgmath::vector::{Vector, Vec2};

use area::Area;
use area::{Location, DIRECTIONS6, DIRECTIONS8};

pub struct Fov(HashSet<Location>);

#[deriving(Eq)]
struct Angle {
    pos: f32,
    radius: uint
}

impl Angle {
    pub fn new(pos: f32, radius: uint) -> Angle { Angle { pos: pos, radius: radius } }
    pub fn winding_index(self) -> int { (self.pos + 0.5).floor() as int }
    pub fn end_index(self) -> int { (self.pos + 0.5).ceil() as int }
    pub fn is_below(self, other: Angle) -> bool { self.winding_index() < other.end_index() }
    pub fn to_vec(self) -> Vec2<int> {
        if self.radius == 0 {
            return Vec2::new(0, 0);
        }

        let index = self.winding_index();

        let sector = index.mod_floor(&(self.radius as int * 6)) / self.radius as int;
        let offset = index.mod_floor(&(self.radius as int)) as int;
        let rod = DIRECTIONS6[sector].mul_s(self.radius as int);
        let tangent = DIRECTIONS6[(sector + 2) % 6].mul_s(offset);
        rod.add_v(&tangent)
    }

    pub fn further(self) -> Angle {
        Angle::new(
            self.pos * (self.radius + 1) as f32 / self.radius as f32,
            self.radius + 1)
    }

    pub fn next(self) -> Angle {
        Angle::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}

impl Fov {
    pub fn new() -> Fov { Fov(HashSet::new()) }

    pub fn add(&mut self, other: ~Fov) {
        let &Fov(ref mut h) = self;
        let ~Fov(o) = other;
        h.extend(&mut o.move_iter());
    }

    pub fn contains(&self, loc: Location) -> bool {
        let &Fov(ref h) = self;
        h.contains(&loc)
    }

    fn insert(&mut self, loc: Location) {
        let Fov(ref mut h) = *self;
        h.insert(loc);
    }
}

pub fn fov(a: &Area, center: Location, range: uint) -> Fov {
    let mut ret = Fov::new();
    // Dummy fov, just cover the immediate surrounding tiles.
    // TODO: Proper fov
    ret.insert(center);
    // Use dir8 to make walls look nice.

    // XXX: Should only show the degenerate directions (-1, 1) and (1, -1) if
    // there's a wall there.
    for &v in DIRECTIONS8.iter() {
        ret.insert(center + v);
    }

    process(a, &mut ret, range, center, Angle::new(0.0, 1), Angle::new(6.0, 1));

    fn process(
        a: &Area, f: &mut Fov, range: uint,
        center: Location, begin: Angle, end: Angle) {
        if begin.radius > range { return; }

        let mut angle = begin;
        let group_opaque = a.is_opaque(center + angle.to_vec());
        while angle.is_below(end) {
            let loc = center + angle.to_vec();
            if a.is_opaque(loc) != group_opaque {
                process(a, f, range, center, angle, end);
                // Terrain opaquity has changed, time to recurse.
                if !group_opaque {
                    process(a, f, range, center, begin.further(), angle.further());
                }
                return;
            }
            f.insert(loc);

            angle = angle.next();
        }

        if !group_opaque {
            process(a, f, range, center, begin.further(), end.further());
        }
    }

    // Post-processing hack to make acute corner wall tiles in fake-isometric
    // rooms visible.
    {
        let Fov(ref mut h) = ret;
        let mut queue = ~[];
        for &loc in h.iter() {
            //    above
            //  left right
            //     loc
            //
            // If both loc and above are visible, left and right will
            // be made visible if they are opaque.
            let above = loc + Vec2::new(-1, -1);
            let left = loc + Vec2::new(-1, 0);
            let right = loc + Vec2::new(0, -1);
            if h.contains(&above) {
               if a.is_opaque(left) {
                   queue.push(left);
               }
               if a.is_opaque(right) {
                   queue.push(right);
               }
            }
        }

        for &loc in queue.iter() { h.insert(loc); }
    }

    ret
}
<|endoftext|>


<commit_before>// Copyright 2017 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use super::*;

use cocoa::foundation::NSUInteger;
use objc::runtime::{YES, NO};
use objc_foundation::{INSString, NSString};

#[cfg(feature = "private")]
use libc;

#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MTLBlendFactor {
    Zero = 0,
    One = 1,
    SourceColor = 2,
    OneMinusSourceColor = 3,
    SourceAlpha = 4,
    OneMinusSourceAlpha = 5,
    DestinationColor = 6,
    OneMinusDestinationColor = 7,
    DestinationAlpha = 8,
    OneMinusDestinationAlpha = 9,
    SourceAlphaSaturated = 10,
    BlendColor = 11,
    OneMinusBlendColor = 12,
    BlendAlpha = 13,
    OneMinusBlendAlpha = 14,
    Source1Color = 15,
    OneMinusSource1Color = 16,
    Source1Alpha = 17,
    OneMinusSource1Alpha = 18,
}

#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MTLBlendOperation {
    Add = 0,
    Subtract = 1,
    ReverseSubtract = 2,
    Min = 3,
    Max = 4,
}

bitflags! {
    pub struct MTLColorWriteMask: NSUInteger {
        const Red   = 0x1 << 3;
        const Green = 0x1 << 2;
        const Blue  = 0x1 << 1;
        const Alpha = 0x1 << 0;
    }
}

#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MTLPrimitiveTopologyClass {
    Unspecified = 0,
    Point = 1,
    Line = 2,
    Triangle = 3,
}

pub enum MTLRenderPipelineColorAttachmentDescriptor {}

foreign_obj_type! {
    type CType = MTLRenderPipelineColorAttachmentDescriptor;
    pub struct RenderPipelineColorAttachmentDescriptor;
    pub struct RenderPipelineColorAttachmentDescriptorRef;
}

impl RenderPipelineColorAttachmentDescriptorRef {
    pub fn pixel_format(&self) -> MTLPixelFormat {
        unsafe {
            msg_send![self, pixelFormat]
        }
    }

    pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) {
        unsafe {
            msg_send![self, setPixelFormat:pixel_format]
        }
    }

    pub fn is_blending_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isBlendingEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_blending_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setBlendingEnabled:enabled]
        }
    }

    pub fn source_rgb_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, sourceRGBBlendFactor]
        }
    }

    pub fn set_source_rgb_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setSourceRGBBlendFactor:blend_factor]
        }
    }

    pub fn destination_rgb_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, destinationRGBBlendFactor]
        }
    }

    pub fn set_destination_rgb_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setDestinationRGBBlendFactor:blend_factor]
        }
    }

    pub fn rgb_blend_operation(&self) -> MTLBlendOperation {
        unsafe {
            msg_send![self, rgbBlendOperation]
        }
    }

    pub fn set_rgb_blend_operation(&self, blend_operation: MTLBlendOperation) {
        unsafe {
            msg_send![self, setRgbBlendOperation:blend_operation]
        }
    }

    pub fn source_alpha_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, sourceAlphaBlendFactor]
        }
    }

    pub fn set_source_alpha_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setSourceAlphaBlendFactor:blend_factor]
        }
    }

    pub fn destination_alpha_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, destinationAlphaBlendFactor]
        }
    }

    pub fn set_destination_alpha_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setDestinationAlphaBlendFactor:blend_factor]
        }
    }

    pub fn alpha_blend_operation(&self) -> MTLBlendOperation {
        unsafe {
            msg_send![self, alphaBlendOperation]
        }
    }

    pub fn set_alpha_blend_operation(&self, blend_operation: MTLBlendOperation) {
        unsafe {
            msg_send![self, setAlphaBlendOperation:blend_operation]
        }
    }

    pub fn write_mask(&self) -> MTLColorWriteMask {
        unsafe {
            msg_send![self, writeMask]
        }
    }

    pub fn set_write_mask(&self, mask: MTLColorWriteMask) {
        unsafe {
            msg_send![self, setWriteMask:mask]
        }
    }
}

pub enum MTLRenderPipelineReflection {}

foreign_obj_type! {
    type CType = MTLRenderPipelineReflection;
    pub struct RenderPipelineReflection;
    pub struct RenderPipelineReflectionRef;
}

impl RenderPipelineReflection {
    #[cfg(feature = "private")]
    pub unsafe fn new(vertex_data: *mut libc::c_void,
            fragment_data: *mut libc::c_void, vertex_desc: *mut libc::c_void,
            device: &DeviceRef, options: u64, flags: u64) -> Self
    {
        let class = class!(MTLRenderPipelineReflection);
        let this: RenderPipelineReflection = msg_send![class, alloc];
        let this_alias: *mut Object = msg_send![this.as_ref(), initWithVertexData:vertex_data
                                                                fragmentData:fragment_data
                                                serializedVertexDescriptor:vertex_desc
                                                                    device:device
                                                                    options:options
                                                                        flags:flags];
        if this_alias.is_null() {
            panic!("[MTLRenderPipelineReflection init] failed");
        }
        this
    }
}

impl RenderPipelineReflectionRef {
    pub fn fragment_arguments(&self) -> &Array<Argument> {
        unsafe {
            msg_send![self, fragmentArguments]
        }
    }

    pub fn vertex_arguments(&self) -> &Array<Argument> {
        unsafe {
            msg_send![self, vertexArguments]
        }
    }
}

pub enum MTLRenderPipelineDescriptor {}

foreign_obj_type! {
    type CType = MTLRenderPipelineDescriptor;
    pub struct RenderPipelineDescriptor;
    pub struct RenderPipelineDescriptorRef;
}

impl RenderPipelineDescriptor {
    pub fn new() -> Self {
        unsafe {
            let class = class!(MTLRenderPipelineDescriptor);
            msg_send![class, new]
        }
    }
}

impl RenderPipelineDescriptorRef {
    pub fn label(&self) -> &str {
        unsafe {
            let label: &NSString = msg_send![self, label];
            label.as_str()
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = NSString::from_str(label);
            msg_send![self, setLabel:nslabel]
        }
    }

    pub fn vertex_function(&self) -> Option<&FunctionRef> {
        unsafe {
            msg_send![self, vertexFunction]
        }
    }

    pub fn set_vertex_function(&self, function: Option<&FunctionRef>) {
        unsafe {
            msg_send![self, setVertexFunction:function]
        }
    }

    pub fn fragment_function(&self) -> Option<&FunctionRef> {
        unsafe {
            msg_send![self, fragmentFunction]
        }
    }

    pub fn set_fragment_function(&self, function: Option<&FunctionRef>) {
        unsafe {
            msg_send![self, setFragmentFunction:function]
        }
    }

    pub fn vertex_descriptor(&self) -> Option<&VertexDescriptorRef> {
        unsafe {
            msg_send![self, vertexDescriptor]
        }
    }

    pub fn set_vertex_descriptor(&self, descriptor: Option<&VertexDescriptorRef>) {
        unsafe {
            msg_send![self, setVertexDescriptor:descriptor]
        }
    }

    pub fn sample_count(&self) -> NSUInteger {
        unsafe {
            msg_send![self, sampleCount]
        }
    }

    pub fn set_sample_count(&self, count: NSUInteger) {
        unsafe {
            msg_send![self, setSampleCount:count]
        }
    }

    pub fn is_alpha_to_coverage_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isAlphaToCoverageEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_alpha_to_coverage_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setAlphaToCoverageEnabled:enabled]
        }
    }

    pub fn is_alpha_to_one_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isAlphaToOneEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_alpha_to_one_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setAlphaToOneEnabled:enabled]
        }
    }

    pub fn is_rasterization_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isRasterizationEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_rasterization_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setRasterizationEnabled:enabled]
        }
    }

    pub fn color_attachments(&self) -> &RenderPipelineColorAttachmentDescriptorArrayRef {
        unsafe {
            msg_send![self, colorAttachments]
        }
    }

    pub fn depth_attachment_pixel_format(&self) -> MTLPixelFormat {
        unsafe {
            msg_send![self, depthAttachmentPixelFormat]
        }
    }

    pub fn set_depth_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) {
        unsafe {
            msg_send![self, setDepthAttachmentPixelFormat:pixel_format]
        }
    }

    pub fn stencil_attachment_pixel_format(&self) -> MTLPixelFormat {
        unsafe {
            msg_send![self, stencilAttachmentPixelFormat]
        }
    }

    pub fn set_stencil_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) {
        unsafe {
            msg_send![self, setStencilAttachmentPixelFormat:pixel_format]
        }
    }

    pub fn input_primitive_topology(&self) -> MTLPrimitiveTopologyClass {
        unsafe {
            msg_send![self, inputPrimitiveTopology]
        }
    }

    pub fn set_input_primitive_topology(&self, topology: MTLPrimitiveTopologyClass) {
        unsafe {
            msg_send![self, setInputPrimitiveTopology:topology]
        }
    }

    #[cfg(feature = "private")]
    pub unsafe fn serialize_vertex_data(&self) -> *mut libc::c_void {
        use std::ptr;
        let flags = 0;
        let err: *mut Object = ptr::null_mut();
        msg_send![self, newSerializedVertexDataWithFlags:flags
                                                    error:err]
    }

    #[cfg(feature = "private")]
    pub unsafe fn serialize_fragment_data(&self) -> *mut libc::c_void {
        msg_send![self, serializeFragmentData]
    }
}

pub enum MTLRenderPipelineState {}

foreign_obj_type! {
    type CType = MTLRenderPipelineState;
    pub struct RenderPipelineState;
    pub struct RenderPipelineStateRef;
}

impl RenderPipelineStateRef {
    pub fn label(&self) -> &str {
        unsafe {
            let label: &NSString = msg_send![self, label];
            label.as_str()
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = NSString::from_str(label);
            msg_send![self, setLabel:nslabel]
        }
    }
}

pub enum MTLRenderPipelineColorAttachmentDescriptorArray {}

foreign_obj_type! {
    type CType = MTLRenderPipelineColorAttachmentDescriptorArray;
    pub struct RenderPipelineColorAttachmentDescriptorArray;
    pub struct RenderPipelineColorAttachmentDescriptorArrayRef;
}

impl RenderPipelineColorAttachmentDescriptorArrayRef {
    pub fn object_at(&self, index: usize) -> Option<&RenderPipelineColorAttachmentDescriptorRef> {
        unsafe {
            msg_send![self, objectAtIndexedSubscript:index]
        }
    }

    pub fn set_object_at(&self, index: usize, attachment: Option<&RenderPipelineColorAttachmentDescriptorRef>) {
        unsafe {
            msg_send![self, setObject:attachment
                   atIndexedSubscript:index]
        }
    }
}
<commit_msg>[MTLRenderPipelineState] add property supportIndirectCommandBuffers<commit_after>// Copyright 2017 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use super::*;

use cocoa::foundation::NSUInteger;
use objc::runtime::{YES, NO};
use objc_foundation::{INSString, NSString};

#[cfg(feature = "private")]
use libc;

#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MTLBlendFactor {
    Zero = 0,
    One = 1,
    SourceColor = 2,
    OneMinusSourceColor = 3,
    SourceAlpha = 4,
    OneMinusSourceAlpha = 5,
    DestinationColor = 6,
    OneMinusDestinationColor = 7,
    DestinationAlpha = 8,
    OneMinusDestinationAlpha = 9,
    SourceAlphaSaturated = 10,
    BlendColor = 11,
    OneMinusBlendColor = 12,
    BlendAlpha = 13,
    OneMinusBlendAlpha = 14,
    Source1Color = 15,
    OneMinusSource1Color = 16,
    Source1Alpha = 17,
    OneMinusSource1Alpha = 18,
}

#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MTLBlendOperation {
    Add = 0,
    Subtract = 1,
    ReverseSubtract = 2,
    Min = 3,
    Max = 4,
}

bitflags! {
    pub struct MTLColorWriteMask: NSUInteger {
        const Red   = 0x1 << 3;
        const Green = 0x1 << 2;
        const Blue  = 0x1 << 1;
        const Alpha = 0x1 << 0;
    }
}

#[repr(u64)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum MTLPrimitiveTopologyClass {
    Unspecified = 0,
    Point = 1,
    Line = 2,
    Triangle = 3,
}

pub enum MTLRenderPipelineColorAttachmentDescriptor {}

foreign_obj_type! {
    type CType = MTLRenderPipelineColorAttachmentDescriptor;
    pub struct RenderPipelineColorAttachmentDescriptor;
    pub struct RenderPipelineColorAttachmentDescriptorRef;
}

impl RenderPipelineColorAttachmentDescriptorRef {
    pub fn pixel_format(&self) -> MTLPixelFormat {
        unsafe {
            msg_send![self, pixelFormat]
        }
    }

    pub fn set_pixel_format(&self, pixel_format: MTLPixelFormat) {
        unsafe {
            msg_send![self, setPixelFormat:pixel_format]
        }
    }

    pub fn is_blending_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isBlendingEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_blending_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setBlendingEnabled:enabled]
        }
    }

    pub fn source_rgb_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, sourceRGBBlendFactor]
        }
    }

    pub fn set_source_rgb_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setSourceRGBBlendFactor:blend_factor]
        }
    }

    pub fn destination_rgb_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, destinationRGBBlendFactor]
        }
    }

    pub fn set_destination_rgb_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setDestinationRGBBlendFactor:blend_factor]
        }
    }

    pub fn rgb_blend_operation(&self) -> MTLBlendOperation {
        unsafe {
            msg_send![self, rgbBlendOperation]
        }
    }

    pub fn set_rgb_blend_operation(&self, blend_operation: MTLBlendOperation) {
        unsafe {
            msg_send![self, setRgbBlendOperation:blend_operation]
        }
    }

    pub fn source_alpha_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, sourceAlphaBlendFactor]
        }
    }

    pub fn set_source_alpha_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setSourceAlphaBlendFactor:blend_factor]
        }
    }

    pub fn destination_alpha_blend_factor(&self) -> MTLBlendFactor {
        unsafe {
            msg_send![self, destinationAlphaBlendFactor]
        }
    }

    pub fn set_destination_alpha_blend_factor(&self, blend_factor: MTLBlendFactor) {
        unsafe {
            msg_send![self, setDestinationAlphaBlendFactor:blend_factor]
        }
    }

    pub fn alpha_blend_operation(&self) -> MTLBlendOperation {
        unsafe {
            msg_send![self, alphaBlendOperation]
        }
    }

    pub fn set_alpha_blend_operation(&self, blend_operation: MTLBlendOperation) {
        unsafe {
            msg_send![self, setAlphaBlendOperation:blend_operation]
        }
    }

    pub fn write_mask(&self) -> MTLColorWriteMask {
        unsafe {
            msg_send![self, writeMask]
        }
    }

    pub fn set_write_mask(&self, mask: MTLColorWriteMask) {
        unsafe {
            msg_send![self, setWriteMask:mask]
        }
    }
}

pub enum MTLRenderPipelineReflection {}

foreign_obj_type! {
    type CType = MTLRenderPipelineReflection;
    pub struct RenderPipelineReflection;
    pub struct RenderPipelineReflectionRef;
}

impl RenderPipelineReflection {
    #[cfg(feature = "private")]
    pub unsafe fn new(vertex_data: *mut libc::c_void,
            fragment_data: *mut libc::c_void, vertex_desc: *mut libc::c_void,
            device: &DeviceRef, options: u64, flags: u64) -> Self
    {
        let class = class!(MTLRenderPipelineReflection);
        let this: RenderPipelineReflection = msg_send![class, alloc];
        let this_alias: *mut Object = msg_send![this.as_ref(), initWithVertexData:vertex_data
                                                                fragmentData:fragment_data
                                                serializedVertexDescriptor:vertex_desc
                                                                    device:device
                                                                    options:options
                                                                        flags:flags];
        if this_alias.is_null() {
            panic!("[MTLRenderPipelineReflection init] failed");
        }
        this
    }
}

impl RenderPipelineReflectionRef {
    pub fn fragment_arguments(&self) -> &Array<Argument> {
        unsafe {
            msg_send![self, fragmentArguments]
        }
    }

    pub fn vertex_arguments(&self) -> &Array<Argument> {
        unsafe {
            msg_send![self, vertexArguments]
        }
    }
}

pub enum MTLRenderPipelineDescriptor {}

foreign_obj_type! {
    type CType = MTLRenderPipelineDescriptor;
    pub struct RenderPipelineDescriptor;
    pub struct RenderPipelineDescriptorRef;
}

impl RenderPipelineDescriptor {
    pub fn new() -> Self {
        unsafe {
            let class = class!(MTLRenderPipelineDescriptor);
            msg_send![class, new]
        }
    }
}

impl RenderPipelineDescriptorRef {
    pub fn label(&self) -> &str {
        unsafe {
            let label: &NSString = msg_send![self, label];
            label.as_str()
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = NSString::from_str(label);
            msg_send![self, setLabel:nslabel]
        }
    }

    pub fn vertex_function(&self) -> Option<&FunctionRef> {
        unsafe {
            msg_send![self, vertexFunction]
        }
    }

    pub fn set_vertex_function(&self, function: Option<&FunctionRef>) {
        unsafe {
            msg_send![self, setVertexFunction:function]
        }
    }

    pub fn fragment_function(&self) -> Option<&FunctionRef> {
        unsafe {
            msg_send![self, fragmentFunction]
        }
    }

    pub fn set_fragment_function(&self, function: Option<&FunctionRef>) {
        unsafe {
            msg_send![self, setFragmentFunction:function]
        }
    }

    pub fn vertex_descriptor(&self) -> Option<&VertexDescriptorRef> {
        unsafe {
            msg_send![self, vertexDescriptor]
        }
    }

    pub fn set_vertex_descriptor(&self, descriptor: Option<&VertexDescriptorRef>) {
        unsafe {
            msg_send![self, setVertexDescriptor:descriptor]
        }
    }

    pub fn sample_count(&self) -> NSUInteger {
        unsafe {
            msg_send![self, sampleCount]
        }
    }

    pub fn set_sample_count(&self, count: NSUInteger) {
        unsafe {
            msg_send![self, setSampleCount:count]
        }
    }

    pub fn is_alpha_to_coverage_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isAlphaToCoverageEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_alpha_to_coverage_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setAlphaToCoverageEnabled:enabled]
        }
    }

    pub fn is_alpha_to_one_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isAlphaToOneEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_alpha_to_one_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setAlphaToOneEnabled:enabled]
        }
    }

    pub fn is_rasterization_enabled(&self) -> bool {
        unsafe {
            match msg_send![self, isRasterizationEnabled] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn set_rasterization_enabled(&self, enabled: bool) {
        unsafe {
            msg_send![self, setRasterizationEnabled:enabled]
        }
    }

    pub fn color_attachments(&self) -> &RenderPipelineColorAttachmentDescriptorArrayRef {
        unsafe {
            msg_send![self, colorAttachments]
        }
    }

    pub fn depth_attachment_pixel_format(&self) -> MTLPixelFormat {
        unsafe {
            msg_send![self, depthAttachmentPixelFormat]
        }
    }

    pub fn set_depth_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) {
        unsafe {
            msg_send![self, setDepthAttachmentPixelFormat:pixel_format]
        }
    }

    pub fn stencil_attachment_pixel_format(&self) -> MTLPixelFormat {
        unsafe {
            msg_send![self, stencilAttachmentPixelFormat]
        }
    }

    pub fn set_stencil_attachment_pixel_format(&self, pixel_format: MTLPixelFormat) {
        unsafe {
            msg_send![self, setStencilAttachmentPixelFormat:pixel_format]
        }
    }

    pub fn input_primitive_topology(&self) -> MTLPrimitiveTopologyClass {
        unsafe {
            msg_send![self, inputPrimitiveTopology]
        }
    }

    pub fn set_input_primitive_topology(&self, topology: MTLPrimitiveTopologyClass) {
        unsafe {
            msg_send![self, setInputPrimitiveTopology:topology]
        }
    }

    #[cfg(feature = "private")]
    pub unsafe fn serialize_vertex_data(&self) -> *mut libc::c_void {
        use std::ptr;
        let flags = 0;
        let err: *mut Object = ptr::null_mut();
        msg_send![self, newSerializedVertexDataWithFlags:flags
                                                    error:err]
    }

    #[cfg(feature = "private")]
    pub unsafe fn serialize_fragment_data(&self) -> *mut libc::c_void {
        msg_send![self, serializeFragmentData]
    }

    pub fn support_indirect_command_buffers(&self) -> bool {
        unsafe {
            match msg_send![self, supportIndirectCommandBuffers] {
                YES => true,
                NO => false,
                _ => unreachable!(),
            }
        }
    }

    pub fn set_support_indirect_command_buffers(&self, support: bool) {
        unsafe {
            msg_send![self, setSupportIndirectCommandBuffers: support]
        }
    }
}

pub enum MTLRenderPipelineState {}

foreign_obj_type! {
    type CType = MTLRenderPipelineState;
    pub struct RenderPipelineState;
    pub struct RenderPipelineStateRef;
}

impl RenderPipelineStateRef {
    pub fn label(&self) -> &str {
        unsafe {
            let label: &NSString = msg_send![self, label];
            label.as_str()
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = NSString::from_str(label);
            msg_send![self, setLabel:nslabel]
        }
    }
}

pub enum MTLRenderPipelineColorAttachmentDescriptorArray {}

foreign_obj_type! {
    type CType = MTLRenderPipelineColorAttachmentDescriptorArray;
    pub struct RenderPipelineColorAttachmentDescriptorArray;
    pub struct RenderPipelineColorAttachmentDescriptorArrayRef;
}

impl RenderPipelineColorAttachmentDescriptorArrayRef {
    pub fn object_at(&self, index: usize) -> Option<&RenderPipelineColorAttachmentDescriptorRef> {
        unsafe {
            msg_send![self, objectAtIndexedSubscript:index]
        }
    }

    pub fn set_object_at(&self, index: usize, attachment: Option<&RenderPipelineColorAttachmentDescriptorRef>) {
        unsafe {
            msg_send![self, setObject:attachment
                   atIndexedSubscript:index]
        }
    }
}
<|endoftext|>


<commit_before>use clap::value_t;
use point_viewer::color::{TRANSPARENT, WHITE};
use point_viewer::octree::octree_from_directory;
use scoped_pool::Pool;
use std::path::Path;
use xray::generation::{
    ColoringStrategyArgument, ColoringStrategyKind, TileBackgroundColorArgument,
};

fn parse_arguments() -> clap::ArgMatches<'static> {
    clap::App::new("build_xray_quadtree")
        .version("1.0")
        .author("Holger H. Rapp <hrapp@lyft.com>")
        .args(&[
            clap::Arg::with_name("output_directory")
                .help("Output directory to write the X-Ray quadtree into.")
                .long("output_directory")
                .required(true)
                .takes_value(true),
            clap::Arg::with_name("resolution")
                .help("Size of 1px in meters on the finest X-Ray level.")
                .long("resolution")
                .default_value("0.01"),
            clap::Arg::with_name("num_theads")
                .help("The number of threads used to shard X-Ray tile building.")
                .long("num_threads")
                .default_value("10"),
            clap::Arg::with_name("tile_size")
                .help("Size of finest X-Ray level tile in pixels. Must be a power of two.")
                .long("tile_size")
                .default_value("256"),
            clap::Arg::with_name("coloring_strategy")
                .long("coloring_strategy")
                .takes_value(true)
                .possible_values(&ColoringStrategyArgument::variants())
                .default_value("xray"),
            clap::Arg::with_name("min_intensity")
                .help(
                    "Minimum intensity of all points for color scaling. \
                     Only used for 'colored_with_intensity'.",
                )
                .long("min_intensity")
                .takes_value(true)
                .required_if("coloring_strategy", "colored_with_intensity"),
            clap::Arg::with_name("max_stddev")
                .help(
                    "Maximum standard deviation for colored_with_height_stddev. Every stddev above this \
                     will be clamped to this value and appear saturated in the X-Rays. \
                     Only used for 'colored_with_height_stddev'.",
                )
                .long("max_stddev")
                .takes_value(true)
                .required_if("coloring_strategy", "colored_with_height_stddev"),
            clap::Arg::with_name("max_intensity")
                .help(
                    "Minimum intensity of all points for color scaling. \
                     Only used for 'colored_with_intensity'.",
                )
                .long("max_intensity")
                .takes_value(true)
                .required_if("coloring_strategy", "colored_with_intensity"),
            clap::Arg::with_name("octree_directory")
                .help("Octree directory to turn into xrays.")
                .index(1)
                .required(true),
            clap::Arg::with_name("tile_background_color")
                .long("tile_background_color")
                .takes_value(true)
                .possible_values(&TileBackgroundColorArgument::variants())
                .default_value("white"),
        ])
        .get_matches()
}

pub fn main() {
    let args = parse_arguments();
    let resolution = args
        .value_of("resolution")
        .unwrap()
        .parse::<f64>()
        .expect("resolution could not be parsed.");
    let num_threads = args
        .value_of("num_threads")
        .unwrap()
        .parse::<usize>()
        .expect("num_threads could not be parsed.");
    let tile_size = args
        .value_of("tile_size")
        .unwrap()
        .parse::<u32>()
        .expect("tile_size could not be parsed.");
    if !tile_size.is_power_of_two() {
        panic!("tile_size is not a power of two.");
    }

    let coloring_strategy_kind = {
        use crate::ColoringStrategyArgument::*;
        let arg = value_t!(args, "coloring_strategy", ColoringStrategyArgument)
            .expect("coloring_strategy is invalid");
        match arg {
            xray => ColoringStrategyKind::XRay,
            colored => ColoringStrategyKind::Colored,
            colored_with_intensity => ColoringStrategyKind::ColoredWithIntensity(
                value_t!(args, "min_intensity", f32).unwrap_or(1.),
                value_t!(args, "max_intensity", f32).unwrap_or(1.),
            ),
            colored_with_height_stddev => ColoringStrategyKind::ColoredWithHeightStddev(
                value_t!(args, "max_stddev", f32).unwrap_or(1.),
            ),
        }
    };

    let tile_background_color = {
        let arg = value_t!(args, "tile_background_color", TileBackgroundColorArgument)
            .expect("tile_background_color is invalid");
        match arg {
            TileBackgroundColorArgument::white => WHITE.to_u8(),
            TileBackgroundColorArgument::transparent => TRANSPARENT.to_u8(),
        }
    };

    let octree_directory = Path::new(args.value_of("octree_directory").unwrap());
    let output_directory = Path::new(args.value_of("output_directory").unwrap());

    let pool = Pool::new(num_threads);
    let octree = octree_from_directory(octree_directory).expect("Could not open octree.");
    xray::generation::build_xray_quadtree(
        &pool,
        &octree,
        output_directory,
        resolution,
        tile_size,
        &coloring_strategy_kind,
        tile_background_color,
    )
    .unwrap();
}
<commit_msg>Stupid little bug fix. (#258)<commit_after>use clap::value_t;
use point_viewer::color::{TRANSPARENT, WHITE};
use point_viewer::octree::octree_from_directory;
use scoped_pool::Pool;
use std::path::Path;
use xray::generation::{
    ColoringStrategyArgument, ColoringStrategyKind, TileBackgroundColorArgument,
};

fn parse_arguments() -> clap::ArgMatches<'static> {
    clap::App::new("build_xray_quadtree")
        .version("1.0")
        .author("Holger H. Rapp <hrapp@lyft.com>")
        .args(&[
            clap::Arg::with_name("output_directory")
                .help("Output directory to write the X-Ray quadtree into.")
                .long("output_directory")
                .required(true)
                .takes_value(true),
            clap::Arg::with_name("resolution")
                .help("Size of 1px in meters on the finest X-Ray level.")
                .long("resolution")
                .default_value("0.01"),
            clap::Arg::with_name("num_threads")
                .help("The number of threads used to shard X-Ray tile building.")
                .takes_value(true)
                .long("num_threads")
                .default_value("10"),
            clap::Arg::with_name("tile_size")
                .help("Size of finest X-Ray level tile in pixels. Must be a power of two.")
                .long("tile_size")
                .default_value("256"),
            clap::Arg::with_name("coloring_strategy")
                .long("coloring_strategy")
                .takes_value(true)
                .possible_values(&ColoringStrategyArgument::variants())
                .default_value("xray"),
            clap::Arg::with_name("min_intensity")
                .help(
                    "Minimum intensity of all points for color scaling. \
                     Only used for 'colored_with_intensity'.",
                )
                .long("min_intensity")
                .takes_value(true)
                .required_if("coloring_strategy", "colored_with_intensity"),
            clap::Arg::with_name("max_stddev")
                .help(
                    "Maximum standard deviation for colored_with_height_stddev. Every stddev above this \
                     will be clamped to this value and appear saturated in the X-Rays. \
                     Only used for 'colored_with_height_stddev'.",
                )
                .long("max_stddev")
                .takes_value(true)
                .required_if("coloring_strategy", "colored_with_height_stddev"),
            clap::Arg::with_name("max_intensity")
                .help(
                    "Minimum intensity of all points for color scaling. \
                     Only used for 'colored_with_intensity'.",
                )
                .long("max_intensity")
                .takes_value(true)
                .required_if("coloring_strategy", "colored_with_intensity"),
            clap::Arg::with_name("octree_directory")
                .help("Octree directory to turn into xrays.")
                .index(1)
                .required(true),
            clap::Arg::with_name("tile_background_color")
                .long("tile_background_color")
                .takes_value(true)
                .possible_values(&TileBackgroundColorArgument::variants())
                .default_value("white"),
        ])
        .get_matches()
}

pub fn main() {
    let args = parse_arguments();
    let resolution = args
        .value_of("resolution")
        .unwrap()
        .parse::<f64>()
        .expect("resolution could not be parsed.");
    let num_threads = args
        .value_of("num_threads")
        .unwrap()
        .parse::<usize>()
        .expect("num_threads could not be parsed.");
    let tile_size = args
        .value_of("tile_size")
        .unwrap()
        .parse::<u32>()
        .expect("tile_size could not be parsed.");
    if !tile_size.is_power_of_two() {
        panic!("tile_size is not a power of two.");
    }

    let coloring_strategy_kind = {
        use crate::ColoringStrategyArgument::*;
        let arg = value_t!(args, "coloring_strategy", ColoringStrategyArgument)
            .expect("coloring_strategy is invalid");
        match arg {
            xray => ColoringStrategyKind::XRay,
            colored => ColoringStrategyKind::Colored,
            colored_with_intensity => ColoringStrategyKind::ColoredWithIntensity(
                value_t!(args, "min_intensity", f32).unwrap_or(1.),
                value_t!(args, "max_intensity", f32).unwrap_or(1.),
            ),
            colored_with_height_stddev => ColoringStrategyKind::ColoredWithHeightStddev(
                value_t!(args, "max_stddev", f32).unwrap_or(1.),
            ),
        }
    };

    let tile_background_color = {
        let arg = value_t!(args, "tile_background_color", TileBackgroundColorArgument)
            .expect("tile_background_color is invalid");
        match arg {
            TileBackgroundColorArgument::white => WHITE.to_u8(),
            TileBackgroundColorArgument::transparent => TRANSPARENT.to_u8(),
        }
    };

    let octree_directory = Path::new(args.value_of("octree_directory").unwrap());
    let output_directory = Path::new(args.value_of("output_directory").unwrap());

    let pool = Pool::new(num_threads);
    let octree = octree_from_directory(octree_directory).expect("Could not open octree.");
    xray::generation::build_xray_quadtree(
        &pool,
        &octree,
        output_directory,
        resolution,
        tile_size,
        &coloring_strategy_kind,
        tile_background_color,
    )
    .unwrap();
}
<|endoftext|>