fix winner logic

This commit is contained in:
2019-10-31 16:31:44 +01:00
parent 4b904370ab
commit 7ed65c475a

View File

@@ -2,6 +2,7 @@ use std::fmt;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::str::FromStr;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
enum State { enum State {
@@ -24,6 +25,7 @@ impl fmt::Display for State {
} }
} }
#[derive(Clone, Debug, PartialEq)]
struct Board(Vec<State>); struct Board(Vec<State>);
impl fmt::Display for Board { impl fmt::Display for Board {
@@ -61,6 +63,26 @@ impl IndexMut<(usize, usize)> for Board {
} }
} }
impl FromStr for Board {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut board = Self::new(0);
for c in s.chars() {
match c {
'X' | 'x' => board.0.push(State::X),
'O' | 'o' => board.0.push(State::O),
'N' | 'n' => board.0.push(State::N),
_ => (),
}
}
if board.dimension() * board.dimension() != board.0.len() {
return Err(());
}
Ok(board)
}
}
impl Board { impl Board {
fn new(size: usize) -> Board { fn new(size: usize) -> Board {
let mut b = Vec::with_capacity(size * size); let mut b = Vec::with_capacity(size * size);
@@ -75,50 +97,68 @@ impl Board {
} }
fn winner(&self) -> Option<State> { fn winner(&self) -> Option<State> {
let mut winners = [State::N; 4]; //winner row, col, diag right, diag left let mut winners = [State::N; 4];
let dim = self.dimension(); let dim = self.dimension();
for a in 0..dim { fn winner(winners: &[State]) -> Option<State> {
for b in 0..dim {
//winner row
winners[0] = if b == 0 || (winners[0] != State::N && winners[0] == self[(a, b)]) {
self[(a, b)]
} else {
State::N
};
//winner col
winners[1] = if b == 0 || (winners[1] != State::N && winners[1] == self[(b, a)]) {
self[(b, a)]
} else {
State::N
};
//winner diag
if a == b {
winners[2] = if a == 0 || winners[2] != State::N && winners[2] == self[(a, a)] {
self[(a, a)]
} else {
State::N
};
winners[3] = if a == 0
|| winners[3] != State::N && winners[3] == self[(dim - a - 1, dim - a - 1)]
{
self[(dim - a - 1, dim - a - 1)]
} else {
State::N
};
}
}
}
winners winners
.into_iter() .into_iter()
.find(|w| *w != &State::N) .find(|w| *w != &State::N)
.map(|w| w.clone()) .map(|w| w.clone())
} }
for x in 0..dim {
for y in 0..dim {
let row = (y, x);
let col = (x, y);
{
let r = self[row];
let c = self[col];
winners[0] = if y == 0 || winners[0] == r {
r
} else {
State::N
};
winners[1] = if y == 0 || winners[1] == c {
c
} else {
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
winner(&winners[2..4])
}
} }
fn main() { fn main() {
let mut board = Board::default(); let mut board = Board::default();
let mut stdin = std::io::stdin(); let stdin = std::io::stdin();
println!("{}", &board);
let winner = loop { let winner = loop {
if let Some(winner) = board.winner() { if let Some(winner) = board.winner() {
break winner; break winner;
@@ -128,9 +168,9 @@ fn main() {
loop { loop {
let (x, y) = loop { let (x, y) = loop {
print!("{}, your move: (x y) ", s); print!("{}, your move: (x y) ", s);
io::stdout().flush(); io::stdout().flush().unwrap();
input.clear(); input.clear();
stdin.read_line(&mut input); stdin.read_line(&mut input).unwrap();
let parts = input.trim().split(" ").collect::<Vec<_>>(); let parts = input.trim().split(" ").collect::<Vec<_>>();
if parts.len() != 2 { if parts.len() != 2 {
eprintln!("Invalid input"); eprintln!("Invalid input");
@@ -158,7 +198,7 @@ fn main() {
_ => eprintln!("({}, {}) is already occupied! Try again", x, y), _ => eprintln!("({}, {}) is already occupied! Try again", x, y),
} }
} }
if let Some(winner) = board.winner() { if let Some(_) = board.winner() {
break; break;
} }
println!("{}", &board); println!("{}", &board);
@@ -166,3 +206,32 @@ fn main() {
}; };
println!("The winner is {}", winner); println!("The winner is {}", winner);
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn winner() {
assert_eq!(Board::from_str("NNN,NNN,NNN").unwrap(), Board::default());
assert_eq!(
Board::from_str("XXX,NNN,NNN").unwrap().winner(),
Some(State::X)
);
assert_eq!(
Board::from_str("XNX,NNN,NNN").unwrap().winner(),
None
);
assert_eq!(
Board::from_str("NNN,XXX,NNN").unwrap().winner(),
Some(State::X)
);
assert_eq!(
Board::from_str("XON,OXN,ONX").unwrap().winner(),
Some(State::X)
);
assert_eq!(
Board::from_str("OXN,OON,XNO").unwrap().winner(),
Some(State::O)
);
}
}