From 166a5c16768afcbda44f261ec36201cf44927bbb Mon Sep 17 00:00:00 2001 From: Jan-Eric Nitschke <47750513+JanEricNitschke@users.noreply.github.com> Date: Sun, 10 Nov 2024 18:47:21 +0100 Subject: [PATCH] Got mojo version running --- README.md | 11 +++ "tictactoe_mojo/main.\360\237\224\245" | 2 +- .../test/test_game.\360\237\224\245" | 13 ++- .../tictactoe/game.\360\237\224\245" | 94 ++++++++++++++++--- 4 files changed, 101 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6f0bf84..992d45f 100644 --- a/README.md +++ b/README.md @@ -532,6 +532,17 @@ Run with: dreamberd tictactoe.db ``` +## TicTacToe-Mojo + +Version using [Mojo](https://docs.modular.com/mojo/manual/). + +Build, test and run with: +``` +mojo build main.🔥 +mojo test -I . test +./main +``` + ## TicTacToe-scratch Very simple two player tictactoe game with Scratch. diff --git "a/tictactoe_mojo/main.\360\237\224\245" "b/tictactoe_mojo/main.\360\237\224\245" index eebf660..6493f88 100644 --- "a/tictactoe_mojo/main.\360\237\224\245" +++ "b/tictactoe_mojo/main.\360\237\224\245" @@ -1,4 +1,4 @@ from tictactoe.game import play fn main(): - play[4](0, 0) + play[4](1, 1) diff --git "a/tictactoe_mojo/test/test_game.\360\237\224\245" "b/tictactoe_mojo/test/test_game.\360\237\224\245" index a6e14e1..e0e4031 100644 --- "a/tictactoe_mojo/test/test_game.\360\237\224\245" +++ "b/tictactoe_mojo/test/test_game.\360\237\224\245" @@ -1,5 +1,12 @@ from testing import assert_equal -from tictactoe.game import play +from tictactoe.game import Move, BOARD, X, O, OPEN, _swap_player, _minmax -def test_play(): - assert_equal(play(), 1) +def test_minmax(): + var board = BOARD[16](X, O, OPEN, OPEN, X, O, OPEN, OPEN, X, O, OPEN, OPEN, OPEN, OPEN, OPEN, OPEN) + var result = _minmax[X, 4](board) + assert_equal(result.score, 1) + assert_equal(result.spot, 12) + +def test_swap_player(): + assert_equal(_swap_player(X), O) + assert_equal(_swap_player(O), X) diff --git "a/tictactoe_mojo/tictactoe/game.\360\237\224\245" "b/tictactoe_mojo/tictactoe/game.\360\237\224\245" index 5bf9777..0558370 100644 --- "a/tictactoe_mojo/tictactoe/game.\360\237\224\245" +++ "b/tictactoe_mojo/tictactoe/game.\360\237\224\245" @@ -1,8 +1,17 @@ +from random import seed, random_ui64 +from time import sleep + alias OPEN: UInt8 = 0 # (00) alias X: UInt8 = 1 # (01) alias O: UInt8 = 2 # (10) alias BOARD = SIMD[DType.uint8, _] +@value +struct Move: + var spot: Int + var score: Int + + @always_inline("nodebug") fn _board_construction_checks[size: Int](): """Checks if the board size is valid. @@ -26,19 +35,64 @@ fn _swap_player(player: UInt8) -> UInt8: """ return 3 - player -fn _ai_turn_o[size: Int](board: BOARD[size*size], strength: UInt8) -> Int: - """Returns the move that the O player made. +fn _minmax[player: UInt8, size: Int](inout board: BOARD[size*size]) -> Move: + """Returns the best move for the player. Args: board: The current board. - strength: The strength of the O player. + + Parameters: + player: The player to get the best move for. + + Returns: + The best move for the player. + """ + if _is_winner_param[player](board): + return Move(-1, 1) + @parameter + if player == X: + if _is_winner_param[O](board): + return Move(-1, -1) + else: + if _is_winner_param[X](board): + return Move(-1, -1) + var empty_cells = _get_empty_cells(board) + if empty_cells.size == 0: + return Move(-1, 0) + var best_move = Move(-1, -2) + var score: Int + for spot in empty_cells: + board[spot[]] = player + if player == X: + score = -(_minmax[O](board).score) + else: + score = -(_minmax[X](board).score) + board[spot[]] = OPEN + if score > best_move.score: + best_move.score = score + best_move.spot = spot[] + if score == 1: + break + return best_move + +fn _get_empty_cells[size: Int](board: BOARD[size*size]) -> List[Int]: + """Returns the empty cells in the board. + + Args: + board: The board to check. Returns: - The move the O player made as an index into the board. + The empty cells in the board. """ - return 0 + var empty_cells = List[Int]() + @parameter + for i in range(board.size): + if board[i] == OPEN: + empty_cells.append(i) + return empty_cells -fn _ai_turn_x[size: Int](board: BOARD[size*size], strength: UInt8) -> Int: + +fn _ai_turn[size: Int, //, player: UInt8](inout board: BOARD[size*size], strength: UInt8) -> Int: """Returns the move that the X player made. Args: @@ -48,7 +102,17 @@ fn _ai_turn_x[size: Int](board: BOARD[size*size], strength: UInt8) -> Int: Returns: The move the X player made as an index into the board. """ - return 0 + print("AI turn as ", _int_to_player(player), " with strength ", strength, ".", sep="") + _show_board(board) + var empty_cells = _get_empty_cells(board) + var spot: Int + if empty_cells.size >= 12: + spot = empty_cells[int(random_ui64(0, empty_cells.size-1))] + else: + spot = _minmax[player](board).spot + sleep(1) + return spot + fn _player_turn[size: Int](board: BOARD[size*size], player: UInt8) -> Int: """Returns the move that the player made. @@ -174,7 +238,7 @@ fn _is_winner[size: Int](board: BOARD[size*size], player: UInt8) -> Bool: return _is_winner_param[X](board) return _is_winner_param[O](board) -fn _play[size: Int](board: BOARD[size*size], player: UInt8, x_strength: UInt8, o_strength: UInt8) -> Int: +fn _play[size: Int](inout board: BOARD[size*size], player: UInt8, x_strength: UInt8, o_strength: UInt8): """Returns the move that the player made. Args: @@ -187,22 +251,22 @@ fn _play[size: Int](board: BOARD[size*size], player: UInt8, x_strength: UInt8, o The move the player made as an index into the board. """ var move: Int - if player == 1 and x_strength > 0: - move = _ai_turn_x(board, x_strength) - elif player == 3 and o_strength > 0: - move = _ai_turn_o(board, o_strength) + if player == X and x_strength > 0: + move = _ai_turn[X](board, x_strength) + elif player == O and o_strength > 0: + move = _ai_turn[O](board, o_strength) else: move = _player_turn(board, player) - return move + board[move] = player fn play[size: Int](x_strength: UInt8, o_strength: UInt8): + seed() _board_construction_checks[size]() var board = BOARD[size*size](0) var player: UInt8 = 1 while True: - var move = _play(board, player, x_strength, o_strength) - board[move] = player + _play(board, player, x_strength, o_strength) if _is_winner(board, player): print("Player ", _int_to_player(player), " wins!", sep="") break