Implement the basic rules of Go #40

Merged
savanni merged 13 commits from feature/go-rules into main 2023-05-04 02:34:40 +00:00
2 changed files with 23 additions and 79 deletions
Showing only changes of commit d68e088500 - Show all commits

View File

@ -2,9 +2,10 @@ use crate::{Color, Size};
use grid::Grid; use grid::Grid;
use std::collections::HashSet; use std::collections::HashSet;
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub enum Error { pub enum Error {
InvalidPosition, InvalidPosition,
SelfCapture,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -69,6 +70,8 @@ impl Board {
return Err(Error::InvalidPosition); return Err(Error::InvalidPosition);
} }
let old_groups = self.groups.clone();
let mut friendly_group = self let mut friendly_group = self
.adjacencies(&coordinate) .adjacencies(&coordinate)
.into_iter() .into_iter()
@ -95,6 +98,11 @@ impl Board {
} }
} }
if self.liberties(&friendly_group) == 0 {
self.groups = old_groups;
return Err(Error::SelfCapture);
}
Ok(()) Ok(())
} }
@ -117,13 +125,11 @@ impl Board {
pub fn adjacent_groups(&self, group: &Group) -> Vec<Group> { pub fn adjacent_groups(&self, group: &Group) -> Vec<Group> {
let adjacent_spaces = self.group_halo(group).into_iter(); let adjacent_spaces = self.group_halo(group).into_iter();
println!("adjacent spaces: {:?}", adjacent_spaces);
let mut grps: Vec<Group> = Vec::new(); let mut grps: Vec<Group> = Vec::new();
adjacent_spaces.for_each(|coord| match self.group(&coord) { adjacent_spaces.for_each(|coord| match self.group(&coord) {
None => return, None => return,
Some(adj) => { Some(adj) => {
println!("group found: {:?}", adj.color);
if group.color == adj.color { if group.color == adj.color {
return; return;
} }
@ -151,32 +157,6 @@ impl Board {
.into_iter() .into_iter()
.filter(|c| self.stone(&c) == None) .filter(|c| self.stone(&c) == None)
.count() .count()
// let adjacencies: HashSet<Coordinate> = self.adjacencies(group.coordinates.iter()).collect();
/*
println!("adjacencies: {:?}", adjacencies);
let opposing_spaces = adjacencies
.iter()
.filter(|coord| match self.grid.get(coord.row, coord.column) {
None => true,
Some(&None) => false,
Some(&Some(c)) => c != group.color,
})
.cloned()
.collect::<HashSet<Coordinate>>();
println!("opposition: {:?}", opposing_spaces);
adjacencies.len() - opposing_spaces.len()
*/
/*
group
.adjacencies(self.size.width as u8 - 1, self.size.height as u8 - 1)
.into_iter()
.filter(|coordinate| self.stone(*coordinate).is_none())
.collect::<Vec<Coordinate>>()
.len()
*/
} }
pub fn adjacencies(&self, coordinate: &Coordinate) -> Vec<Coordinate> { pub fn adjacencies(&self, coordinate: &Coordinate) -> Vec<Coordinate> {
@ -204,41 +184,6 @@ impl Board {
v.into_iter().filter(|c| self.within_board(c)).collect() v.into_iter().filter(|c| self.within_board(c)).collect()
} }
/*
pub fn adjacencies<'a>(
&'a self,
coordinates: impl Iterator<Item = &'a Coordinate> + 'a,
) -> impl Iterator<Item = Coordinate> + 'a {
coordinates
.map(|coordinate| {
let mut v = Vec::new();
if coordinate.column > 0 {
v.push(Coordinate {
column: coordinate.column - 1,
row: coordinate.row,
});
}
if coordinate.row > 0 {
v.push(Coordinate {
column: coordinate.column,
row: coordinate.row - 1,
});
}
v.push(Coordinate {
column: coordinate.column + 1,
row: coordinate.row,
});
v.push(Coordinate {
column: coordinate.column,
row: coordinate.row + 1,
});
v
})
.flatten()
.filter(|coordinate| self.within_board(coordinate))
}
*/
pub fn within_board(&self, coordinate: &Coordinate) -> bool { pub fn within_board(&self, coordinate: &Coordinate) -> bool {
coordinate.column < self.size.width && coordinate.row < self.size.height coordinate.column < self.size.width && coordinate.row < self.size.height
} }
@ -256,18 +201,6 @@ impl Group {
} }
} }
/*
impl Group {
pub fn adjacencies(&self, max_column: usize, max_row: usize) -> HashSet<Coordinate> {
self.coordinates
.iter()
.map(|stone| stone.adjacencies(max_column, max_row))
.flatten()
.collect()
}
}
*/
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -329,6 +262,10 @@ mod test {
(Coordinate { column: 6, row: 16 }, Color::White), (Coordinate { column: 6, row: 16 }, Color::White),
(Coordinate { column: 7, row: 17 }, Color::White), (Coordinate { column: 7, row: 17 }, Color::White),
(Coordinate { column: 7, row: 18 }, Color::White), (Coordinate { column: 7, row: 18 }, Color::White),
/* */
(Coordinate { column: 17, row: 0 }, Color::White),
(Coordinate { column: 17, row: 1 }, Color::White),
(Coordinate { column: 18, row: 1 }, Color::White),
] ]
.into_iter(), .into_iter(),
); );
@ -594,8 +531,15 @@ mod test {
}); });
} }
fn suicide_is_forbidden() { #[test]
assert!(false); fn self_capture_is_forbidden() {
with_example_board(|mut board| {
let res = board.place_stone(Coordinate { column: 18, row: 0 }, Color::Black);
assert_eq!(res, Err(Error::SelfCapture));
let res = board.place_stone(Coordinate { column: 5, row: 18 }, Color::Black);
assert_eq!(res, Err(Error::SelfCapture));
});
} }
fn captures_preceed_self_capture() { fn captures_preceed_self_capture() {

View File

@ -2,4 +2,4 @@ release:
cargo build --release cargo build --release
dev: dev:
cargo watch -x run cargo watch -x 'run --bin kifu-gtk'