Skip to content

full code #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea/
__pycache__/g
18 changes: 18 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from src.view.console_board_view import ConsoleBoardView
from src.state import StateChecker
from src.tic_tac_toe_runner import TicTacToeRunner
from src.data.board import Board

def init_board():
board_size = int(input("choose board size --->"))
return Board(board_size)

def main():
state_checker = StateChecker()
board_view = ConsoleBoardView()
runner = TicTacToeRunner(state_checker, board_view, init_board())
runner.run()


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
numpy==1.19.5
pytest==7.1.2
Empty file added src/__init__.py
Empty file.
Empty file added src/data/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions src/data/board.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import numpy as np
from src.state import State

class Board:
def __init__(self, size = 3):
self._matrix = np.zeros((size, size), int)
self.size = size

def fill_cell(self, index: tuple, state: State):
self._matrix[index[0], index[1]] = state.value

def get_matrix(self):
return self._matrix

def is_valid_coordinates(self, coordinates: (int, int)):
try:
if (self._matrix[coordinates[0], coordinates[1]] == State.Empty.value):
return True
return False
except KeyError:
return False
2 changes: 2 additions & 0 deletions src/state/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .state_type import *
from .state_checker import *
25 changes: 25 additions & 0 deletions src/state/state_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from .state_type import State
import numpy as np


class StateChecker:

def is_won(self, board):
board_matrix = board.get_matrix()
board_matrix_t = np.transpose(board_matrix)
return self._scan_rows(board_matrix) or self._scan_rows(board_matrix_t) \
or self._scan_diagonals(board_matrix)

def _scan_rows(self, matrix):
for row in matrix:
# this if checks whether the entire row has the same value and its not empty
if (len(set(row)) == 1 and row[0] != State.Empty.value):
return True
return False

def _scan_diagonals(self, matrix):
is_diag_win = self._scan_rows([np.diag(matrix)])
if (not is_diag_win):
# the secondary diagonal
is_diag_win = self._scan_rows([np.diag(np.rot90(matrix))])
return is_diag_win
6 changes: 6 additions & 0 deletions src/state/state_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum

class State(Enum):
Empty = 0
X = 1
O = 2
41 changes: 41 additions & 0 deletions src/tic_tac_toe_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from src.data.board import Board
from src.state import StateChecker, State
from src.view.console_board_view import ConsoleBoardView


class TicTacToeRunner:
def __init__(self, state_checker: StateChecker, board_view: ConsoleBoardView,
board: Board):
self.state_checker = state_checker
self.board_view = board_view
self.board = board

def run(self):
turns_elapsed = 0
max_turns = self.board.size ** 2
while turns_elapsed < max_turns:
self.board_view.show_board(self.board)
user_turn = ('X', State.X) if turns_elapsed % 2 == 0 else ('O', State.O)
coordinates = self._get_user_input(user_turn)
self.board.fill_cell(coordinates, user_turn[1])
has_finished = self.state_checker.is_won(self.board)
if has_finished:
print(F"its a win for {user_turn[0]}!")
return
turns_elapsed += 1
print("this game ends with a tie")

def _get_user_input(self, whos_turn) -> (int, int):
while True:
try:
coordinates_raw = input(F"{whos_turn[0]} turn, select coordinates <x, y>")
split = coordinates_raw.split(',')
coordinates = int(split[0]), int(split[1])
if (self.board.is_valid_coordinates(coordinates)):
return coordinates
else:
raise Exception("invalid coordinates, try again")
except KeyError or ValueError:
print("please input coordinates in the following format: x,y")
except Exception as e:
print(e)
17 changes: 17 additions & 0 deletions src/view/console_board_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from src.data.board import Board

class ConsoleBoardView:

def show_board(self, board: Board):
vertical_border = '| '
self.__print_horizontal_border(len(board.get_matrix()))
for row in board.get_matrix():
print(vertical_border , end="")
for cell in row:
print(" " if cell == 0 else 'X' if cell == 1 else 'O', vertical_border, end="")
print()
self.__print_horizontal_border(len(board.get_matrix()))

def __print_horizontal_border(self, length):
horizontal_border = ' --- '
print(horizontal_border * length)
20 changes: 20 additions & 0 deletions tests/test_board.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from src.data.board import Board
from src.state import State


class TestBoard:
board = Board()

def test_fill_cell_updates_correct_cell(self):
self.board.fill_cell((0, 0), State.O)
assert self.board.get_matrix()[0][0] == State.O.value

def test_board_is_valid_coordinates_false_on_taken_cell(self):
coordinates = (0, 0)
self.board.fill_cell(coordinates, State.X)
assert self.board.is_valid_coordinates(coordinates) == False

def test_board_is_valid_coordinates_true_on_empty_cell(self):
coordinates = (0, 0)
self.board.fill_cell(coordinates, State.X)
assert self.board.is_valid_coordinates((0, 1)) == True
42 changes: 42 additions & 0 deletions tests/test_state_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from src.state import State, StateChecker
from unittest.mock import Mock

class TestStateChecker:
state_checker = StateChecker()
board_mock = Mock()

def test_is_won_returns_false_on_empty_board(self):
game_matrix = [[State.Empty.value for j in range(3)] for i in range(3)]
self.board_mock.get_matrix.return_value = game_matrix
assert self.state_checker.is_won(self.board_mock) == False

def test_is_won_returns_true_on_row_of_x(self):
game_matrix = [[State.X.value for j in range(3)] for i in range(1)] \
+ [[State.Empty.value for k in range(2)]]
self.board_mock.get_matrix.return_value = game_matrix
assert self.state_checker.is_won(self.board_mock) == True

def test_is_won_returns_true_on_row_of_o(self):
game_matrix = [[State.O.value for j in range(3)] for i in range(1)] \
+ [[State.Empty.value for k in range(2)]]
self.board_mock.get_matrix.return_value = game_matrix
assert self.state_checker.is_won(self.board_mock) == True

def test_is_won_returns_true_on_column_of_x(self):
game_matrix = [[State.X.value, State.Empty.value, State.Empty.value] for i in range(3)]
self.board_mock.get_matrix.return_value = game_matrix
assert self.state_checker.is_won(self.board_mock) == True

def test_is_won_returns_true_on_diagonal_of_x(self):
game_matrix = [[State.X.value, State.Empty.value, State.Empty.value],
[State.O.value, State.X.value, State.Empty.value],
[State.O.value, State.Empty.value, State.X.value]]
self.board_mock.get_matrix.return_value = game_matrix
assert self.state_checker.is_won(self.board_mock) == True

def test_is_won_returns_true_on_secondary_diagonal_of_x(self):
game_matrix = [[State.O.value, State.Empty.value, State.X.value],
[State.O.value, State.X.value, State.Empty.value],
[State.X.value, State.Empty.value, State.O.value]]
self.board_mock.get_matrix.return_value = game_matrix
assert self.state_checker.is_won(self.board_mock) == True