Skip to content

Commit

Permalink
Working 2 player version
Browse files Browse the repository at this point in the history
  • Loading branch information
JanEricNitschke committed Nov 10, 2024
1 parent 7b89d11 commit 1268b1d
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/mojo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ jobs:
uses: fabasoad/setup-mojo-action@v1
- name: Build
run: mojo build main.🔥
- name: Run
run: ./main
- name: Test
run: mojo test -I . test
2 changes: 1 addition & 1 deletion tictactoe_mojo/main.🔥
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from tictactoe.game import play

fn main():
play()
play[4](0, 0)
228 changes: 225 additions & 3 deletions tictactoe_mojo/tictactoe/game.🔥
Original file line number Diff line number Diff line change
@@ -1,3 +1,225 @@
fn play() -> Int:
print("Hello Mojo!")
return 1
alias OPEN: UInt8 = 0 # (00)
alias X: UInt8 = 1 # (01)
alias O: UInt8 = 2 # (10)
alias BOARD = SIMD[DType.uint8, _]

@always_inline("nodebug")
fn _board_construction_checks[size: Int]():
"""Checks if the board size is valid.

The board size is valid if it is a power of two and is positive.

Parameters:
size: The number of elements in the board.
"""
constrained[size > 0, "board size must be > 0"]()
constrained[size & (size - 1) == 0, "board size must be power of 2"]()

fn _swap_player(player: UInt8) -> UInt8:
"""Returns the other player.

Args:
player: The player to swap.

Returns:
The other player.
"""
return 3 - player

fn _ai_turn_o[size: Int](board: BOARD[size*size], strength: UInt8) -> Int:
"""Returns the move that the O player made.

Args:
board: The current board.
strength: The strength of the O player.

Returns:
The move the O player made as an index into the board.
"""
return 0

fn _ai_turn_x[size: Int](board: BOARD[size*size], strength: UInt8) -> Int:
"""Returns the move that the X player made.

Args:
board: The current board.
strength: The strength of the X player.

Returns:
The move the X player made as an index into the board.
"""
return 0

fn _player_turn[size: Int](board: BOARD[size*size], player: UInt8) -> Int:
"""Returns the move that the player made.

Args:
board: The current board.
player: The player that should make the move.

Returns:
The move the player made as an index into the board.
"""
var player_input: String
var move: Int
while True:
print("Player ", _int_to_player(player), " enter your move (0-", (board.size)-1, ").", sep="")
_show_board(board)
player_input = input()
try:
move = int(player_input)
if move >= 0 and move < board.size and board[move] == OPEN:
break
print("Enter a valid number and try again.")
except ValueError:
print("Enter a number and try again.")
continue

return move

fn _int_to_player(player: UInt8) -> String:
"""Returns the player as a string.

Args:
player: The player to convert.

Returns:
The player as a string.
"""
if player == X:
return "X"
elif player == O:
return "O"
else:
return "-"

fn _show_board[size: Int](board: BOARD[size*size]):
"""Prints the board to the console.

Args:
board: The board to print.
"""
var SEP: String = "-"
var SEPARATOR: String = SEP * (5 * size)
print(SEPARATOR)
for i in range(size):
for j in range(size):
print("| ", _int_to_player(board[i * size + j]), " |", end="", sep="")
print()
print(SEPARATOR)


fn _is_full[size: Int](board: BOARD[size*size]) -> Bool:
"""Returns True if the board is full.

Args:
board: The board to check.

Returns:
True if the board is full, False otherwise.
"""
return all(board != OPEN)

fn _is_winner_param[size: Int, //, player: UInt8](board: BOARD[size*size]) -> Bool:
@parameter
for row in range(size):
row_win = True
@parameter
for col in range(size):
if board[row * size + col] != player:
row_win = False
break
if row_win:
print("Row win")
return True

# Check columns
@parameter
for col in range(size):
col_win = True
@parameter
for row in range(size):
if board[row * size + col] != player:
col_win = False
break
if col_win:
print("Col win")
return True

# Check main diagonal
main_diag_win = True
@parameter
for i in range(size):
if board[i * size + i] != player:
main_diag_win = False
break
if main_diag_win:
print("Main diag win")
return True

# Check anti-diagonal
anti_diag_win = True
@parameter
for i in range(size):
if board[i * size + (size - 1 - i)] != player:
anti_diag_win = False
break
if anti_diag_win:
print("Anti diag win")
return True
print("No win")
return False


fn _is_winner[size: Int](board: BOARD[size*size], player: UInt8) -> Bool:
"""Returns True if the player is the winner.

Args:
board: The board to check.
player: The player to check.

Returns:
True if the player is the winner, False otherwise.
"""
if player == X:
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:
"""Returns the move that the player made.

Args:
board: The current board.
player: The player that should make the move.
x_strength: The strength of the X player. (0 means human)
o_strength: The strength of the O player. (0 means human)

Returns:
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)
else:
move = _player_turn(board, player)
return move

fn play[size: Int](x_strength: UInt8, o_strength: UInt8):
_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
if _is_winner(board, player):
print("Player ", _int_to_player(player), " wins!", sep="")
break
if _is_full(board):
print("It's a draw!")
break
player = _swap_player(player)

_show_board(board)

0 comments on commit 1268b1d

Please sign in to comment.