allow for larger boards to be won using less than a full path
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing

This commit is contained in:
shimun 2019-11-10 20:42:58 +01:00
parent cc87ad378e
commit e703d914ed
Signed by: shimun
GPG Key ID: E81D8382DC2F971B

View File

@ -12,6 +12,12 @@ enum State {
N, N,
} }
impl State {
fn players() -> &'static [State] {
&[State::O, State::X]
}
}
impl fmt::Display for State { impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
@ -107,69 +113,52 @@ impl Board {
s s
} }
fn winner(&self) -> Option<State> { fn winner_with_criteria(&self, criteria: usize) -> Option<State> {
let mut winners = [State::N; 4]; for s in State::players() {
let dim = self.dimension(); let update = |coords: (usize, usize), counter: &mut usize| {
fn winner(winners: &[State]) -> Option<State> { if self[coords] == *s {
winners *counter += 1;
.into_iter() } else {
.find(|w| *w != &State::N) *counter = 0;
.map(|w| w.clone()) }
} };
for x in 0..dim { let winner = |candidates: &[usize]| -> bool {
for y in 0..dim { candidates.iter().find(|c| *c >= &criteria).is_some()
let row = (y, x); };
let col = (x, y); let (mut diar, mut dial) = (0usize, 0usize);
for i in 0..self.dimension() {
{ let (mut row, mut col) = (0usize, 0usize);
let r = self[row]; for j in 0..self.dimension() {
let c = self[col]; update((i, j), &mut row);
winners[0] = if y == 0 || winners[0] == r { update((j, i), &mut col);
r if winner(&[row, col]) {
} else { return Some(*s);
State::N }
}; }
update((i, i), &mut dial);
winners[1] = if y == 0 || winners[1] == c { update((self.dimension() - i - 1, i), &mut diar);
c if winner(&[diar, dial]) {
} else { return Some(*s);
State::N
};
} }
} }
{
//diagonal
let diag_left = (x, x);
let diag_right = (dim - x - 1, x);
let l = self[diag_left];
let r = self[diag_right];
winners[2] = if x == 0 || winners[2] == l {
l
} else {
State::N
};
winners[3] = if x == 0 || winners[3] == r {
r
} else {
State::N
};
}
//Row and Col winners can be determined after one Y pass
if let Some(winner) = winner(&winners[0..2]) {
return Some(winner);
}
} }
//Diagonal winners require a full X pass None.or(Some(State::N).filter(|_| self.0.iter().filter(|s| *s == &State::N).count() == 0))
winner(&winners[2..4]) }
.or(Some(State::N).filter(|_| self.0.iter().filter(|s| *s == &State::N).count() == 0))
#[allow(unused)]
fn winner(&self) -> Option<State> {
self.winner_with_criteria(self.dimension())
} }
} }
fn main() { fn main() {
let mut board = { let (mut board, criteria) = {
if let Some(arg) = args().skip(1).next() { let mut args = args().skip(1);
let (size, criteria) = (
args.next(),
args.next().and_then(|c| c.parse::<usize>().ok()),
);
let board = if let Some(arg) = size {
if let Ok(size) = arg.parse::<usize>() { if let Ok(size) = arg.parse::<usize>() {
Board::new(size) Board::new(size)
} else if let Ok(board) = Board::from_str(&arg) { } else if let Ok(board) = Board::from_str(&arg) {
@ -179,16 +168,18 @@ fn main() {
} }
} else { } else {
Board::default() Board::default()
} };
let dim = board.dimension();
(board, criteria.unwrap_or(dim))
}; };
let stdin = std::io::stdin(); let stdin = std::io::stdin();
println!("{}", &board); println!("{}", &board);
let winner = loop { let winner = loop {
if let Some(winner) = board.winner() { if let Some(winner) = board.winner_with_criteria(criteria) {
break winner; break winner;
} }
let mut input = String::new(); let mut input = String::new();
for s in &[State::X, State::O] { for s in State::players() {
loop { loop {
let (x, y) = loop { let (x, y) = loop {
print!("{}, your move: (x y) ", s); print!("{}, your move: (x y) ", s);
@ -222,7 +213,7 @@ fn main() {
_ => eprintln!("({}, {}) is already occupied! Try again", x, y), _ => eprintln!("({}, {}) is already occupied! Try again", x, y),
} }
} }
if let Some(_) = board.winner() { if let Some(_) = board.winner_with_criteria(criteria) {
break; break;
} }
println!("{}", &board); println!("{}", &board);
@ -259,6 +250,19 @@ mod test {
Board::from_str("XOX,OOX,OXO").unwrap().winner(), Board::from_str("XOX,OOX,OXO").unwrap().winner(),
Some(State::N) Some(State::N)
); );
assert_eq!(
Board::from_str("XOX,OOX,OXO")
.unwrap()
.winner_with_criteria(1),
Some(State::O)
);
assert_eq!(
Board::from_str("XOX,OOX,OXO")
.unwrap()
.winner_with_criteria(2),
Some(State::O)
);
} }
#[test] #[test]