Skip to content

Instantly share code, notes, and snippets.

@edwilliams
Last active May 4, 2023 09:47
Show Gist options
  • Select an option

  • Save edwilliams/7aa270c5a498c7b9360675f3847dafb6 to your computer and use it in GitHub Desktop.

Select an option

Save edwilliams/7aa270c5a498c7b9360675f3847dafb6 to your computer and use it in GitHub Desktop.
Noughts and Crosses (TicTacToe) in Rust and Node for comparison
import prompts from 'prompts'
const grid = [0, 0, 0, 0, 0, 0, 0, 0, 0]
/**
* Semantic wrapper for console.log
*/
const render = (val) => console.log(val)
/**
* @returns Random number between 0 and 8
*/
const getRandGridIndex = () => Math.floor(Math.random() * 9)
/**
* Get character for board from grid index
* @param {number} num - 0, 1 or 2
* @returns {string} empty space, O or X
*/
const getChar = (num) => (num === 0 ? ' ' : num === 1 ? 'O' : 'X')
/**
* Get character for board from grid index
* @param {array} grid
* @returns {string} empty space, O or X
*/
const getWinner = (grid) => {
const horizontal =
// top
grid[0] === 1 && grid[1] === 1 && grid[2] === 1
? 'O'
: grid[0] === 2 && grid[1] === 2 && grid[2] === 2
? 'X'
: // middle
grid[3] === 1 && grid[4] === 1 && grid[5] === 1
? 'O'
: grid[3] === 2 && grid[4] === 2 && grid[5] === 2
? 'X'
: // bottom
grid[6] === 1 && grid[7] === 1 && grid[8] === 1
? 'O'
: grid[6] === 2 && grid[7] === 2 && grid[8] === 2
? 'X'
: ''
const vertical =
// top
grid[0] === 1 && grid[3] === 1 && grid[6] === 1
? 'O'
: grid[0] === 2 && grid[3] === 2 && grid[6] === 2
? 'X'
: // middle
grid[1] === 1 && grid[4] === 1 && grid[7] === 1
? 'O'
: grid[1] === 2 && grid[4] === 2 && grid[7] === 2
? 'X'
: // bottom
grid[2] === 1 && grid[5] === 1 && grid[8] === 1
? 'O'
: grid[2] === 2 && grid[5] === 2 && grid[8] === 2
? 'X'
: ''
const diagonal =
grid[0] === 1 && grid[4] === 1 && grid[8] === 1
? 'O'
: grid[0] === 2 && grid[4] === 2 && grid[8] === 2
? 'X'
: grid[2] === 1 && grid[4] === 1 && grid[6] === 1
? 'O'
: grid[2] === 2 && grid[4] === 2 && grid[6] === 2
? 'X'
: ''
return diagonal || horizontal || vertical || ''
}
/**
* @param {array} grid
* @returns Two tictactoe boards, one for reference and one to display moves
*/
const getBoard = (grid) => `
0 | 1 | 2 ${getChar(grid[0])} | ${getChar(grid[1])} | ${getChar(grid[2])}
--------- ---------
3 | 4 | 5 ${getChar(grid[3])} | ${getChar(grid[4])} | ${getChar(grid[5])}
--------- ---------
6 | 7 | 8 ${getChar(grid[6])} | ${getChar(grid[7])} | ${getChar(grid[8])}
`
/**
* Get random index in grid array where the value is `0`
* @param {array} grid
* @returns number
*/
const getRandEmpty = (grid) => {
let bool = true
while (bool) {
const index = getRandGridIndex()
if (grid[index] === 0) {
bool = false
return index
}
}
}
/**
* TicTacToe
*/
const main = async () => {
let gameLoop = true
let isValidIndex = true
while (gameLoop) {
let promptMsg = isValidIndex
? 'Take your turn via choosing a number: '
: 'Oops, that space is taken. Try again: '
// clear screen
process.stdout.write('\u001b[2J\u001b[0;0H')
render(getBoard(grid))
const { index } = await prompts({
type: 'number',
name: 'index',
message: promptMsg,
})
if (grid[index] !== 0) {
isValidIndex = false
} else {
isValidIndex = true
grid[index] = 1
render(getBoard(grid))
const winner = getWinner(grid)
if (winner) {
render('You win!')
gameLoop = false
} else {
// first check if array has any zeros (i.e. can computer take a turn)
if (grid.filter((num) => num === 0).length === 0) {
render("It's a draw!")
gameLoop = false
return
}
const index = getRandEmpty(grid)
grid[index] = 2
const winner = getWinner(grid)
if (winner) {
render(getBoard(grid))
render('You loose!')
gameLoop = false
}
}
}
}
}
main()
use rand::Rng;
use std::io;
fn main() {
let mut grid = vec![
0, 0, 0, //
0, 0, 0, //
0, 0, 0,
];
let mut is_valid_index = true;
let mut prompt_msg: String;
loop {
prompt_msg = if is_valid_index {
let msg = String::from("Take your turn via choosing a number: ");
msg
} else {
let msg = String::from("Oops, that number is either taken or invalid. Try again: ");
msg
};
// clear screen
print!("{}[2J", 27 as char);
// render board and get user's choice
render_board(&grid);
println!("{}", prompt_msg);
let mut user_index = String::new();
io::stdin()
.read_line(&mut user_index)
.expect("Failed to read line");
let user_index: i32 = match user_index.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
// check choice then proceed or restart loop
if (user_index < 0 || user_index > 8) || grid[user_index as usize] != 0 {
is_valid_index = false;
continue;
} else {
grid[user_index as usize] = 1;
// render board then check for winner
render_board(&grid);
if get_winner(&grid) == "O" {
println!("You win!")
} else {
// check for remaining empty spaces, if none then it's a draw
let length = grid.iter().filter(|&&x| x == 0).count();
if length == 0 {
println!("It's a draw!");
} else {
// cpu's turn then check for winner or restart loop
let cpu_index = get_rand_empty(&grid);
grid[cpu_index as usize] = 2;
if get_winner(&grid) == "X" {
render_board(&grid);
println!("You loose!");
} else {
continue;
}
}
}
}
break;
}
}
fn get_rand_grid_index() -> u32 {
let num = rand::thread_rng().gen_range(0, 9);
num
}
fn get_char(num: i32) -> String {
if num == 0 {
let foo = String::from(" ");
foo
} else if num == 1 {
let foo = String::from("O");
foo
} else {
let foo = String::from("X");
foo
}
}
fn get_winner(grid: &Vec<i32>) -> String {
let winner = if (grid[0] == 1 && grid[1] == 1 && grid[2] == 1) // horizontal
|| (grid[3] == 1 && grid[4] == 1 && grid[5] == 1)
|| (grid[6] == 1 && grid[7] == 1 && grid[8] == 1)
|| (grid[0] == 1 && grid[3] == 1 && grid[6] == 1) // vertical
|| (grid[1] == 1 && grid[4] == 1 && grid[7] == 1)
|| (grid[2] == 1 && grid[5] == 1 && grid[8] == 1)
|| (grid[0] == 1 && grid[4] == 1 && grid[8] == 1) // diagonal
|| (grid[2] == 1 && grid[4] == 1 && grid[6] == 1)
{
let val = String::from("O");
val
} else if (grid[0] == 2 && grid[1] == 2 && grid[2] == 2) // horizontal
|| (grid[3] == 2 && grid[4] == 2 && grid[5] == 2)
|| (grid[6] == 2 && grid[7] == 2 && grid[8] == 2)
|| (grid[0] == 2 && grid[3] == 2 && grid[6] == 2) // vertical
|| (grid[1] == 2 && grid[4] == 2 && grid[7] == 2)
|| (grid[2] == 2 && grid[5] == 2 && grid[8] == 2)
|| (grid[0] == 2 && grid[4] == 2 && grid[8] == 2) // diagonal
|| (grid[2] == 2 && grid[4] == 2 && grid[6] == 2)
{
let val = String::from("X");
val
} else {
let val = String::from("");
val
};
winner
}
fn render_board(grid: &Vec<i32>) {
println!("");
println!("");
println!(
"0 | 1 | 2 {} | {} | {}",
get_char(grid[0]),
get_char(grid[1]),
get_char(grid[2])
);
println!("--------- ---------");
println!(
"3 | 4 | 5 {} | {} | {}",
get_char(grid[3]),
get_char(grid[4]),
get_char(grid[5])
);
println!("--------- ---------");
println!(
"6 | 7 | 8 {} | {} | {}",
get_char(grid[6]),
get_char(grid[7]),
get_char(grid[8])
);
println!("");
println!("");
}
fn get_rand_empty(grid: &Vec<i32>) -> u32 {
let i: u32;
loop {
let index = get_rand_grid_index();
if grid[index as usize] == 0 {
i = index;
break;
}
}
i
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment