Last active
May 4, 2023 09:47
-
-
Save edwilliams/7aa270c5a498c7b9360675f3847dafb6 to your computer and use it in GitHub Desktop.
Noughts and Crosses (TicTacToe) in Rust and Node for comparison
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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