This commit is contained in:
tilman
2019-09-11 17:11:44 +02:00
commit 133cfeff65
5 changed files with 209 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/target
**/*.rs.bk
/.idea/
/*.iml

6
Cargo.lock generated Normal file
View File

@@ -0,0 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "tictactoe"
version = "0.1.0"

7
Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "tictactoe"
version = "0.1.0"
authors = ["t"]
edition = "2018"
[dependencies]

21
src/main.rs Normal file
View File

@@ -0,0 +1,21 @@
mod tictactoe;
use crate::tictactoe::*;
fn main() {
let mut field = Field::new(3, 3);
//.mark(0, 0, Player::X)
//.mark(0, 1, Player::O);
field.print();
println!("----------------");
let mut player = Player::X;
while field.get_winner().is_none() {
let (x, y) = get_best_turn(&field, player);
field = field.mark(x, y, player);
field.print();
player = player.change();
println!("----------------");
}
println!("{:?}", field.get_winner());
}

171
src/tictactoe.rs Normal file
View File

@@ -0,0 +1,171 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Player {
X, O, NONE
}
impl Player {
pub fn change(&self) -> Self {
match self {
&Player::X => Player::O,
&Player::O => Player::X,
x => *x
}
}
}
pub struct Field {
fields: Vec<Vec<Player>>,
width: usize,
height: usize
}
impl Field {
pub fn new(width: usize, height: usize) -> Self {
Field { fields: vec![vec![Player::NONE; height]; width], width, height }
}
pub fn print(&self) {
for j in 0 .. self.height {
for i in 0 .. self.width {
match self.fields[i][j] {
Player::X => print!("X"),
Player::O => print!("O"),
Player::NONE => print!(" ")
}
}
println!()
}
}
pub fn mark(&self, x: usize, y: usize, player: Player) -> Self {
let mut fields = Vec::with_capacity(self.width);
for i in 0 .. self.width {
let mut column = Vec::with_capacity(self.height);
for j in 0 .. self.height {
if i == x && j == y {
column.push(player);
} else {
column.push(self.fields[i][j]);
}
}
fields.push(column);
}
Field { fields, width: self.width, height: self.height }
}
pub fn is_winner(&self, player: Player) -> bool {
assert_eq!(self.width, self.height);
for i in 0 .. self.width { // check columns
let mut winner = true;
for j in 0 .. self.height {
if self.fields[i][j] != player {
winner = false;
break;
}
}
if winner {
return true;
}
}
for j in 0 .. self.height { // check rows
let mut winner = true;
for i in 0 .. self.width {
if self.fields[i][j] != player {
winner = false;
break;
}
}
if winner {
return true;
}
}
{ // check top-left bottom-right diagonal
let mut winner = true;
for i in 0 .. self.width {
if self.fields[i][i] != player {
winner = false;
break;
}
}
if winner {
return true;
}
}
{ // check bottom-left top-right diagonal
let mut winner = true;
for i in 0 .. self.width {
if self.fields[i][self.height - i - 1] != player {
winner = false;
break;
}
}
if winner {
return true;
}
}
return false;
}
pub fn get_winner(&self) -> Option<Player> {
if self.is_winner(Player::X) {
Some(Player::X)
} else if self.is_winner(Player::O) {
Some(Player::O)
} else {
let mut full = true;
for i in 0 .. self.width {
for j in 0 .. self.height {
if self.fields[i][j] == Player::NONE {
full = false;
}
}
}
if full {
Some(Player::NONE)
} else {
None
}
}
}
}
pub fn get_best_turn(field: &Field, player: Player) -> (usize, usize) {
let (x, y, _) = search_best_turn(field, player);
(x, y)
}
fn search_best_turn(field: &Field, player: Player) -> (usize, usize, i8) {
let mut max_turn = (0, 0);
let mut max_value = -2;
for i in 0 .. field.width {
for j in 0 .. field.height {
if field.fields[i][j] == Player::NONE {
let played = field.mark(i, j, player);
let value = match played.get_winner() {
Some(p) => {
if p == player {
1
} else if p == Player::NONE {
0
} else { // loose
-1
}
},
None => {
let (_, _, sub_value) = search_best_turn(&played, player.change());
-sub_value
}
};
if value > max_value {
max_turn = (i, j);
max_value = value;
}
}
}
}
(max_turn.0, max_turn.1, max_value)
}