Make grid axes dumb enums

This commit is contained in:
2025-02-18 21:27:12 +01:00
parent c7f82769e3
commit 98645f01d0
2 changed files with 170 additions and 101 deletions

View File

@@ -19,10 +19,10 @@ impl Color {
Color::White => Rank::_2, Color::White => Rank::_2,
} }
} }
fn get_direction(&self) -> i8 { fn other(&self) -> Color {
match self { match self {
Color::Black => -1, Color::Black => Color::White,
Color::White => 1, Color::White => Color::Black,
} }
} }
} }
@@ -40,12 +40,15 @@ impl Position {
file, file,
} }
} }
fn delta(&self, delta_rank_file: (i8, i8)) -> Result<Position, ()> { fn delta(
let (delta_rank, delta_file) = delta_rank_file; &self,
Ok(Position::new( file_direction: &Direction,
self.rank.delta(delta_rank)?, rank_direction: &Direction,
self.file.delta(delta_file)?, ) -> Option<Position> {
)) Some(Position {
rank: self.rank.delta(rank_direction)?,
file: self.file.delta(file_direction)?,
})
} }
} }
@@ -73,33 +76,25 @@ pub enum File {
H, H,
} }
enum Direction {
Incr,
Decr,
Stay,
}
pub trait GridAxis pub trait GridAxis
where where
Self: Sized, Self: Sized,
Self: Clone, Self: Clone,
{ {
const ALL_VALUES: [Self; 8]; const ALL_VALUES: [Self; 8];
fn new(index: u8) -> Result<Self, ()> { fn incr(&self) -> Option<Self>;
Ok(Self::ALL_VALUES[Self::validate_index(index)? as usize].clone()) fn decr(&self) -> Option<Self>;
} fn delta(&self, direction: &Direction) -> Option<Self> {
fn validate_index(index: u8) -> Result<u8, ()> { match direction {
if index as usize >= Self::ALL_VALUES.len() { Direction::Incr => self.incr(),
Err(()) Direction::Decr => self.decr(),
} else { Direction::Stay => Some(self.clone()),
Ok(index)
}
}
fn get_index(&self) -> u8;
fn delta(&self, delta: i8) -> Result<Self, ()> {
let highest = Self::ALL_VALUES.len() as i8 - 1;
if delta > highest
|| delta < -highest
|| (delta < 0 && self.get_index() < delta.abs() as u8)
|| (delta > 0 && self.get_index() + delta as u8 > highest as u8)
{
Err(())
} else {
Self::new(((self.get_index() as i8) + delta) as u8)
} }
} }
} }
@@ -115,8 +110,29 @@ impl GridAxis for Rank {
Rank::_7, Rank::_7,
Rank::_8, Rank::_8,
]; ];
fn get_index(&self) -> u8 { fn incr(&self) -> Option<Self> {
*self as u8 match self {
Rank::_1 => Some(Rank::_2),
Rank::_2 => Some(Rank::_3),
Rank::_3 => Some(Rank::_4),
Rank::_4 => Some(Rank::_5),
Rank::_5 => Some(Rank::_6),
Rank::_6 => Some(Rank::_7),
Rank::_7 => Some(Rank::_8),
Rank::_8 => None,
}
}
fn decr(&self) -> Option<Self> {
match self {
Rank::_1 => None,
Rank::_2 => Some(Rank::_1),
Rank::_3 => Some(Rank::_2),
Rank::_4 => Some(Rank::_3),
Rank::_5 => Some(Rank::_4),
Rank::_6 => Some(Rank::_5),
Rank::_7 => Some(Rank::_6),
Rank::_8 => Some(Rank::_7),
}
} }
} }
@@ -131,8 +147,29 @@ impl GridAxis for File {
File::G, File::G,
File::H, File::H,
]; ];
fn get_index(&self) -> u8 { fn incr(&self) -> Option<Self> {
*self as u8 match self {
File::A => Some(File::B),
File::B => Some(File::C),
File::C => Some(File::D),
File::D => Some(File::E),
File::E => Some(File::F),
File::F => Some(File::G),
File::G => Some(File::H),
File::H => None,
}
}
fn decr(&self) -> Option<Self> {
match self {
File::A => None,
File::B => Some(File::A),
File::C => Some(File::B),
File::D => Some(File::C),
File::E => Some(File::D),
File::F => Some(File::E),
File::G => Some(File::F),
File::H => Some(File::G),
}
} }
} }
@@ -168,71 +205,59 @@ pub struct Piece {
} }
struct Ray { struct Ray {
unit: (i8, i8), positions: Vec<Position>,
coefs: Option<Vec<u8>>,
source: Position,
current: u8,
} }
impl Ray { fn pawn_move_rays(position: &Position, color: &Color) -> Vec<Ray> {
fn new(unit: (i8, i8), coefs: Option<Vec<u8>>, source: Position) -> Self { let direction = match color {
Ray { Color::Black => Direction::Decr,
unit, Color::White => Direction::Incr,
coefs, };
source, if position.rank == color.get_pawn_rank() {
current: 0, let one = position.delta(&Direction::Stay, &direction).unwrap();
let two = one.delta(&Direction::Stay, &direction).unwrap();
vec![Ray {
positions: vec![one, two],
}]
} else {
if let Some(position) = position.delta(&Direction::Stay, &direction) {
vec![Ray {
positions: vec![position],
}]
} else {
vec![]
} }
} }
} }
impl Iterator for Ray { fn pawn_capture_rays(position: &Position, color: &Color) -> Vec<Ray> {
type Item = Position; let direction = match color {
fn next(&mut self) -> Option<Position> { Color::Black => Direction::Decr,
let (delta_unit_rank, delta_unit_file) = self.unit; Color::White => Direction::Incr,
let coef = i8::try_from(match &self.coefs { };
None => self.current + 1, let mut capture_rays = vec![];
Some(coefs) => { if let Some(p) = position.delta(&Direction::Incr, &direction) {
let crt_usize = self.current as usize; capture_rays.push(Ray {
if crt_usize >= coefs.len() { positions: vec![p],
return None; });
} }
coefs[crt_usize] if let Some(p) = position.delta(&Direction::Decr, &direction) {
} capture_rays.push(Ray {
}) positions: vec![p],
.unwrap(); });
let delta = (
delta_unit_rank.checked_mul(coef).unwrap(),
delta_unit_file.checked_mul(coef).unwrap(),
);
let rval = self.source.delta(delta).ok();
self.current = self.current.wrapping_add(1);
rval
} }
capture_rays
} }
impl Piece { impl Piece {
fn get_move_rays(&self, position: &Position) -> Vec<Ray> { fn get_move_rays(&self, position: &Position) -> Vec<Ray> {
match self.piece_type { match self.piece_type {
PieceType::Pawn => { PieceType::Pawn => pawn_move_rays(position, &self.color),
let direction = self.color.get_direction();
let coefs = if position.rank == self.color.get_pawn_rank() {
vec![1, 2]
} else {
vec![1]
};
vec![Ray::new((direction, 0), Some(coefs), position.clone())]
}
} }
} }
fn get_capture_rays(&self, position: &Position) -> Vec<Ray> { fn get_capture_rays(&self, position: &Position) -> Vec<Ray> {
match self.piece_type { match self.piece_type {
PieceType::Pawn => { PieceType::Pawn => pawn_capture_rays(position, &self.color),
let direction = self.color.get_direction();
vec![
Ray::new((direction, 1), Some(vec![1]), position.clone()),
Ray::new((direction, -1), Some(vec![1]), position.clone()),
]
}
} }
} }
} }
@@ -284,14 +309,19 @@ impl Board {
self.state.iter() self.state.iter()
} }
fn cut_move_ray(&self, ray: Ray) -> impl Iterator<Item = Position> + '_ { fn cut_move_ray(&self, ray: Ray) -> impl Iterator<Item = Position> + '_ {
ray.into_iter().take_while(|position| self.get_at(position).is_none()) ray.positions
.into_iter()
.take_while(|position| self.get_at(position).is_none())
} }
fn find_capture_along_ray(&self, ray: Ray) -> Option<Position> { fn find_capture_along_ray(
&self,
ray: Ray,
captured_color: &Color,
) -> Option<Position> {
// I'm not expecting to call this if there's no piece in the position // I'm not expecting to call this if there's no piece in the position
let crt_piece = self.get_at(&ray.source).unwrap(); ray.positions.into_iter().find(|p| match self.get_at(p) {
ray.into_iter().find(|p| match self.get_at(p) {
None => false, None => false,
Some(piece) => piece.color != crt_piece.color, Some(piece) => &piece.color == captured_color,
}) })
} }
pub fn find_moves( pub fn find_moves(
@@ -312,10 +342,9 @@ impl Board {
position: &Position, position: &Position,
) -> Result<impl Iterator<Item = Position> + '_, ()> { ) -> Result<impl Iterator<Item = Position> + '_, ()> {
if let Some(piece) = self.get_at(position) { if let Some(piece) = self.get_at(position) {
Ok(piece Ok(piece.get_capture_rays(position).into_iter().filter_map(
.get_capture_rays(position) |ray| self.find_capture_along_ray(ray, &piece.color.other()),
.into_iter() ))
.filter_map(|ray| self.find_capture_along_ray(ray)))
} else { } else {
Err(()) Err(())
} }

View File

@@ -30,30 +30,70 @@ trait GridAxisIO: std::fmt::Display
where where
Self: GridAxis, Self: GridAxis,
{ {
const ALL_CHARS: [char; 8]; fn parse(c: char) -> Result<Self, ()>;
fn parse(c: char) -> Result<Self, ()> {
let index = Self::ALL_CHARS.iter().position(|p| p == &c).ok_or(())?;
Ok(Self::ALL_VALUES[index].clone())
}
} }
impl GridAxisIO for board::Rank { impl GridAxisIO for board::Rank {
const ALL_CHARS: [char; 8] = ['1', '2', '3', '4', '5', '6', '7', '8']; fn parse(c: char) -> Result<Self, ()> {
match c {
'1' => Ok(board::Rank::_1),
'2' => Ok(board::Rank::_2),
'3' => Ok(board::Rank::_3),
'4' => Ok(board::Rank::_4),
'5' => Ok(board::Rank::_5),
'6' => Ok(board::Rank::_6),
'7' => Ok(board::Rank::_7),
'8' => Ok(board::Rank::_8),
_ => Err(()),
}
}
} }
impl std::fmt::Display for board::Rank { impl std::fmt::Display for board::Rank {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", Self::ALL_CHARS[self.get_index() as usize]) let c = match self {
board::Rank::_1 => '1',
board::Rank::_2 => '2',
board::Rank::_3 => '3',
board::Rank::_4 => '4',
board::Rank::_5 => '5',
board::Rank::_6 => '6',
board::Rank::_7 => '7',
board::Rank::_8 => '8',
};
write!(f, "{}", c)
} }
} }
impl GridAxisIO for board::File { impl GridAxisIO for board::File {
const ALL_CHARS: [char; 8] = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; fn parse(c: char) -> Result<Self, ()> {
match c {
'a' => Ok(board::File::A),
'b' => Ok(board::File::B),
'c' => Ok(board::File::C),
'd' => Ok(board::File::D),
'e' => Ok(board::File::E),
'f' => Ok(board::File::F),
'g' => Ok(board::File::G),
'h' => Ok(board::File::H),
_ => Err(()),
}
}
} }
impl std::fmt::Display for board::File { impl std::fmt::Display for board::File {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", Self::ALL_CHARS[self.get_index() as usize]) let c = match self {
board::File::A => 'a',
board::File::B => 'b',
board::File::C => 'c',
board::File::D => 'd',
board::File::E => 'e',
board::File::F => 'f',
board::File::G => 'g',
board::File::H => 'h',
};
write!(f, "{}", c)
} }
} }