<commit_before>use crate::{direction::Direction, ship::Ship, space::Space};
use std::cmp;

pub struct Player {
    is_cpu: bool,
    spaces: Vec<Space>,
    ships: Vec<Ship>,
    grid_size: [u8; 2],
    grid_cursor: [u8; 2],
}

impl Player {
    pub fn new(grid_size: [u8; 2], ship_count: usize, is_cpu: bool) -> Player {
        Player {
            is_cpu: is_cpu,
            spaces: Space::all_grid_spaces(&grid_size),
            ships: Vec::with_capacity(ship_count),
            grid_size: grid_size,
            grid_cursor: [0, 0],
        }
    }

    /// Selects a space.
    ///
    /// # Errors
    ///
    /// Returns an error if the space at `pos` was already checked.
    pub fn select_space(&mut self, pos: &[u8; 2]) -> Result<(), &'static str> {
        let space_index = self.space_index(pos);
        let ship_hit = self.ships.iter().position(|s| s.pos().contains(pos));
        self.spaces[space_index].set_checked(ship_hit.is_some())?;

        Ok(())
    }

    /// Sets the ship at the given position as sunk if all spaces it occupies
    /// have been checked, and returns whether the ship was sunk.
    ///
    /// # Errors
    ///
    /// Returns an error if no ship is at the given position, or if the ship
    /// state is not active.
    pub fn sink_ship_if_all_hit(&mut self, pos: &[u8; 2]) -> Result<bool, &'static str> {
        if let Some(index) = self.ships.iter().position(|s| s.pos().contains(pos)) {
            let sunk = self.ships[index]
                .pos()
                .iter()
                .all(|p| self.space(p).is_hit());

            if sunk {
                self.ships[index].set_sunk()?;
            }

            Ok(sunk)
        } else {
            Err("no ship at the given position")
        }
    }

    /// Returns whether all of the player's ships have been sunk.
    pub fn all_ships_sunk(&self) -> bool {
        self.ships.iter().all(|ship| ship.is_sunk())
    }

    /// Returns suggestions for the best spaces to check based on the player's hit spaces and ships.
    ///
    /// This is intended for use in cases where the active player is computer-controlled, to
    /// determine the space they check.  However, it could also be used to suggest a space that a
    /// human player could check.
    pub fn suggested_checks(&self) -> Vec<[u8; 2]> {
        let mut select = vec![];
        let directions = Direction::all();
        let hit_spaces = self
            .spaces
            .iter()
            .filter(|s| s.is_hit() && self.ship(s.pos()).unwrap().is_active())
            .collect::<Vec<&Space>>();

        // Check for a line of hit spaces.
        for space in &hit_spaces {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(space.pos(), *direction, true);

                if let Some(pos) = unchecked {
                    if !select.contains(&pos) {
                        select.push(pos);
                    }
                }
            }
        }

        // If a hit space was found, but no hit spaces next to it, look for unchecked spaces next
        // to it.
        if hit_spaces.len() > 0 && select.is_empty() {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(hit_spaces[0].pos(), *direction, false);

                if let Some(pos) = unchecked {
                    select.push(pos);
                }
            }
        }

        // If no candidates have been found yet, just add any unchecked space.
        // TODO: smarter checking for spaces that remaining ships could realistically occupy.
        if select.is_empty() {
            select = self
                .spaces
                .iter()
                .filter(|space| space.is_unchecked())
                .map(|space| *space.pos())
                .collect::<Vec<[u8; 2]>>();
        }

        select
    }

    pub fn add_ship(
        &mut self,
        head: [u8; 2],
        direction: Direction,
        length: u8,
        placement: bool,
    ) -> Result<(), &'static str> {
        if self.ships.len() == self.ships.capacity() {
            Err("tried to add ship to a player with all ships already added")
        } else {
            let pos = self
                .get_ship_position(head, direction, length)
                .ok_or("tried to place a ship partially out of bounds")?;

            if !placement && !self.valid_ship_position(&pos) {
                Err("tried to place a ship in an invalid position")
            } else {
                let mut ship = Ship::new(pos)?;

                if !placement {
                    ship.set_active()?;
                }

                self.ships.push(ship);

                Ok(())
            }
        }
    }

    /// Moves the player's placement ship in the given direction.
    ///
    /// # Errors
    ///
    /// Returns an error if the player does not have a placement ship, or if the ship could not be
    /// moved in `direction` without going out of bounds.
    pub fn move_placement_ship(&mut self, direction: Direction) -> Result<(), &'static str> {
        let index = self.ships.len() - 1;
        let old_head = self.ships[index].pos()[0];
        let new_head = self
            .movement(&old_head, direction)
            .ok_or("movement not possible without going out of bounds")?;
        let ship_pos = self
            .get_ship_position(
                new_head,
                self.ships[index].dir(),
                self.ships[index].len() as u8,
            )
            .ok_or("movement not possible without going out of bounds")?;

        self.ships[index].set_pos(ship_pos)?;

        Ok(())
    }

    pub fn place_placement_ship(&mut self) -> Result<(), &'static str> {
        let index = self.ships.len() - 1;

        // Ensure the ship doesn't overlap with another ship.
        if !self.valid_ship_position(&self.ships[index].pos()) {
            Err("placement ship overlaps with another ship")
        } else {
            self.ships[index].set_active()?;

            Ok(())
        }
    }

    /// Rotates the player's placement ship during the ship placement game state.
    ///
    /// # Errors
    ///
    /// Returns an error if the player does not have a placement ship.
    pub fn rotate_placement_ship(&mut self) -> Result<(), &'static str> {
        let index = self.ships.len() - 1;
        let ship_len = self.ships[index].len() as u8;
        let dir = self.ships[index].dir().rotated();

        // If the current ship position would cause the rotation to position the ship partially out
        // of bounds, adjust the position such that the ship will be entirely within bounds.
        let old_head = self.ships[index].pos()[0];
        let new_head = match dir {
            Direction::North => [
                old_head[0],
                cmp::min(old_head[1], self.grid_size[1] - ship_len),
            ],
            Direction::East => [cmp::max(old_head[0], ship_len - 1), old_head[1]],
            Direction::South => [old_head[0], cmp::max(old_head[1], ship_len - 1)],
            Direction::West => [
                cmp::min(old_head[0], self.grid_size[0] - ship_len),
                old_head[1],
            ],
        };

        // get_ship_position() should always return Some(ship_pos) in this situation.
        let ship_pos = self.get_ship_position(new_head, dir, ship_len).unwrap();
        self.ships[index].set_pos(ship_pos)?;

        Ok(())
    }

    /// Returns a ship position, given its head position, direction and length.
    ///
    /// `direction` refers to the direction the ship is facing, not the
    /// direction in which positions are generated.
    /// Returns `None` if the resulting ship position would not be contained
    /// within the grid.
    pub fn get_ship_position(
        &self,
        head: [u8; 2],
        direction: Direction,
        length: u8,
    ) -> Option<Vec<[u8; 2]>> {
        let valid = match direction {
            Direction::North => head[1] + length <= self.grid_size[1],
            Direction::East => head[0] >= length - 1,
            Direction::South => head[1] >= length - 1,
            Direction::West => head[0] + length <= self.grid_size[0],
        };

        if valid {
            let mut ship = Vec::with_capacity(length as usize);

            for pos in 0..length {
                let pos_u8 = pos as u8;

                ship.push(match direction {
                    Direction::North => [head[0], head[1] + pos_u8],
                    Direction::East => [head[0] - pos_u8, head[1]],
                    Direction::South => [head[0], head[1] - pos_u8],
                    Direction::West => [head[0] + pos_u8, head[1]],
                });
            }

            Some(ship)
        } else {
            None
        }
    }

    /// Checks that the given ship position is valid.
    ///
    /// If the player is CPU-controlled, a ship in a space next to another ship
    /// will be considered invalid.
    fn valid_ship_position(&self, new_ship: &[[u8; 2]]) -> bool {
        new_ship.iter().all(|s| {
            self.valid_space(s)
                && !self.ship_is_in_space(s)
                && !(self.ship_is_next_to(s) && self.is_cpu)
        })
    }

    /// Gets a reference to the ships.
    pub fn ships(&self) -> &[Ship] {
        &self.ships
    }

    /// Gets a reference to a ship if it is in the given position.
    fn ship(&self, pos: &[u8; 2]) -> Option<&Ship> {
        self.ships.iter().find(|s| s.pos().contains(pos))
    }

    /// Returns whether a ship occupies the specified grid coordinates.
    pub fn ship_is_in_space(&self, pos: &[u8; 2]) -> bool {
        self.ships
            .iter()
            .any(|s| s.pos().contains(pos) && !s.is_placement())
    }

    /// Returns whether there is a ship next to the specified grid coordinates.
    fn ship_is_next_to(&self, pos: &[u8; 2]) -> bool {
        let mut result = false;
        let x = pos[0];
        let y = pos[1];

        // Left
        if x > 0 {
            result = self.ship_is_in_space(&[x - 1, y]);
        }

        // Right
        if x < self.grid_size[0] - 1 && !result {
            result = self.ship_is_in_space(&[x + 1, y]);
        }

        // Above
        if y > 0 && !result {
            result = self.ship_is_in_space(&[x, y - 1]);
        }

        // Below
        if y < self.grid_size[1] - 1 && !result {
            result = self.ship_is_in_space(&[x, y + 1]);
        }

        result
    }

    /// Gets a reference to the spaces.
    pub fn spaces(&self) -> &[Space] {
        &self.spaces
    }

    /// Returns whether the given position is valid.
    fn valid_space(&self, pos: &[u8; 2]) -> bool {
        pos[0] < self.grid_size[0] && pos[1] < self.grid_size[1]
    }

    /// Gets a reference to the space with the given position.
    pub fn space(&self, pos: &[u8; 2]) -> &Space {
        self.spaces.get(self.space_index(pos)).unwrap()
    }

    /// Calculates the index of the given position in the spaces vector.
    fn space_index(&self, pos: &[u8; 2]) -> usize {
        self.grid_size[0] as usize * pos[0] as usize + pos[1] as usize
    }

    /// Returns the coordinates of a movement from `pos` in a `direction`.
    /// Returns `None` if the movement is not possible.
    fn movement(&self, pos: &[u8; 2], direction: Direction) -> Option<[u8; 2]> {
        let valid = match direction {
            Direction::North => pos[1] > 0,
            Direction::East => pos[0] < self.grid_size[0] - 1,
            Direction::South => pos[1] < self.grid_size[1] - 1,
            Direction::West => pos[0] > 0,
        };

        match valid {
            true => Some(match direction {
                Direction::North => [pos[0], pos[1] - 1],
                Direction::East => [pos[0] + 1, pos[1]],
                Direction::South => [pos[0], pos[1] + 1],
                Direction::West => [pos[0] - 1, pos[1]],
            }),
            false => None,
        }
    }

    /// Finds the first non-hit, unchecked space in a `direction` from `pos`.
    /// Can also make sure the space is at the end of a `line` of hit spaces.
    /// Returns `None` if the first non-hit space has been checked or if a grid
    /// boundary is reached.
    fn find_unchecked_space(
        &self,
        pos: &[u8; 2],
        direction: Direction,
        check_for_line: bool,
    ) -> Option<[u8; 2]> {
        let mut check_pos = self.movement(pos, direction);

        while let Some(next_pos) = check_pos {
            let next_space = self.space(&next_pos);

            match next_space.is_hit() {
                true => check_pos = self.movement(&next_pos, direction),
                false => {
                    if !next_space.is_unchecked() {
                        check_pos = None;
                    }
                    break;
                }
            };
        }

        if check_for_line && check_pos.is_some() {
            let unchecked = check_pos.unwrap();
            let opposite_dir = direction.opposite();
            let prev_pos = self.movement(&unchecked, opposite_dir).unwrap();

            if &prev_pos == pos {
                check_pos = None;
            }
        }

        check_pos
    }

    /// Returns the coordinates of the player's grid cursor.
    pub fn grid_cursor(&self) -> &[u8; 2] {
        &self.grid_cursor
    }

    /// Moves the player's grid cursor in the given `direction`.
    ///
    /// # Errors
    ///
    /// Returns an error if moving the grid cursor in `direction` would move it out of bounds.
    pub fn move_grid_cursor(&mut self, direction: Direction) -> Result<(), &'static str> {
        if let Some(new_cursor) = self.movement(&self.grid_cursor, direction) {
            self.set_grid_cursor(&new_cursor)?;

            Ok(())
        } else {
            Err("tried to move grid cursor out of bounds")
        }
    }

    /// Sets the player's grid cursor position.
    ///
    /// # Errors
    ///
    /// Returns an error if no space exists at `pos`.
    pub fn set_grid_cursor(&mut self, pos: &[u8; 2]) -> Result<(), &'static str> {
        if self.space_index(pos) < self.spaces.len() {
            self.grid_cursor = *pos;

            Ok(())
        } else {
            Err("tried to set the grid cursor to a nonexistent space")
        }
    }

    pub fn is_cpu(&self) -> bool {
        self.is_cpu
    }

    pub fn placement_ship(&self) -> Result<&Ship, &'static str> {
        let ships_len = self.ships.len();

        if ships_len == 0 {
            Err("player has no ships")
        } else if !self.ships[ships_len - 1].is_placement() {
            Err("player has no placement ship")
        } else {
            Ok(&self.ships[self.ships.len() - 1])
        }
    }

    pub fn placement_ship_mut(&mut self) -> Result<&mut Ship, &'static str> {
        let ships_len = self.ships.len();

        if ships_len == 0 {
            Err("player has no ships")
        } else if !self.ships[ships_len - 1].is_placement() {
            Err("player has no placement ship")
        } else {
            Ok(&mut self.ships[ships_len - 1])
        }
    }
}
<commit_msg>Player: remove mutable result variable from ship_is_next_to()<commit_after>use crate::{direction::Direction, ship::Ship, space::Space};
use std::cmp;

pub struct Player {
    is_cpu: bool,
    spaces: Vec<Space>,
    ships: Vec<Ship>,
    grid_size: [u8; 2],
    grid_cursor: [u8; 2],
}

impl Player {
    pub fn new(grid_size: [u8; 2], ship_count: usize, is_cpu: bool) -> Player {
        Player {
            is_cpu: is_cpu,
            spaces: Space::all_grid_spaces(&grid_size),
            ships: Vec::with_capacity(ship_count),
            grid_size: grid_size,
            grid_cursor: [0, 0],
        }
    }

    /// Selects a space.
    ///
    /// # Errors
    ///
    /// Returns an error if the space at `pos` was already checked.
    pub fn select_space(&mut self, pos: &[u8; 2]) -> Result<(), &'static str> {
        let space_index = self.space_index(pos);
        let ship_hit = self.ships.iter().position(|s| s.pos().contains(pos));
        self.spaces[space_index].set_checked(ship_hit.is_some())?;

        Ok(())
    }

    /// Sets the ship at the given position as sunk if all spaces it occupies
    /// have been checked, and returns whether the ship was sunk.
    ///
    /// # Errors
    ///
    /// Returns an error if no ship is at the given position, or if the ship
    /// state is not active.
    pub fn sink_ship_if_all_hit(&mut self, pos: &[u8; 2]) -> Result<bool, &'static str> {
        if let Some(index) = self.ships.iter().position(|s| s.pos().contains(pos)) {
            let sunk = self.ships[index]
                .pos()
                .iter()
                .all(|p| self.space(p).is_hit());

            if sunk {
                self.ships[index].set_sunk()?;
            }

            Ok(sunk)
        } else {
            Err("no ship at the given position")
        }
    }

    /// Returns whether all of the player's ships have been sunk.
    pub fn all_ships_sunk(&self) -> bool {
        self.ships.iter().all(|ship| ship.is_sunk())
    }

    /// Returns suggestions for the best spaces to check based on the player's hit spaces and ships.
    ///
    /// This is intended for use in cases where the active player is computer-controlled, to
    /// determine the space they check.  However, it could also be used to suggest a space that a
    /// human player could check.
    pub fn suggested_checks(&self) -> Vec<[u8; 2]> {
        let mut select = vec![];
        let directions = Direction::all();
        let hit_spaces = self
            .spaces
            .iter()
            .filter(|s| s.is_hit() && self.ship(s.pos()).unwrap().is_active())
            .collect::<Vec<&Space>>();

        // Check for a line of hit spaces.
        for space in &hit_spaces {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(space.pos(), *direction, true);

                if let Some(pos) = unchecked {
                    if !select.contains(&pos) {
                        select.push(pos);
                    }
                }
            }
        }

        // If a hit space was found, but no hit spaces next to it, look for unchecked spaces next
        // to it.
        if hit_spaces.len() > 0 && select.is_empty() {
            for direction in &directions {
                let unchecked = self.find_unchecked_space(hit_spaces[0].pos(), *direction, false);

                if let Some(pos) = unchecked {
                    select.push(pos);
                }
            }
        }

        // If no candidates have been found yet, just add any unchecked space.
        // TODO: smarter checking for spaces that remaining ships could realistically occupy.
        if select.is_empty() {
            select = self
                .spaces
                .iter()
                .filter(|space| space.is_unchecked())
                .map(|space| *space.pos())
                .collect::<Vec<[u8; 2]>>();
        }

        select
    }

    pub fn add_ship(
        &mut self,
        head: [u8; 2],
        direction: Direction,
        length: u8,
        placement: bool,
    ) -> Result<(), &'static str> {
        if self.ships.len() == self.ships.capacity() {
            Err("tried to add ship to a player with all ships already added")
        } else {
            let pos = self
                .get_ship_position(head, direction, length)
                .ok_or("tried to place a ship partially out of bounds")?;

            if !placement && !self.valid_ship_position(&pos) {
                Err("tried to place a ship in an invalid position")
            } else {
                let mut ship = Ship::new(pos)?;

                if !placement {
                    ship.set_active()?;
                }

                self.ships.push(ship);

                Ok(())
            }
        }
    }

    /// Moves the player's placement ship in the given direction.
    ///
    /// # Errors
    ///
    /// Returns an error if the player does not have a placement ship, or if the ship could not be
    /// moved in `direction` without going out of bounds.
    pub fn move_placement_ship(&mut self, direction: Direction) -> Result<(), &'static str> {
        let index = self.ships.len() - 1;
        let old_head = self.ships[index].pos()[0];
        let new_head = self
            .movement(&old_head, direction)
            .ok_or("movement not possible without going out of bounds")?;
        let ship_pos = self
            .get_ship_position(
                new_head,
                self.ships[index].dir(),
                self.ships[index].len() as u8,
            )
            .ok_or("movement not possible without going out of bounds")?;

        self.ships[index].set_pos(ship_pos)?;

        Ok(())
    }

    pub fn place_placement_ship(&mut self) -> Result<(), &'static str> {
        let index = self.ships.len() - 1;

        // Ensure the ship doesn't overlap with another ship.
        if !self.valid_ship_position(&self.ships[index].pos()) {
            Err("placement ship overlaps with another ship")
        } else {
            self.ships[index].set_active()?;

            Ok(())
        }
    }

    /// Rotates the player's placement ship during the ship placement game state.
    ///
    /// # Errors
    ///
    /// Returns an error if the player does not have a placement ship.
    pub fn rotate_placement_ship(&mut self) -> Result<(), &'static str> {
        let index = self.ships.len() - 1;
        let ship_len = self.ships[index].len() as u8;
        let dir = self.ships[index].dir().rotated();

        // If the current ship position would cause the rotation to position the ship partially out
        // of bounds, adjust the position such that the ship will be entirely within bounds.
        let old_head = self.ships[index].pos()[0];
        let new_head = match dir {
            Direction::North => [
                old_head[0],
                cmp::min(old_head[1], self.grid_size[1] - ship_len),
            ],
            Direction::East => [cmp::max(old_head[0], ship_len - 1), old_head[1]],
            Direction::South => [old_head[0], cmp::max(old_head[1], ship_len - 1)],
            Direction::West => [
                cmp::min(old_head[0], self.grid_size[0] - ship_len),
                old_head[1],
            ],
        };

        // get_ship_position() should always return Some(ship_pos) in this situation.
        let ship_pos = self.get_ship_position(new_head, dir, ship_len).unwrap();
        self.ships[index].set_pos(ship_pos)?;

        Ok(())
    }

    /// Returns a ship position, given its head position, direction and length.
    ///
    /// `direction` refers to the direction the ship is facing, not the
    /// direction in which positions are generated.
    /// Returns `None` if the resulting ship position would not be contained
    /// within the grid.
    pub fn get_ship_position(
        &self,
        head: [u8; 2],
        direction: Direction,
        length: u8,
    ) -> Option<Vec<[u8; 2]>> {
        let valid = match direction {
            Direction::North => head[1] + length <= self.grid_size[1],
            Direction::East => head[0] >= length - 1,
            Direction::South => head[1] >= length - 1,
            Direction::West => head[0] + length <= self.grid_size[0],
        };

        if valid {
            let mut ship = Vec::with_capacity(length as usize);

            for pos in 0..length {
                let pos_u8 = pos as u8;

                ship.push(match direction {
                    Direction::North => [head[0], head[1] + pos_u8],
                    Direction::East => [head[0] - pos_u8, head[1]],
                    Direction::South => [head[0], head[1] - pos_u8],
                    Direction::West => [head[0] + pos_u8, head[1]],
                });
            }

            Some(ship)
        } else {
            None
        }
    }

    /// Checks that the given ship position is valid.
    ///
    /// If the player is CPU-controlled, a ship in a space next to another ship
    /// will be considered invalid.
    fn valid_ship_position(&self, new_ship: &[[u8; 2]]) -> bool {
        new_ship.iter().all(|s| {
            self.valid_space(s)
                && !self.ship_is_in_space(s)
                && !(self.ship_is_next_to(s) && self.is_cpu)
        })
    }

    /// Gets a reference to the ships.
    pub fn ships(&self) -> &[Ship] {
        &self.ships
    }

    /// Gets a reference to a ship if it is in the given position.
    fn ship(&self, pos: &[u8; 2]) -> Option<&Ship> {
        self.ships.iter().find(|s| s.pos().contains(pos))
    }

    /// Returns whether a ship occupies the specified grid coordinates.
    pub fn ship_is_in_space(&self, pos: &[u8; 2]) -> bool {
        self.ships
            .iter()
            .any(|s| s.pos().contains(pos) && !s.is_placement())
    }

    /// Returns whether there is a ship next to the specified grid coordinates.
    fn ship_is_next_to(&self, pos: &[u8; 2]) -> bool {
        let x = pos[0];
        let y = pos[1];

        if x > 0 {
            // Left
            self.ship_is_in_space(&[x - 1, y])
        } else if x < self.grid_size[0] - 1 {
            // Right
            self.ship_is_in_space(&[x + 1, y])
        } else if y > 0 {
            // Above
            self.ship_is_in_space(&[x, y - 1])
        } else if y < self.grid_size[1] - 1 {
            // Below
            self.ship_is_in_space(&[x, y + 1])
        } else {
            false
        }
    }

    /// Gets a reference to the spaces.
    pub fn spaces(&self) -> &[Space] {
        &self.spaces
    }

    /// Returns whether the given position is valid.
    fn valid_space(&self, pos: &[u8; 2]) -> bool {
        pos[0] < self.grid_size[0] && pos[1] < self.grid_size[1]
    }

    /// Gets a reference to the space with the given position.
    pub fn space(&self, pos: &[u8; 2]) -> &Space {
        self.spaces.get(self.space_index(pos)).unwrap()
    }

    /// Calculates the index of the given position in the spaces vector.
    fn space_index(&self, pos: &[u8; 2]) -> usize {
        self.grid_size[0] as usize * pos[0] as usize + pos[1] as usize
    }

    /// Returns the coordinates of a movement from `pos` in a `direction`.
    /// Returns `None` if the movement is not possible.
    fn movement(&self, pos: &[u8; 2], direction: Direction) -> Option<[u8; 2]> {
        let valid = match direction {
            Direction::North => pos[1] > 0,
            Direction::East => pos[0] < self.grid_size[0] - 1,
            Direction::South => pos[1] < self.grid_size[1] - 1,
            Direction::West => pos[0] > 0,
        };

        match valid {
            true => Some(match direction {
                Direction::North => [pos[0], pos[1] - 1],
                Direction::East => [pos[0] + 1, pos[1]],
                Direction::South => [pos[0], pos[1] + 1],
                Direction::West => [pos[0] - 1, pos[1]],
            }),
            false => None,
        }
    }

    /// Finds the first non-hit, unchecked space in a `direction` from `pos`.
    /// Can also make sure the space is at the end of a `line` of hit spaces.
    /// Returns `None` if the first non-hit space has been checked or if a grid
    /// boundary is reached.
    fn find_unchecked_space(
        &self,
        pos: &[u8; 2],
        direction: Direction,
        check_for_line: bool,
    ) -> Option<[u8; 2]> {
        let mut check_pos = self.movement(pos, direction);

        while let Some(next_pos) = check_pos {
            let next_space = self.space(&next_pos);

            match next_space.is_hit() {
                true => check_pos = self.movement(&next_pos, direction),
                false => {
                    if !next_space.is_unchecked() {
                        check_pos = None;
                    }
                    break;
                }
            };
        }

        if check_for_line && check_pos.is_some() {
            let unchecked = check_pos.unwrap();
            let opposite_dir = direction.opposite();
            let prev_pos = self.movement(&unchecked, opposite_dir).unwrap();

            if &prev_pos == pos {
                check_pos = None;
            }
        }

        check_pos
    }

    /// Returns the coordinates of the player's grid cursor.
    pub fn grid_cursor(&self) -> &[u8; 2] {
        &self.grid_cursor
    }

    /// Moves the player's grid cursor in the given `direction`.
    ///
    /// # Errors
    ///
    /// Returns an error if moving the grid cursor in `direction` would move it out of bounds.
    pub fn move_grid_cursor(&mut self, direction: Direction) -> Result<(), &'static str> {
        if let Some(new_cursor) = self.movement(&self.grid_cursor, direction) {
            self.set_grid_cursor(&new_cursor)?;

            Ok(())
        } else {
            Err("tried to move grid cursor out of bounds")
        }
    }

    /// Sets the player's grid cursor position.
    ///
    /// # Errors
    ///
    /// Returns an error if no space exists at `pos`.
    pub fn set_grid_cursor(&mut self, pos: &[u8; 2]) -> Result<(), &'static str> {
        if self.space_index(pos) < self.spaces.len() {
            self.grid_cursor = *pos;

            Ok(())
        } else {
            Err("tried to set the grid cursor to a nonexistent space")
        }
    }

    pub fn is_cpu(&self) -> bool {
        self.is_cpu
    }

    pub fn placement_ship(&self) -> Result<&Ship, &'static str> {
        let ships_len = self.ships.len();

        if ships_len == 0 {
            Err("player has no ships")
        } else if !self.ships[ships_len - 1].is_placement() {
            Err("player has no placement ship")
        } else {
            Ok(&self.ships[self.ships.len() - 1])
        }
    }

    pub fn placement_ship_mut(&mut self) -> Result<&mut Ship, &'static str> {
        let ships_len = self.ships.len();

        if ships_len == 0 {
            Err("player has no ships")
        } else if !self.ships[ships_len - 1].is_placement() {
            Err("player has no placement ship")
        } else {
            Ok(&mut self.ships[ships_len - 1])
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::{PartialOrd, Ordering};
use std::iter::repeat;
use std::usize;

use frame::Frame;
use draw::RGB;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Point {
   pub x: i64,
   pub y: i64,
}

impl Point {
   #[inline]
   pub fn new(x: i64, y: i64) -> Self {
      Point {
         x: x,
         y: y,
      }
   }
}

impl Default for Point {
   fn default() -> Point {
      Point::new(0, 0)
   }
}

impl PartialOrd for Point {
   fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
      Some(self.cmp(other))
   }
}

impl Ord for Point {
   fn cmp(&self, other: &Point) -> Ordering {
      if self.y < other.y {
         Ordering::Less
      } else if self.y > other.y {
         Ordering::Greater
      } else if self.x < other.x {
         Ordering::Less
      } else if self.x > other.x {
         Ordering::Greater
      } else {
         Ordering::Equal
      }
   }
}

#[derive(Debug, Clone, Copy)]
pub struct Segment {
   pub p1: usize,
   pub p2: usize,
}

impl Segment {
   #[inline]
   pub fn new(p1: usize, p2: usize) -> Self {
      Segment {
         p1: p1,
         p2: p2,
      }
   }
}

impl Default for Segment {
   fn default() -> Segment {
      Segment::new(0, 0)
   }
}

#[derive(Debug, Clone, Copy)]
pub struct Circle {
   pub center: usize,
   pub radius: i64,
}

impl Circle {
   #[inline]
   pub fn new(center: usize, radius: i64) -> Self {
      Circle {
         center: center,
         radius: radius,
      }
   }
}

impl Default for Circle {
   fn default() -> Circle {
      Circle::new(0, 0)
   }
}

#[derive(Debug, Clone, Copy)]
pub enum EdgeType {
   LTR,  // line top-right
   LTL,  // line top-left
   LBR,  // line bottom-right
   LBL,  // line bottom-left

   LHR,  // line horizontal-right
   LHL,  // line horizontal-left
   LVT,  // line vertical-top
   LVB,  // line vertical-bottom

   CTR, // clockwise arc top-right
   CTL, // clockwise arc top-left
   CBR, // clockwise arc bottom-right
   CBL, // clockwise arc bottom-left

   ATR, // anti-clockwise arc top-right
   ATL, // anti-clockwise arc top-left
   ABR, // anti-clockwise arc bottom-right
   ABL, // anti-clockwise arc bottom-left
}

impl EdgeType {
   #[inline]
   pub fn reversed(&self) -> bool {
      match *self {
         EdgeType::LBR | EdgeType::LBL | EdgeType::LHL | EdgeType::LVB |
         EdgeType::CBR | EdgeType::CBL | EdgeType::ATR | EdgeType::ATL => {
            true
         },
         _ => {
            false
         }
      }
   }
}

#[derive(Debug, Clone, Copy)]
pub struct Edge {
   pub edge_type: EdgeType,
   pub segment: usize,
   pub circle: usize,
}

impl Edge {
   #[inline]
   pub fn new(edge_type: EdgeType, segment: usize, circle: usize) -> Self {
      Edge {
         edge_type: edge_type,
         segment: segment,
         circle: circle,
      }
   }

   #[inline]
   pub fn reversed(&self) -> bool {
      self.edge_type.reversed()
   }
}

impl Default for Edge {
   fn default() -> Edge {
      Edge::new(EdgeType::LTR, 0, 0)
   }
}

#[derive(Debug, Clone)]
pub struct Poly {
   pub start: usize,
   pub end: usize,
   pub color: usize,
}

impl Poly {
   #[inline]
   pub fn new(start: usize, end: usize, color: usize) -> Self {
      Poly {
         start: start,
         end: end,
         color: color,
      }
   }
}

impl Default for Poly {
   fn default() -> Poly {
      Poly::new(0, 0, 0)
   }
}

pub struct Scene {
   pub points: Vec<Point>,
   pub segments: Vec<Segment>,
   pub circles: Vec<Circle>,
   pub edges: Vec<Edge>,
   pub polys: Vec<Poly>,
   pub colors: Vec<RGB>,
}

#[derive(Debug, Clone)]
pub struct PolyRef {
   pub start: usize,
   pub end: usize,
   pub src: usize,
}

impl PolyRef {
   #[inline]
   pub fn new(start: usize, end: usize, src: usize) -> Self {
      PolyRef {
         start: start,
         end: end,
         src: src,
      }
   }
}

impl Default for PolyRef {
   fn default() -> PolyRef {
      PolyRef::new(0, 0, 0)
   }
}

#[derive(Debug, Clone)]
pub struct IntersectionRef {
   first_px: i64,
   start: usize,
   end: usize,
}

impl IntersectionRef {
   #[inline]
   pub fn new(first_px: i64, start: usize, end: usize) -> Self {
      IntersectionRef {
         first_px: first_px,
         start: start,
         end: end,
      }
   }
}

impl Default for IntersectionRef {
   #[inline]
   fn default() -> IntersectionRef {
      IntersectionRef::new(0, 0, 0)
   }
}

pub struct Rasterizer {
   pub points: Vec<Point>,
   pub segments: Vec<Segment>,
   pub edges: Vec<Edge>,
   pub polys: Vec<PolyRef>,

   pub points_end: usize,
   pub segments_end: usize,
   pub edges_end: usize,
   pub polys_end: usize,

   pub vert_intersections_ref: Vec<IntersectionRef>,
   pub hori_intersections_ref: Vec<IntersectionRef>,
   pub vert_intersections: Vec<i64>,
   pub hori_intersections: Vec<i64>,
}

impl Rasterizer {
   pub fn new() -> Self {
      let points = create_default_vec(65536);
      let segments = create_default_vec(65536);
      let edges = create_default_vec(65536);
      let polys = create_default_vec(65536);

      let vert_intersections_ref = create_default_vec(65536);
      let hori_intersections_ref = create_default_vec(65536);
      let vert_intersections = create_default_vec(65536);
      let hori_intersections = create_default_vec(65536);

      Rasterizer {
         points: points,
         segments: segments,
         edges: edges,
         polys: polys,

         points_end: 0,
         segments_end: 0,
         edges_end: 0,
         polys_end: 0,

         vert_intersections_ref: vert_intersections_ref,
         hori_intersections_ref: hori_intersections_ref,
         vert_intersections: vert_intersections,
         hori_intersections: hori_intersections,
      }
   }

   #[allow(unused_variables)]
   pub fn render(&mut self, scene: &Scene, frame: &mut Frame) {
      self.tranfer_scene(scene);

      self.check_correctness(scene);
   }

   pub fn tranfer_scene(&mut self, scene: &Scene) {
      self.points_end = scene.points.len();
      for i in 0..self.points_end {
         self.points[i] = scene.points[i];
      }

      self.segments_end = scene.segments.len();
      for i in 0..self.segments_end {
         self.segments[i] = scene.segments[i];
      }

      self.edges_end = scene.edges.len();
      for i in 0..self.edges_end {
         self.edges[i] = scene.edges[i];
      }

      self.polys_end = scene.polys.len();
      for i in 0..self.polys_end {
         self.polys[i].start = scene.polys[i].start;
         self.polys[i].end = scene.polys[i].end;
         self.polys[i].src = i;
      }
   }

   pub fn check_correctness(&self, scene: &Scene) {
      for poly in &self.polys[0..self.polys_end] {
         self.check_poly_connected(poly);
         self.check_segments_orientation(poly);
         self.check_edges_orientation(poly);
      }

      self.check_index_coverage(scene);
   }

   fn check_poly_connected(&self, poly: &PolyRef) {
      let mut prev = self.edge_head(&self.edges[poly.end - 1]);

      for edge_index in poly.start..poly.end {
         let ref edge = self.edges[edge_index];

         let current = self.edge_tail(edge);
         if current != prev {
            panic!("Unclosed poly : {:?}", poly);
         }

         prev = self.edge_head(edge);
      }
   }

   fn check_segments_orientation(&self, poly: &PolyRef) {
      for edge_index in poly.start..poly.end {
         let ref edge = self.edges[edge_index];
         let ref segment = self.segments[edge.segment];

         let ref less = self.points[segment.p1];
         let ref greater = self.points[segment.p2];

         if greater <= less {
            panic!("Wrong segment orientation : {:?} / {:?}", edge, segment);
         }
      }
   }

   fn check_edges_orientation(&self, poly: &PolyRef) {
      for edge_index in poly.start..poly.end {
         let ref edge = self.edges[edge_index];

         let tail = self.edge_tail(edge);
         let head = self.edge_head(edge);

         let reversed = edge.reversed();

         if (reversed && tail <= head) || (!reversed && head <= tail) {
            panic!("Wrong edge orientation : {:?}", edge);
         }
      }
   }

   fn check_index_coverage(&self, scene: &Scene) {
      self.check_edge_index_coverage();
      self.check_circle_index_coverage(scene);
      self.check_segment_index_coverage();
      self.check_point_index_coverage(scene);
   }

   fn check_edge_index_coverage(&self) {
      let len = self.edges_end;
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for poly in &self.polys[0..self.polys_end] {
         for edge_index in poly.start..poly.end {
            coverage[edge_index] = true;
         }
      }

      if coverage.contains(&false) {
         panic!("Unreferenced edge found");
      }
   }

   fn check_circle_index_coverage(&self, scene: &Scene) {
      let len = scene.circles.len();
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for edge in &self.edges[0..self.edges_end] {
         if edge.circle != usize::MAX {
            coverage[edge.circle] = true;
         }
      }

      if coverage.contains(&false) {
         panic!("Unreferenced circle found");
      }
   }

   fn check_segment_index_coverage(&self) {
      let len = self.segments_end;
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for edge in &self.edges[0..self.edges_end] {
         coverage[edge.segment] = true;
      }

      if coverage.contains(&false) {
         panic!("Unreferenced segment found");
      }
   }

   fn check_point_index_coverage(&self, scene: &Scene) {
      let len = self.points_end;
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for circle in &scene.circles {
         coverage[circle.center] = true;
      }

      for segment in &self.segments[0..self.segments_end] {
         coverage[segment.p1] = true;
         coverage[segment.p2] = true;
      }

      if coverage.contains(&false) {
         panic!("Unreferenced segment found");
      }
   }

   // TODO: Combine into edge_head_tail method?

   fn edge_head(&self, edge: &Edge) -> &Point {
      let ref segment = self.segments[edge.segment];
      if edge.reversed() {
         &self.points[segment.p1]
      } else {
         &self.points[segment.p2]
      }
   }

   fn edge_tail(&self, edge: &Edge) -> &Point {
      let ref segment = self.segments[edge.segment];
      if edge.reversed() {
         &self.points[segment.p2]
      } else {
         &self.points[segment.p1]
      }
   }
}

pub fn create_default_vec<T>(capacity: usize) -> Vec<T> where T: Default + Clone {
   repeat(T::default()).take(capacity).collect()
}

<commit_msg>Transferring the intersection code<commit_after>use std::cmp::{PartialOrd, Ordering};
use std::iter::repeat;
use std::{usize, i64};

use frame::Frame;
use draw::RGB;

pub const HALF_MAX_ERR: i64  = i64::MAX / 2;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Point {
   pub x: i64,
   pub y: i64,
}

impl Point {
   #[inline]
   pub fn new(x: i64, y: i64) -> Self {
      Point {
         x: x,
         y: y,
      }
   }
}

impl Default for Point {
   fn default() -> Point {
      Point::new(0, 0)
   }
}

impl PartialOrd for Point {
   fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
      Some(self.cmp(other))
   }
}

impl Ord for Point {
   fn cmp(&self, other: &Point) -> Ordering {
      if self.y < other.y {
         Ordering::Less
      } else if self.y > other.y {
         Ordering::Greater
      } else if self.x < other.x {
         Ordering::Less
      } else if self.x > other.x {
         Ordering::Greater
      } else {
         Ordering::Equal
      }
   }
}

#[derive(Debug, Clone, Copy)]
pub struct Segment {
   pub p1: usize,
   pub p2: usize,
}

impl Segment {
   #[inline]
   pub fn new(p1: usize, p2: usize) -> Self {
      Segment {
         p1: p1,
         p2: p2,
      }
   }
}

impl Default for Segment {
   fn default() -> Segment {
      Segment::new(0, 0)
   }
}

#[derive(Debug, Clone, Copy)]
pub struct Circle {
   pub center: usize,
   pub radius: i64,
}

impl Circle {
   #[inline]
   pub fn new(center: usize, radius: i64) -> Self {
      Circle {
         center: center,
         radius: radius,
      }
   }
}

impl Default for Circle {
   fn default() -> Circle {
      Circle::new(0, 0)
   }
}

#[derive(Debug, Clone, Copy)]
pub enum EdgeType {
   LTR,  // line top-right
   LTL,  // line top-left
   LBR,  // line bottom-right
   LBL,  // line bottom-left

   LHR,  // line horizontal-right
   LHL,  // line horizontal-left
   LVT,  // line vertical-top
   LVB,  // line vertical-bottom

   CTR, // clockwise arc top-right
   CTL, // clockwise arc top-left
   CBR, // clockwise arc bottom-right
   CBL, // clockwise arc bottom-left

   ATR, // anti-clockwise arc top-right
   ATL, // anti-clockwise arc top-left
   ABR, // anti-clockwise arc bottom-right
   ABL, // anti-clockwise arc bottom-left
}

impl EdgeType {
   #[inline]
   pub fn reversed(&self) -> bool {
      match *self {
         EdgeType::LBR | EdgeType::LBL | EdgeType::LHL | EdgeType::LVB |
         EdgeType::CBR | EdgeType::CBL | EdgeType::ATR | EdgeType::ATL => {
            true
         },
         _ => {
            false
         }
      }
   }
}

#[derive(Debug, Clone, Copy)]
pub struct Edge {
   pub edge_type: EdgeType,
   pub segment: usize,
   pub circle: usize,
}

impl Edge {
   #[inline]
   pub fn new(edge_type: EdgeType, segment: usize, circle: usize) -> Self {
      Edge {
         edge_type: edge_type,
         segment: segment,
         circle: circle,
      }
   }

   #[inline]
   pub fn reversed(&self) -> bool {
      self.edge_type.reversed()
   }
}

impl Default for Edge {
   fn default() -> Edge {
      Edge::new(EdgeType::LTR, 0, 0)
   }
}

#[derive(Debug, Clone)]
pub struct Poly {
   pub start: usize,
   pub end: usize,
   pub color: usize,
}

impl Poly {
   #[inline]
   pub fn new(start: usize, end: usize, color: usize) -> Self {
      Poly {
         start: start,
         end: end,
         color: color,
      }
   }
}

impl Default for Poly {
   fn default() -> Poly {
      Poly::new(0, 0, 0)
   }
}

pub struct Scene {
   pub points: Vec<Point>,
   pub segments: Vec<Segment>,
   pub circles: Vec<Circle>,
   pub edges: Vec<Edge>,
   pub polys: Vec<Poly>,
   pub colors: Vec<RGB>,
}

#[derive(Debug, Clone)]
pub struct PolyRef {
   pub start: usize,
   pub end: usize,
   pub src: usize,
}

impl PolyRef {
   #[inline]
   pub fn new(start: usize, end: usize, src: usize) -> Self {
      PolyRef {
         start: start,
         end: end,
         src: src,
      }
   }
}

impl Default for PolyRef {
   fn default() -> PolyRef {
      PolyRef::new(0, 0, 0)
   }
}

#[derive(Debug, Clone)]
pub struct IntersectionRef {
   first_px: i64,
   start: usize,
   end: usize,
}

impl IntersectionRef {
   #[inline]
   pub fn new(first_px: i64, start: usize, end: usize) -> Self {
      IntersectionRef {
         first_px: first_px,
         start: start,
         end: end,
      }
   }
}

impl Default for IntersectionRef {
   #[inline]
   fn default() -> IntersectionRef {
      IntersectionRef::new(0, 0, 0)
   }
}

pub struct Rasterizer {
   pub points: Vec<Point>,
   pub segments: Vec<Segment>,
   pub edges: Vec<Edge>,
   pub polys: Vec<PolyRef>,

   pub vert_intersections_ref: Vec<IntersectionRef>,
   pub hori_intersections_ref: Vec<IntersectionRef>,
   pub vert_intersections: Vec<i64>,
   pub hori_intersections: Vec<i64>,

   pub points_end: usize,
   pub segments_end: usize,
   pub edges_end: usize,
   pub polys_end: usize,
   pub vert_intersections_end: usize,
   pub hori_intersections_end: usize,
}

impl Rasterizer {
   pub fn new() -> Self {
      let points = create_default_vec(65536);
      let segments = create_default_vec(65536);
      let edges = create_default_vec(65536);
      let polys = create_default_vec(65536);

      let vert_intersections_ref = create_default_vec(65536);
      let hori_intersections_ref = create_default_vec(65536);
      let vert_intersections = create_default_vec(65536);
      let hori_intersections = create_default_vec(65536);

      Rasterizer {
         points: points,
         segments: segments,
         edges: edges,
         polys: polys,

         vert_intersections_ref: vert_intersections_ref,
         hori_intersections_ref: hori_intersections_ref,
         vert_intersections: vert_intersections,
         hori_intersections: hori_intersections,

         points_end: 0,
         segments_end: 0,
         edges_end: 0,
         polys_end: 0,
         vert_intersections_end: 0,
         hori_intersections_end: 0,
      }
   }

   #[allow(unused_variables)]
   pub fn render(&mut self, scene: &Scene, frame: &mut Frame) {
      self.tranfer_scene(scene);

      self.check_correctness(scene);

      self.intersect_edges();
   }

   pub fn tranfer_scene(&mut self, scene: &Scene) {
      self.points_end = scene.points.len();
      for i in 0..self.points_end {
         self.points[i] = scene.points[i];
      }

      self.segments_end = scene.segments.len();
      for i in 0..self.segments_end {
         self.segments[i] = scene.segments[i];

         self.vert_intersections_ref[i].start = usize::MAX;
      }

      self.edges_end = scene.edges.len();
      for i in 0..self.edges_end {
         self.edges[i] = scene.edges[i];
      }

      self.polys_end = scene.polys.len();
      for i in 0..self.polys_end {
         self.polys[i].start = scene.polys[i].start;
         self.polys[i].end = scene.polys[i].end;
         self.polys[i].src = i;
      }
   }

   pub fn check_correctness(&self, scene: &Scene) {
      for poly in &self.polys[0..self.polys_end] {
         self.check_poly_connected(poly);
         self.check_segments_orientation(poly);
         self.check_edges_orientation(poly);
      }

      self.check_index_coverage(scene);
   }

   fn check_poly_connected(&self, poly: &PolyRef) {
      let mut prev = self.edge_head(&self.edges[poly.end - 1]);

      for edge in &self.edges[poly.start..poly.end] {
         let current = self.edge_tail(edge);
         if current != prev {
            panic!("Unclosed poly : {:?}", poly);
         }

         prev = self.edge_head(edge);
      }
   }

   fn check_segments_orientation(&self, poly: &PolyRef) {
      for edge in &self.edges[poly.start..poly.end] {
         let ref segment = self.segments[edge.segment];

         let ref less = self.points[segment.p1];
         let ref greater = self.points[segment.p2];

         if greater <= less {
            panic!("Wrong segment orientation : {:?} / {:?}", edge, segment);
         }
      }
   }

   fn check_edges_orientation(&self, poly: &PolyRef) {
      for edge in &self.edges[poly.start..poly.end] {
         let tail = self.edge_tail(edge);
         let head = self.edge_head(edge);

         let reversed = edge.reversed();

         if (reversed && tail <= head) || (!reversed && head <= tail) {
            panic!("Wrong edge orientation : {:?}", edge);
         }
      }
   }

   fn check_index_coverage(&self, scene: &Scene) {
      self.check_edge_index_coverage();
      self.check_circle_index_coverage(scene);
      self.check_segment_index_coverage();
      self.check_point_index_coverage(scene);
   }

   fn check_edge_index_coverage(&self) {
      let len = self.edges_end;
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for poly in &self.polys[..self.polys_end] {
         for edge_index in poly.start..poly.end {
            coverage[edge_index] = true;
         }
      }

      if coverage.contains(&false) {
         panic!("Unreferenced edge found");
      }
   }

   fn check_circle_index_coverage(&self, scene: &Scene) {
      let len = scene.circles.len();
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for edge in &self.edges[..self.edges_end] {
         if edge.circle != usize::MAX {
            coverage[edge.circle] = true;
         }
      }

      if coverage.contains(&false) {
         panic!("Unreferenced circle found");
      }
   }

   fn check_segment_index_coverage(&self) {
      let len = self.segments_end;
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for edge in &self.edges[..self.edges_end] {
         coverage[edge.segment] = true;
      }

      if coverage.contains(&false) {
         panic!("Unreferenced segment found");
      }
   }

   fn check_point_index_coverage(&self, scene: &Scene) {
      let len = self.points_end;
      let mut coverage: Vec<bool> = repeat(false).take(len).collect();

      for circle in &scene.circles {
         coverage[circle.center] = true;
      }

      for segment in &self.segments[0..self.segments_end] {
         coverage[segment.p1] = true;
         coverage[segment.p2] = true;
      }

      if coverage.contains(&false) {
         panic!("Unreferenced segment found");
      }
   }

   // TODO: Combine into edge_head_tail method?

   fn edge_head(&self, edge: &Edge) -> &Point {
      let ref segment = self.segments[edge.segment];
      if edge.reversed() {
         &self.points[segment.p1]
      } else {
         &self.points[segment.p2]
      }
   }

   fn edge_tail(&self, edge: &Edge) -> &Point {
      let ref segment = self.segments[edge.segment];
      if edge.reversed() {
         &self.points[segment.p2]
      } else {
         &self.points[segment.p1]
      }
   }

   fn intersect_edges(&mut self) {
      self.vert_intersections_end = 0;
      self.hori_intersections_end = 0;

      for edge in &self.edges[..self.edges_end] {
         let i = edge.segment;

         let ref mut vert_ref = self.vert_intersections_ref[i];
         if vert_ref.start == usize::MAX {
            continue;
         }

         let ref mut hori_ref = self.hori_intersections_ref[i];

         let segment = self.segments[i];
         let p1 = self.points[segment.p1];
         let p2 = self.points[segment.p2];

         vert_ref.start = self.vert_intersections_end;
         hori_ref.start = self.hori_intersections_end;

         let (vert_end, x_first_px) = v_multi_intersect_fast(
            p1, p2, 1000, self.vert_intersections_end, &mut self.vert_intersections
         );

         let (hori_end, y_first_px) = h_multi_intersect_fast(
            p1, p2, 1000, self.hori_intersections_end, &mut self.hori_intersections
         );

         self.vert_intersections_end = vert_end;
         vert_ref.end = vert_end;
         vert_ref.first_px = x_first_px;

         self.hori_intersections_end = hori_end;
         hori_ref.end = hori_end;
         hori_ref.first_px = y_first_px;
      }
   }
}

fn create_default_vec<T>(capacity: usize) -> Vec<T> where T: Default + Clone {
   repeat(T::default()).take(capacity).collect()
}

fn h_multi_intersect_fast(p1: Point, p2: Point, step_y: i64, mut vec_start: usize, inters: &mut Vec<i64>) -> (usize, i64) {
   let (p1, p2) = if p1.y > p2.y {
      (p2, p1)
   } else {
      (p1, p2)
   };

   let start = 1 + p1.y / step_y;
   let end = 1 + (p2.y - 1) / step_y;

   let dy = p2.y - p1.y;
   let dx = p2.x - p1.x;
   let dx_signum = dx.signum();

   let step_x = dx * step_y / dy;

   let max_div_dy = i64::MAX / dy;

   let err_step = max_div_dy * (step_y * dx * dx_signum - step_x * dx_signum * dy);

   let first_y = start * step_y;

   let fdy = first_y - p1.y;
   let fdx = dx * fdy / dy;

   let mut x = p1.x + fdx;

   if err_step == 0 {
      for _ in start..end {
         inters[vec_start] = x;
         vec_start += 1;

         x += step_x;
      }

      return (vec_start, first_y);
   }

   let mut err = max_div_dy * (fdy * dx * dx_signum - fdx * dx_signum * dy) - HALF_MAX_ERR;

   for _ in start..end {
      if err > 0 {
         x += dx_signum;
         err -= i64::MAX;
      }

      inters[vec_start] = x;
      vec_start += 1;

      x += step_x;

      err += err_step;
   }

   (vec_start, first_y)
}

fn v_multi_intersect_fast(p1: Point, p2: Point, step_x: i64, mut vec_start: usize, inters: &mut Vec<i64>) -> (usize, i64) {
   let (p1, p2) = if p1.x > p2.x {
      (p2, p1)
   } else {
      (p1, p2)
   };

   let start = 1 + p1.x / step_x;
   let end = 1 + (p2.x - 1) / step_x;

   let dx = p2.x - p1.x;
   let dy = p2.y - p1.y;
   let dy_signum = dy.signum();

   let step_y = dy * step_x / dx;

   let max_div_dx = i64::MAX / dx;

   let err_step = max_div_dx * (step_x * dy * dy_signum - step_y * dy_signum * dx);

   let first_x = start * step_x;

   let fdx = first_x - p1.x;
   let fdy = dy * fdx / dx;

   let mut y = p1.y + fdy;

   if err_step == 0 {
      for _ in start..end {
         inters[vec_start] = y;
         vec_start += 1;

         y += step_y;
      }

      return (vec_start, first_x);
   }

   let mut err = max_div_dx * (fdx * dy * dy_signum - fdy * dy_signum * dx) - HALF_MAX_ERR;

   for _ in start..end {
      if err > 0 {
         y += dy_signum;
         err -= i64::MAX;
      }

      inters[vec_start] = y;
      vec_start += 1;

      y += step_y;

      err += err_step;
   }

   (vec_start, first_x)
}
<|endoftext|>


<commit_before>/// Reader is used for reading items from datasource (e.g. stdin or command output)
///
/// After reading in a line, reader will save an item into the pool(items)
use crate::casmutex::CasMutex;
use crate::event::{Event, EventArg, EventReceiver, EventSender};
use crate::field::FieldRange;
use crate::item::Item;
use crate::options::SkimOptions;
use crate::sender::CachedSender;
use regex::Regex;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::io::{BufRead, BufReader};
use std::mem;
use std::process::{Child, Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender, SyncSender};
use std::sync::{Arc, RwLock};
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;

const DELIMITER_STR: &str = r"[\t\n ]+";

struct ReaderOption {
    pub use_ansi_color: bool,
    pub default_arg: String,
    pub transform_fields: Vec<FieldRange>,
    pub matching_fields: Vec<FieldRange>,
    pub delimiter: Regex,
    pub replace_str: String,
    pub line_ending: u8,
}

impl ReaderOption {
    pub fn new() -> Self {
        ReaderOption {
            use_ansi_color: false,
            default_arg: String::new(),
            transform_fields: Vec::new(),
            matching_fields: Vec::new(),
            delimiter: Regex::new(DELIMITER_STR).unwrap(),
            replace_str: "{}".to_string(),
            line_ending: b'\n',
        }
    }

    pub fn with_options(options: &SkimOptions) -> Self {
        let mut reader_option = Self::new();
        reader_option.parse_options(&options);
        reader_option
    }

    fn parse_options(&mut self, options: &SkimOptions) {
        if options.ansi {
            self.use_ansi_color = true;
        }

        if let Some(delimiter) = options.delimiter {
            self.delimiter = Regex::new(delimiter).unwrap_or_else(|_| Regex::new(DELIMITER_STR).unwrap());
        }

        if let Some(transform_fields) = options.with_nth {
            self.transform_fields = transform_fields
                .split(',')
                .filter_map(|string| FieldRange::from_str(string))
                .collect();
        }

        if let Some(matching_fields) = options.nth {
            self.matching_fields = matching_fields
                .split(',')
                .filter_map(|string| FieldRange::from_str(string))
                .collect();
        }

        if options.read0 {
            self.line_ending = b'\0';
        }
    }
}

pub struct Reader {
    rx_cmd: EventReceiver,
    items: Arc<CasMutex<Vec<Item>>>,
    option: Arc<ReaderOption>,
    data_source: Option<Box<BufRead + Send>>, // used to support piped output
}

impl Reader {
    pub fn new(
        rx_cmd: EventReceiver,
        items: Arc<CasMutex<Vec<Item>>>,
        data_source: Option<Box<BufRead + Send>>,
    ) -> Self {
        Reader {
            rx_cmd,
            items,
            option: Arc::new(ReaderOption::new()),
            data_source,
        }
    }

    pub fn parse_options(mut self, options: &SkimOptions) -> Self {
        self.option = Arc::new(ReaderOption::with_options(&options));
        self
    }

    pub fn run(&mut self) {
        // event loop
        let mut thread_reader: Option<JoinHandle<()>> = None;
        let reader_stopped: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));

        let mut last_command = "".to_string();
        let mut last_query = "".to_string();

        while let Ok((ev, arg)) = self.rx_cmd.recv() {
            match ev {
                Event::EvReaderRestart => {
                    // close existing command or file if exists
                    let (cmd, query, force_update) = *arg
                        .downcast::<(String, String, bool)>()
                        .expect("reader:EvReaderRestart: failed to get argument");
                    if !force_update && cmd == last_command && query == last_query {
                        continue;
                    }

                    // restart command with new `command`
                    if cmd != last_command {
                        // stop existing command
                        reader_stopped.store(true, Ordering::SeqCst);
                        thread_reader.take().map(|thrd| thrd.join());

                        // create needed data for thread
                        reader_stopped.store(false, Ordering::SeqCst);
                        let cmd_clone = cmd.clone();
                        let option_clone = self.option.clone();
                        let query_clone = query.clone();
                        let data_source = self.data_source.take();
                        let stopped = reader_stopped.clone();

                        // start the new command
                        let items_clone = self.items.clone();
                        thread_reader = Some(thread::spawn(move || {
                            reader(&cmd_clone, stopped, items_clone, option_clone, data_source);
                        }));
                    }

                    last_command = cmd;
                    last_query = query;
                }

                ev @ Event::EvActAccept | ev @ Event::EvActAbort => {
                    // stop existing command
                    reader_stopped.store(true, Ordering::SeqCst);
                    thread_reader.take().map(|thrd| thrd.join());
                    let tx_ack: Sender<usize> = *arg.downcast().expect("reader:EvActAccept: failed to get argument");
                    let _ = tx_ack.send(0);

                    // pass the event to sender
                    let _ = tx_sender.send((ev, Box::new(true)));

                    // quit the loop
                    break;
                }

                _ => {
                    // do nothing
                }
            }
        }
    }
}

fn get_command_output(cmd: &str) -> Result<(Option<Child>, Box<BufRead + Send>), Box<Error>> {
    let shell = env::var("SHELL").unwrap_or_else(|_| "sh".to_string());
    let mut command = Command::new(shell)
        .arg("-c")
        .arg(cmd)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn()?;

    let stdout = command
        .stdout
        .take()
        .ok_or_else(|| "command output: unwrap failed".to_owned())?;

    Ok((Some(command), Box::new(BufReader::new(stdout))))
}

// Consider that you invoke a command with different arguments several times
// If you select some items each time, how will skim remeber it?
// => Well, we'll give each invokation a number, i.e. RUN_NUM
// What if you invoke the same command and same arguments twice?
// => We use NUM_MAP to specify the same run number.
lazy_static! {
    static ref RUN_NUM: RwLock<usize> = RwLock::new(0);
    static ref NUM_MAP: RwLock<HashMap<String, usize>> = RwLock::new(HashMap::new());
}

fn reader(
    cmd: &str,
    stopped: Arc<AtomicBool>,
    items: Arc<CasMutex<Vec<Item>>>,
    option: Arc<ReaderOption>,
    source_file: Option<Box<BufRead + Send>>,
) {
    let (command, mut source) = source_file
        .map(|f| (None, f))
        .unwrap_or_else(|| get_command_output(cmd).expect("command not found"));

    let command_stopped = Arc::new(AtomicBool::new(false));

    let stopped_clone = stopped.clone();
    let command_stopped_clone = command_stopped.clone();
    thread::spawn(move || {
        // kill command if it is got
        while command.is_some() && !stopped_clone.load(Ordering::Relaxed) {
            thread::sleep(Duration::from_millis(5));
        }

        // clean up resources
        if let Some(mut x) = command {
            let _ = x.kill();
            let _ = x.wait();
        }
        command_stopped_clone.store(true, Ordering::Relaxed);
    });

    let opt = option.read().expect("reader: failed to lock option");

    // set the proper run number
    let run_num = { *RUN_NUM.read().expect("reader: failed to lock RUN_NUM") };
    let run_num = *NUM_MAP
        .write()
        .expect("reader: failed to lock NUM_MAP")
        .entry(cmd.to_string())
        .or_insert_with(|| {
            *(RUN_NUM.write().expect("reader: failed to lock RUN_NUM for write")) = run_num + 1;
            run_num + 1
        });

    let mut index = 0;
    let mut buffer = Vec::with_capacity(100);
    loop {
        buffer.clear();
        // start reading
        match source.read_until(opt.line_ending, &mut buffer) {
            Ok(n) => {
                if n == 0 {
                    break;
                }
                debug!("reader:reader: read a new line. index = {}", index);

                if buffer.ends_with(&[b'\r', b'\n']) {
                    buffer.pop();
                    buffer.pop();
                } else if buffer.ends_with(&[b'\n']) || buffer.ends_with(&[b'\0']) {
                    buffer.pop();
                }

                debug!("reader:reader: create new item. index = {}", index);
                let item = Item::new(
                    String::from_utf8_lossy(&buffer),
                    opt.use_ansi_color,
                    &opt.transform_fields,
                    &opt.matching_fields,
                    &opt.delimiter,
                    (run_num, index),
                );

                {
                    // save item into pool
                    let mut vec = items.lock();
                    vec.push(item);
                    index += 1;
                }

                if stopped.load(Ordering::SeqCst) {
                    break;
                }
            }
            Err(_err) => {} // String not UTF8 or other error, skip.
        }
    }

    stopped.store(true, Ordering::Relaxed);
    while !command_stopped.load(Ordering::Relaxed) {
        thread::sleep(Duration::from_millis(5));
    }
}
<commit_msg>[reader] simplify what reader does<commit_after>/// Reader is used for reading items from datasource (e.g. stdin or command output)
///
/// After reading in a line, reader will save an item into the pool(items)
use crate::casmutex::CasMutex;
use crate::event::{Event, EventArg, EventReceiver, EventSender};
use crate::field::FieldRange;
use crate::item::Item;
use crate::options::SkimOptions;
use crate::sender::CachedSender;
use regex::Regex;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::io::{BufRead, BufReader};
use std::mem;
use std::process::{Child, Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender, SyncSender};
use std::sync::{Arc, RwLock};
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;

const DELIMITER_STR: &str = r"[\t\n ]+";

pub struct ReaderControl {
    stopped: Arc<AtomicBool>,
    thread_reader: JoinHandle<()>,
    items: Arc<CasMutex<Vec<Arc<Item>>>>,
}

impl ReaderControl {
    pub fn kill(self) {
        self.stopped.store(true, Ordering::SeqCst);
        self.thread_reader.join();
    }
}

pub struct Reader {
    option: Arc<ReaderOption>,
}

impl Reader {
    pub fn with_options(mut self, options: &SkimOptions) -> Self {
        Self {
            option: Arc::new(ReaderOption::with_options(&options)),
        }
    }

    pub fn run(&mut self, cmd: &str, source_file: Option<Box<BufRead + Send>>) -> ReaderControl {
        let stopped = Arc::new(AtomicBool::new(false));
        let stopped_clone = stopped.clone();
        stopped.store(false, Ordering::SeqCst);
        let items = Arc::new(CasMutex::new(Vec::new()));
        let items_clone = items.clone();
        let option_clone = self.option.clone();

        // start the new command
        thread_reader = Some(thread::spawn(move || {
            reader(cmd, stopped_clone, items_clone, option_clone, source_file);
        }));

        ReaderControl {
            stopped,
            thread_reader,
            items,
        }
    }
}

struct ReaderOption {
    pub use_ansi_color: bool,
    pub default_arg: String,
    pub transform_fields: Vec<FieldRange>,
    pub matching_fields: Vec<FieldRange>,
    pub delimiter: Regex,
    pub replace_str: String,
    pub line_ending: u8,
}

impl ReaderOption {
    pub fn new() -> Self {
        ReaderOption {
            use_ansi_color: false,
            default_arg: String::new(),
            transform_fields: Vec::new(),
            matching_fields: Vec::new(),
            delimiter: Regex::new(DELIMITER_STR).unwrap(),
            replace_str: "{}".to_string(),
            line_ending: b'\n',
        }
    }

    pub fn with_options(options: &SkimOptions) -> Self {
        let mut reader_option = Self::new();
        reader_option.parse_options(&options);
        reader_option
    }

    fn parse_options(&mut self, options: &SkimOptions) {
        if options.ansi {
            self.use_ansi_color = true;
        }

        if let Some(delimiter) = options.delimiter {
            self.delimiter = Regex::new(delimiter).unwrap_or_else(|_| Regex::new(DELIMITER_STR).unwrap());
        }

        if let Some(transform_fields) = options.with_nth {
            self.transform_fields = transform_fields
                .split(',')
                .filter_map(|string| FieldRange::from_str(string))
                .collect();
        }

        if let Some(matching_fields) = options.nth {
            self.matching_fields = matching_fields
                .split(',')
                .filter_map(|string| FieldRange::from_str(string))
                .collect();
        }

        if options.read0 {
            self.line_ending = b'\0';
        }
    }
}

fn get_command_output(cmd: &str) -> Result<(Option<Child>, Box<BufRead + Send>), Box<Error>> {
    let shell = env::var("SHELL").unwrap_or_else(|_| "sh".to_string());
    let mut command = Command::new(shell)
        .arg("-c")
        .arg(cmd)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .spawn()?;

    let stdout = command
        .stdout
        .take()
        .ok_or_else(|| "command output: unwrap failed".to_owned())?;

    Ok((Some(command), Box::new(BufReader::new(stdout))))
}

// Consider that you invoke a command with different arguments several times
// If you select some items each time, how will skim remeber it?
// => Well, we'll give each invokation a number, i.e. RUN_NUM
// What if you invoke the same command and same arguments twice?
// => We use NUM_MAP to specify the same run number.
lazy_static! {
    static ref RUN_NUM: RwLock<usize> = RwLock::new(0);
    static ref NUM_MAP: RwLock<HashMap<String, usize>> = RwLock::new(HashMap::new());
}

fn reader(
    cmd: &str,
    stopped: Arc<AtomicBool>,
    items: Arc<CasMutex<Vec<Arc<Item>>>>,
    option: Arc<ReaderOption>,
    source_file: Option<Box<BufRead + Send>>,
) {
    let (command, mut source) = source_file
        .map(|f| (None, f))
        .unwrap_or_else(|| get_command_output(cmd).expect("command not found"));

    let command_stopped = Arc::new(AtomicBool::new(false));

    let stopped_clone = stopped.clone();
    let command_stopped_clone = command_stopped.clone();
    thread::spawn(move || {
        // kill command if it is got
        while command.is_some() && !stopped_clone.load(Ordering::Relaxed) {
            thread::sleep(Duration::from_millis(5));
        }

        // clean up resources
        if let Some(mut x) = command {
            let _ = x.kill();
            let _ = x.wait();
        }
        command_stopped_clone.store(true, Ordering::Relaxed);
    });

    let opt = option.read().expect("reader: failed to lock option");

    // set the proper run number
    let run_num = { *RUN_NUM.read().expect("reader: failed to lock RUN_NUM") };
    let run_num = *NUM_MAP
        .write()
        .expect("reader: failed to lock NUM_MAP")
        .entry(cmd.to_string())
        .or_insert_with(|| {
            *(RUN_NUM.write().expect("reader: failed to lock RUN_NUM for write")) = run_num + 1;
            run_num + 1
        });

    let mut index = 0;
    let mut buffer = Vec::with_capacity(100);
    loop {
        buffer.clear();
        // start reading
        match source.read_until(opt.line_ending, &mut buffer) {
            Ok(n) => {
                if n == 0 {
                    break;
                }
                debug!("reader:reader: read a new line. index = {}", index);

                if buffer.ends_with(&[b'\r', b'\n']) {
                    buffer.pop();
                    buffer.pop();
                } else if buffer.ends_with(&[b'\n']) || buffer.ends_with(&[b'\0']) {
                    buffer.pop();
                }

                debug!("reader:reader: create new item. index = {}", index);
                let item = Item::new(
                    String::from_utf8_lossy(&buffer),
                    opt.use_ansi_color,
                    &opt.transform_fields,
                    &opt.matching_fields,
                    &opt.delimiter,
                    (run_num, index),
                );

                {
                    // save item into pool
                    let mut vec = items.lock();
                    vec.push(Arc::new(item));
                    index += 1;
                }

                if stopped.load(Ordering::SeqCst) {
                    break;
                }
            }
            Err(_err) => {} // String not UTF8 or other error, skip.
        }
    }

    stopped.store(true, Ordering::Relaxed);
    while !command_stopped.load(Ordering::Relaxed) {
        thread::sleep(Duration::from_millis(5));
    }
}
<|endoftext|>