Refactor with a Position2d struct

main
Clément FRÉVILLE 2 years ago
parent 2b4ef27f92
commit e67a28d110

@ -1,3 +1,4 @@
use crate::position::Position2d;
use crate::tile::Tile;
pub const BOARD_SIZE: usize = 25;
@ -16,38 +17,38 @@ impl Board {
self.tiles[y * BOARD_SIZE + x] = Some(tile);
}
pub fn difference(&self, other: &Board) -> Vec<(usize, usize)> {
pub fn difference(&self, other: &Board) -> Vec<Position2d> {
let mut diff = Vec::new();
for x in 0..BOARD_SIZE {
for y in 0..BOARD_SIZE {
if self.get(x, y) != other.get(x, y) {
diff.push((x, y));
diff.push(Position2d::new(x, y));
}
}
}
diff
}
pub fn is_contiguous(positions: &[(usize, usize)]) -> Option<bool> {
pub fn is_contiguous(positions: &[Position2d]) -> Option<bool> {
let mut it = positions.iter();
let first = *it.next()?;
let mut second = *it.next()?;
if first.0 == second.0 {
if first.x == second.x {
// Vertical
for &(x, y) in it {
if x != first.0 || y != second.1 + 1 {
for &pos in it {
if pos.x != first.x || pos.y != second.y + 1 {
return Some(false);
}
second = (x, y);
second = pos;
}
Some(true)
} else if first.1 == second.1 {
} else if first.y == second.y {
// Horizontal
for &(x, y) in it {
if y != first.1 || x != second.0 + 1 {
for &pos in it {
if pos.y != first.y || pos.y != second.y + 1 {
return Some(false);
}
second = (x, y);
second = pos;
}
Some(true)
} else {
@ -68,18 +69,27 @@ impl Default for Board {
mod tests {
use super::*;
fn positions(input: &[(usize, usize)]) -> Vec<Position2d> {
input
.iter()
.map(|(x, y)| Position2d::new(*x, *y))
.collect::<Vec<_>>()
}
#[test]
fn test_is_contiguous() {
assert_eq!(Board::is_contiguous(&[]), None);
assert_eq!(Board::is_contiguous(&[(0, 0)]), None);
assert_eq!(Board::is_contiguous(&[(0, 0), (0, 1), (0, 2)]), Some(true));
assert_eq!(Board::is_contiguous(&positions(&[(0, 0)])), None);
assert_eq!(
Board::is_contiguous(&[(1, 0), (2, 0), (3, 0), (4, 0)]),
Board::is_contiguous(&positions(&[(0, 0), (0, 1), (0, 2)])),
Some(true)
);
assert_eq!(Board::is_contiguous(&[(0, 0), (0, 1), (1, 3)]), Some(false));
assert_eq!(
Board::is_contiguous(&[(0, 0), (0, 1), (0, 2), (1, 2)]),
Board::is_contiguous(&positions(&[(0, 0), (0, 1), (1, 3)])),
Some(false)
);
assert_eq!(
Board::is_contiguous(&positions(&[(0, 0), (0, 1), (0, 2), (1, 2)])),
Some(false)
);
}

@ -2,6 +2,7 @@ use crate::board::Board;
use crate::lexer::lexer;
use crate::parser;
use crate::parser::{Expression, Expressions};
use crate::position::Position2d;
use crate::tile::{Operator, Tile};
pub fn calculate(expr: &Expression) -> f64 {
@ -36,10 +37,10 @@ pub fn are_valid_expressions(expr: &Expressions) -> bool {
res.is_some()
}
pub fn is_valid_guess(board: &Board, positions: &[(usize, usize)]) -> Result<bool, ()> {
pub fn is_valid_guess(board: &Board, positions: &[Position2d]) -> Result<bool, ()> {
let tiles = positions
.iter()
.map(|&(x, y)| board.get(x, y))
.map(|&pos| board.get(pos.x, pos.y))
.collect::<Option<Vec<Tile>>>()
.ok_or(())?;

@ -3,4 +3,5 @@ pub mod expr;
pub mod game;
mod lexer;
mod parser;
mod position;
pub mod tile;

@ -0,0 +1,139 @@
/// A position in a 2d grid.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position2d {
pub x: usize,
pub y: usize,
}
/// An alignment in a 2d grid.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Alignment {
Horizontal,
Vertical,
}
/// A direction in a 2d grid.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
impl Alignment {
/// Returns the direction to the start of an alignment.
pub fn start(self) -> Direction {
match self {
Alignment::Horizontal => Direction::Left,
Alignment::Vertical => Direction::Up,
}
}
/// Returns the direction to the end of an alignment.
pub fn end(self) -> Direction {
match self {
Alignment::Horizontal => Direction::Right,
Alignment::Vertical => Direction::Down,
}
}
/// Test if two positions are aligned in this direction.
pub fn is_aligned(self, a: Position2d, b: Position2d) -> bool {
match self {
Alignment::Horizontal => a.x == b.x,
Alignment::Vertical => a.y == b.y,
}
}
}
impl Position2d {
pub fn new(x: usize, y: usize) -> Self {
Self { x, y }
}
/// Returns the position relative to this position in the given direction.
/// If the position is out of bounds, returns None.
pub fn relative(self, dir: Direction, sized: &impl Grid2d) -> Option<Position2d> {
let (x, y) = match dir {
Direction::Up => (self.x, self.y.checked_sub(1)?),
Direction::Down => (self.x, self.y.checked_add(1)?),
Direction::Left => (self.x.checked_sub(1)?, self.y),
Direction::Right => (self.x.checked_add(1)?, self.y),
};
if x < sized.width() && y < sized.height() {
Some(Position2d::new(x, y))
} else {
None
}
}
}
impl From<(usize, usize)> for Position2d {
fn from((x, y): (usize, usize)) -> Self {
Self { x, y }
}
}
/// Trait for elements that have a size.
pub trait Grid2d {
/// Returns the grid width.
fn width(&self) -> usize;
/// Returns the grid height.
fn height(&self) -> usize;
}
#[cfg(test)]
mod tests {
use super::*;
struct Rectangle {
width: usize,
height: usize,
}
impl Rectangle {
fn new(width: usize, height: usize) -> Self {
Self { width, height }
}
}
impl Grid2d for Rectangle {
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
}
#[test]
fn test_relative() {
let rect = Rectangle::new(3, 3);
let pos = Position2d::new(0, 0);
assert_eq!(pos.relative(Direction::Up, &rect), None);
assert_eq!(
pos.relative(Direction::Down, &rect),
Some(Position2d::new(0, 1))
);
assert_eq!(pos.relative(Direction::Left, &rect), None);
assert_eq!(
pos.relative(Direction::Right, &rect),
Some(Position2d::new(1, 0))
);
let pos = Position2d::new(2, 2);
assert_eq!(
pos.relative(Direction::Up, &rect),
Some(Position2d::new(2, 1))
);
assert_eq!(pos.relative(Direction::Down, &rect), None);
assert_eq!(
pos.relative(Direction::Left, &rect),
Some(Position2d::new(1, 2))
);
assert_eq!(pos.relative(Direction::Right, &rect), None);
}
}
Loading…
Cancel
Save