Skip to content

Instantly share code, notes, and snippets.

@Agent-E11
Last active March 10, 2023 04:44
Show Gist options
  • Save Agent-E11/b89154f39c688296482dc56bcae62452 to your computer and use it in GitHub Desktop.
Save Agent-E11/b89154f39c688296482dc56bcae62452 to your computer and use it in GitHub Desktop.
Basic tic tac toe program written in Rust
use std::io;
use ndarray::{self, arr2, Array2, Array1};
fn main() {
let cell_empty: char = '-';
let stdin = io::stdin();
let mut grid = arr2(&[
[cell_empty, cell_empty, cell_empty],
[cell_empty, cell_empty, cell_empty],
[cell_empty, cell_empty, cell_empty]
]);
// let mut x_turn: bool = true;
let mut playing: bool = true;
let mut player = PlayerTurn::X;
// Game loop
while playing {
// Prompt the player
player.print_turn();
// Initialize position and input variables
let mut input = String::new();
let x_num: usize;
let y_num: usize;
// Get position
// Get x:
// Prompt, read line, check for error, set `x_num`
println!("Column: ");
stdin.read_line(&mut input).expect("Read failed: at x_str");
if let Err(e) = check_parse(input.trim()) {
println!("{e}. Try again.");
continue;
}
x_num = input.trim().parse::<usize>().expect("Parse failed: at x_num") - 1;
// Clear input
input.clear();
// Get y:
// Prompt, read line, check for error, set `y_num`
println!("Row: ");
stdin.read_line(&mut input).expect("Read failed: at y_str");
if let Err(e) = check_parse(input.trim()) {
println!("{e}. Try again.");
continue;
}
y_num = input.trim().parse::<usize>().expect("Parse failed: at y_num") - 1;
// Check for out of bounds error
match check_out_of_bounds(&grid, &y_num, &x_num) {
Ok(_) => (),
Err(e) => {
println!("{e}. Try again.");
continue;
},
}
if grid[(y_num, x_num)] == cell_empty { // If the cell is empty
// Place an X or O based on the current player
match player {
PlayerTurn::X => grid[(y_num, x_num)] = 'X',
PlayerTurn::O => grid[(y_num, x_num)] = 'O',
}
} else { // If the cell is invalid, the grid will not be printed and the turn will not change
println!("Invalid location, please try again.");
continue;
}
// Print the grid as a grid
for i in 0..3 {
for j in 0..3 {
print!("{} ", grid[(i, j)]);
}
print!("\n");
}
// Check for a winner
match check_win(&grid) {
true => {
playing = false;
println!("Someone has won!");
continue;
},
false => ()
};
// Switch turn
player = player.switch();
}
}
//
fn check_win(grid: &Array2<char>) -> bool {
// Check columns
for col in grid.columns() {
if col.iter().all(|n| n == &col[0]) && col[0] != '-' {
return true;
}
}
// Check rows
for row in grid.rows() {
if row.iter().all(|n| n == &row[0]) && row[0] != '-' {
return true;
}
}
// Check diagonal
if grid.diag().iter().all(|n| n == &grid.diag()[0]) && grid.diag()[0] != '-' {
return true;
}
// Check other diagonal
if diag_2(grid).iter().all(|n| n == &diag_2(grid)[0]) && diag_2(grid)[0] != '-' {
return true;
}
// Return false if no one has won
false
}
// Returns an Array1 containing the characters on the 'other diagonal' of the given Array2
fn diag_2(grid: &Array2<char>) -> Array1<char> {
// Copy the given array
let mut invert = grid.clone();
// Invert the array side-to-side
invert.invert_axis(ndarray::Axis(1));
// Return the diagonal of the inverted array
invert.diag().to_owned()
}
fn check_out_of_bounds(grid: &Array2<char>, y: &usize, x: &usize) -> Result<(), String> {
match grid.get((*y, *x)) {
Some(_) => Ok(()),
None => Err(String::from("Invalid index")),
}
}
fn check_parse(str: &str) -> Result<(), std::num::ParseIntError> {
match str.parse::<usize>() {
Ok(_) => Ok(()),
Err(e) => Err(e)
}
}
enum PlayerTurn {
X,
O,
}
impl PlayerTurn {
fn switch(self) -> PlayerTurn {
match self {
PlayerTurn::X => PlayerTurn::O,
PlayerTurn::O => PlayerTurn::X,
}
}
fn print_turn(&self) {
match self {
PlayerTurn::X => println!("X's turn"),
PlayerTurn::O => println!("O's turn"),
}
}
}
@Agent-E11
Copy link
Author

Agent-E11 commented Mar 9, 2023

I use the ndarray crate v0.15.6 (It should be the latest at time of posting)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment