Move some functionality into the new Engine mod

This will help with separating the "thinking" logic from the move rules
and stuff.
This commit is contained in:
2021-12-21 21:07:19 +01:00
parent 802b069269
commit 95c0ccfbd2
4 changed files with 83 additions and 44 deletions

View File

@@ -247,7 +247,7 @@ impl Board {
state: Default::default(), state: Default::default(),
} }
} }
fn get_at(&self, position: &Position) -> Option<&Piece> { pub fn get_at(&self, position: &Position) -> Option<&Piece> {
self.state.get(position) self.state.get(position)
} }
pub fn set_at( pub fn set_at(
@@ -260,7 +260,7 @@ impl Board {
None => self.state.remove(&position), None => self.state.remove(&position),
}; };
} }
fn relocate( pub fn relocate(
&mut self, &mut self,
source: &Position, source: &Position,
target: Position, target: Position,
@@ -269,9 +269,6 @@ impl Board {
self.set_at(target, Some(piece)); self.set_at(target, Some(piece));
Ok(()) Ok(())
} }
pub fn clear(&mut self) {
self.state.clear();
}
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.state.clear(); self.state.clear();
for piece_type in &[PieceType::Pawn] { for piece_type in &[PieceType::Pawn] {
@@ -286,7 +283,7 @@ impl Board {
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.into_iter().take_while(|position| self.get_at(position).is_none())
} }
fn find_capture(&self, ray: Ray) -> Option<Position> { fn find_capture_along_ray(&self, ray: Ray) -> 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(); let crt_piece = self.get_at(&ray.source).unwrap();
ray.into_iter().find(|p| match self.get_at(p) { ray.into_iter().find(|p| match self.get_at(p) {
@@ -294,34 +291,30 @@ impl Board {
Some(piece) => piece.color != crt_piece.color, Some(piece) => piece.color != crt_piece.color,
}) })
} }
pub fn get_legal_moves( pub fn find_moves(
&self, &self,
position: &Position, position: &Position,
) -> Option<Vec<Position>> { ) -> Result<impl Iterator<Item = Position> + '_, ()> {
let piece = match self.get_at(position) { if let Some(piece) = self.get_at(position) {
None => return None, Ok(piece
Some(piece) => piece, .get_move_rays(position)
}; .into_iter()
let valid_moves = piece .flat_map(|ray| self.cut_move_ray(ray)))
.get_move_rays(position)
.into_iter()
.flat_map(|ray| self.cut_move_ray(ray));
let valid_captures = piece
.get_capture_rays(position)
.into_iter()
.filter_map(|ray| self.find_capture(ray));
Some(valid_moves.chain(valid_captures).collect())
}
pub fn make_move(
&mut self,
source: &Position,
target: Position,
) -> Result<(), ()> {
if !self.get_legal_moves(source).ok_or(())?.contains(&target) {
Err(())
} else { } else {
// We checked that there is a piece at source in get_legal_moves Err(())
self.relocate(source, target) }
}
pub fn find_captures(
&self,
position: &Position,
) -> Result<impl Iterator<Item = Position> + '_, ()> {
if let Some(piece) = self.get_at(position) {
Ok(piece
.get_capture_rays(position)
.into_iter()
.filter_map(|ray| self.find_capture_along_ray(ray)))
} else {
Err(())
} }
} }
} }

43
rs/src/engine.rs Normal file
View File

@@ -0,0 +1,43 @@
use crate::board;
pub struct Engine {
board: board::Board,
}
impl Engine {
pub fn new() -> Self {
let mut board = board::Board::new();
board.reset();
Engine {
board,
}
}
pub fn get_state(&self) -> &board::Board {
&self.board
}
pub fn set_state(&mut self, board: board::Board) {
self.board = board;
}
pub fn get_legal_moves(
&self,
position: &board::Position,
) -> Result<Vec<board::Position>, ()> {
Ok(self
.board
.find_moves(position)?
.chain(self.board.find_captures(position)?)
.collect())
}
pub fn make_move(
&mut self,
source: &board::Position,
target: board::Position,
) -> Result<(), ()> {
if !self.get_legal_moves(source)?.contains(&target) {
Err(())
} else {
// We checked that there is a piece at source in get_legal_moves
self.board.relocate(source, target)
}
}
}

View File

@@ -1,4 +1,5 @@
mod board; mod board;
mod engine;
mod ui; mod ui;
fn main() -> Result<(), ()> { fn main() -> Result<(), ()> {

View File

@@ -1,5 +1,6 @@
use crate::board; use crate::board;
use crate::board::GridAxis; use crate::board::GridAxis;
use crate::engine;
use std::io::BufRead; use std::io::BufRead;
impl std::fmt::Display for board::Color { impl std::fmt::Display for board::Color {
@@ -140,20 +141,18 @@ impl board::Board {
} }
pub struct Ui { pub struct Ui {
board: board::Board, engine: engine::Engine,
} }
impl Ui { impl Ui {
pub fn new() -> Self { pub fn new() -> Self {
let mut board = board::Board::new();
board.reset();
Ui { Ui {
board, engine: engine::Engine::new(),
} }
} }
fn get_state(&self, args: &[&str]) -> Result<String, String> { fn get_state(&self, args: &[&str]) -> Result<String, String> {
if let [] = args { if let [] = args {
let result = format!("{}", self.board); let result = format!("{}", self.engine.get_state());
Ok(result) Ok(result)
} else { } else {
Err("get_state takes no args".to_owned()) Err("get_state takes no args".to_owned())
@@ -161,9 +160,12 @@ impl Ui {
} }
fn set_state(&mut self, args: &[&str]) -> Result<String, String> { fn set_state(&mut self, args: &[&str]) -> Result<String, String> {
if let [state_str] = args { if let [state_str] = args {
self.board = board::Board::parse(state_str) self.engine.set_state(
.map_err(|_| format!("Error parsing state {}", state_str))?; board::Board::parse(state_str).map_err(|_| {
let result = format!("{}", self.board); format!("Error parsing state {}", state_str)
})?,
);
let result = format!("{}", self.engine.get_state());
Ok(result) Ok(result)
} else { } else {
Err("set_state takes 1 arg".to_owned()) Err("set_state takes 1 arg".to_owned())
@@ -175,12 +177,12 @@ impl Ui {
board::Position::parse(position_str).map_err(|_| { board::Position::parse(position_str).map_err(|_| {
format!("Error parsing position {}", position_str) format!("Error parsing position {}", position_str)
})?; })?;
match self.board.get_legal_moves(&position) { match self.engine.get_legal_moves(&position) {
None => { Err(_) => {
let error = format!("No moves possible from {}", position); let error = format!("No moves possible from {}", position);
Err(error) Err(error)
} }
Some(positions) => Ok(positions Ok(positions) => Ok(positions
.iter() .iter()
.map(|pos| format!("{}", pos)) .map(|pos| format!("{}", pos))
.collect()), .collect()),
@@ -195,12 +197,12 @@ impl Ui {
.map_err(|_| format!("Error parsing source {}", source_str))?; .map_err(|_| format!("Error parsing source {}", source_str))?;
let target = board::Position::parse(target_str) let target = board::Position::parse(target_str)
.map_err(|_| format!("Error parsing target {}", target_str))?; .map_err(|_| format!("Error parsing target {}", target_str))?;
match self.board.make_move(&source, target) { match self.engine.make_move(&source, target) {
Err(()) => Err(format!( Err(()) => Err(format!(
"Move not possible {}-{}", "Move not possible {}-{}",
source_str, target_str source_str, target_str
)), )),
Ok(()) => Ok(self.board.to_string()), Ok(()) => Ok(format!("{}", self.engine.get_state())),
} }
} else { } else {
Err("make_move takes 2 args".to_owned()) Err("make_move takes 2 args".to_owned())