From af2bc841119a6751c240dec95dd5511d4ee31d36 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Wed, 7 May 2025 23:08:15 -0700 Subject: init ; most of tic tac toe done --- tic_tac_toe/src/main.rs | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tic_tac_toe/src/main.rs (limited to 'tic_tac_toe/src/main.rs') diff --git a/tic_tac_toe/src/main.rs b/tic_tac_toe/src/main.rs new file mode 100644 index 0000000..19285a2 --- /dev/null +++ b/tic_tac_toe/src/main.rs @@ -0,0 +1,162 @@ +mod ai; +mod board; +mod console_helpers; + +use ai::get_best_move; +use board::{Board, Coord, GameState, Tile, init_board, parse_coord}; +use console_helpers::{clear_scrn, write_header}; + +use std::io; + +fn render_board(board: &Board) -> () { + println!(" 1 2 3"); + let mut y = 1; + for row_i in 0..3 { + let row: Vec = (0..3).map(|col_i| board.state[col_i][row_i]).collect(); + print!("{y} "); + for tile in row { + print!(" {tile} ") + } + println!("\n"); + y += 1; + } +} + +fn get_coord_input() -> Coord { + loop { + let mut entered = String::new(); + match io::stdin().read_line(&mut entered) { + Ok(_) => {} + Err(_) => { + println!("Couldn't read input!"); + continue; + } + }; + + let coord = match parse_coord(&entered) { + Ok(c) => c, + Err(e) => { + println!("Couldn't parse input '{entered}'! ({e})"); + continue; + } + }; + + return coord; + } +} + +fn check_state(board: &Board) -> GameState { + //check row + let mut has_space = false; + for row_i in 0..3 { + let row: Vec = (0..3) + .map(|col_i| { + if !has_space && board.state[col_i][row_i] == Tile::Unowned { + has_space = true + } + return board.state[col_i][row_i]; + }) + .collect(); + if row[0] != Tile::Unowned && row[0] == row[1] && row[1] == row[2] { + return match row[0] { + Tile::PlayerOne => GameState::PlayerOneWin, + Tile::PlayerTwo => GameState::PlayerTwoWin, + Tile::Unowned => panic!("Impossible state"), + }; + } + } + + //check draw + if !has_space { + return GameState::Draw; + } + + //check col + for col in board.state { + if col[0] != Tile::Unowned && col[0] == col[1] && col[1] == col[2] { + return match col[0] { + Tile::PlayerOne => GameState::PlayerOneWin, + Tile::PlayerTwo => GameState::PlayerTwoWin, + Tile::Unowned => panic!("Impossible state"), + }; + } + } + + //check diagonal + if board.state[0][0] != Tile::Unowned + && board.state[0][0] == board.state[1][1] + && board.state[1][1] == board.state[2][2] + { + return match board.state[0][0] { + Tile::PlayerOne => GameState::PlayerOneWin, + Tile::PlayerTwo => GameState::PlayerTwoWin, + Tile::Unowned => panic!("Impossible state"), + }; + } + + if board.state[2][0] != Tile::Unowned + && board.state[2][0] == board.state[1][1] + && board.state[1][1] == board.state[0][2] + { + return match board.state[2][0] { + Tile::PlayerOne => GameState::PlayerOneWin, + Tile::PlayerTwo => GameState::PlayerTwoWin, + Tile::Unowned => panic!("Impossible state"), + }; + } + + //no wincon + return GameState::InProgress; +} + +fn main() { + let mut board = init_board(); + loop { + // clear_scrn(); + write_header("Current state"); + render_board(&board); + + println!("Enter a move (e.g., 1,2) >"); + let coord = get_coord_input(); + board.set_at_coord(coord, Tile::PlayerOne); + + match check_state(&board) { + GameState::InProgress => (), + GameState::Draw => { + println!("Draw!"); + break; + } + GameState::PlayerOneWin => { + println!("Player one wins!"); + break; + } + GameState::PlayerTwoWin => { + println!("Player two wins!"); + break; + } + } + + //get AI move + let ai_move = get_best_move(&board, Tile::PlayerTwo, true, 0); + board.set_at_coord(ai_move.coord, Tile::PlayerTwo); + + match check_state(&board) { + GameState::InProgress => (), + GameState::Draw => { + println!("Draw!"); + break; + } + GameState::PlayerOneWin => { + println!("Player one wins!"); + break; + } + GameState::PlayerTwoWin => { + println!("Player two wins!"); + break; + } + } + } + + render_board(&board); + println!("Game over!") +} -- cgit v1.2.3-70-g09d2