Skip to content

Instantly share code, notes, and snippets.

@psaia
Created December 3, 2021 19:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save psaia/af93e95e71fe062af97b68ee3832038b to your computer and use it in GitHub Desktop.
Save psaia/af93e95e71fe062af97b68ee3832038b to your computer and use it in GitHub Desktop.
use std::collections::HashMap;
use std::io;
use std::slice::Iter;
#[derive(Debug, PartialEq)]
enum Player {
X,
O,
}
impl Player {
pub fn iterator() -> Iter<'static, Player> {
static PLAYERS: [Player; 2] = [Player::X, Player::O];
PLAYERS.iter()
}
}
enum Outcome {
Win,
Taken,
Valid,
Stalemate,
Invalid,
}
struct Game {
board: HashMap<u32, Option<Player>>,
last_turn: Option<Player>,
}
impl Game {
fn new() -> Game {
let mut board = HashMap::new();
for i in 1..10 {
board.insert(i, None);
}
Game {
board: board,
last_turn: None,
}
}
fn print_instructions(&self) {
println!(
"It {:?}'s turn! Type the number of the space you'd like to move to.",
self.current_player()
);
}
fn current_player(&self) -> Player {
match self.last_turn {
Some(Player::X) => Player::O,
Some(Player::O) => Player::X,
None => Player::X,
}
}
fn print_board(&self) {
println!(
"---------\n{} | {} | {}\n{} | {} | {}\n{} | {} | {}\n---------",
Game::player_mark(self.board.get(&(1)).unwrap(), "1"),
Game::player_mark(self.board.get(&(2)).unwrap(), "2"),
Game::player_mark(self.board.get(&(3)).unwrap(), "3"),
Game::player_mark(self.board.get(&(4)).unwrap(), "4"),
Game::player_mark(self.board.get(&(5)).unwrap(), "5"),
Game::player_mark(self.board.get(&(6)).unwrap(), "6"),
Game::player_mark(self.board.get(&(7)).unwrap(), "7"),
Game::player_mark(self.board.get(&(8)).unwrap(), "8"),
Game::player_mark(self.board.get(&(9)).unwrap(), "9"),
);
}
fn do_move(&mut self, input: &str) -> Outcome {
let placement: u32 = input.parse().unwrap_or(0);
if placement == 0 {
return Outcome::Invalid;
}
match self.board.get(&placement) {
Some(m) => match m {
Some(_) => Outcome::Taken, // Move has been taken by someone else.
None => {
self.board.insert(placement, Some(self.current_player()));
self.last_turn = Some(self.current_player());
if self.won() {
return Outcome::Win;
}
if self.stalemate() {
return Outcome::Stalemate;
}
Outcome::Valid
}
},
None => Outcome::Invalid, // Move is out of bounds.
}
}
fn stalemate(&self) -> bool {
for (_, placement) in &self.board {
if placement.is_none() {
return false
}
}
true
}
fn won(&self) -> bool {
let winners = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
vec![1, 4, 7],
vec![2, 5, 8],
vec![3, 6, 9],
vec![1, 5, 9],
vec![3, 5, 7],
];
for player in Player::iterator() {
for combo in &winners {
let mut matches = 0;
for coord in combo {
let space = self.board.get(&(coord)).expect("will exist").as_ref();
if space.is_some() && space.unwrap() == player {
matches += 1;
}
}
// All three match - winner!
if matches == 3 {
return true;
}
}
}
false
}
fn clear() {
print!("\x1B[2J\x1B[1;1H");
}
fn player_mark(p: &Option<Player>, default_val: &str) -> String {
match p {
Some(player) => match player {
Player::X => String::from("X"),
Player::O => String::from("O"),
},
None => String::from(default_val),
}
}
}
fn main() {
Game::clear();
let mut game = Game::new();
game.print_board();
game.print_instructions();
loop {
// Block; Read in the user's input.
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Couldn't read line");
let game_prompt = |g: &Game| {
g.print_board();
g.print_instructions();
};
// Parse the input and do things.
match game.do_move(input.as_str().trim()) {
Outcome::Valid => {
Game::clear();
game_prompt(&game);
println!("=> Move made!");
}
Outcome::Taken => {
Game::clear();
game_prompt(&game);
println!("=> That move has already been taken.");
}
Outcome::Invalid => {
Game::clear();
game_prompt(&game);
println!("=> That was an invalid input. Try again.");
}
Outcome::Stalemate => {
Game::clear();
println!("=> No one won. Game reset.");
game = Game::new();
game_prompt(&game);
}
Outcome::Win => {
Game::clear();
println!("=> {:?} won! Game reset.", game.current_player());
game = Game::new();
game_prompt(&game);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment