School Commit Init
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
@@ -0,0 +1,43 @@
|
||||
# :video_game: Assignment 09
|
||||
## Requirements
|
||||
- You will be given one of the problems below to solve
|
||||
- Use object oriented programming and layered architecture
|
||||
- All modules with the exception of the UI will have specifications and PyUnit test cases
|
||||
- The program must protect itself against the user’s invalid input
|
||||
|
||||
**NB!** We do not expect you to implement optimal play for the computer player. However, it should still employ a strategy when making its moves in order to attempt to win the game and provide an entertaining opponent for the human player. Minimally, the computer player should move to win the game whenever possible and should block the human player’s attempts at 1-move victory, whenever possible
|
||||
|
||||
**deadline is week 14**
|
||||
|
||||
## GUI Bonus (0.2P)
|
||||
- In addition to the console-based user interface required, also implement a graphical user interface (GUI) for the program
|
||||
- To receive the bonus, both user interfaces (menu-based and graphical) must use the same program layers. You have to be able to start the application with either user interface
|
||||
## AI Bonus (0.2P)
|
||||
- Implement computer AI using a [minimax algorithm](https://en.wikipedia.org/wiki/Minimax). Computer play should be competitive against the human player
|
||||
- In the case where minimax cannot be applied (e.g. Battleship, which is not a [complete information](https://en.wikipedia.org/wiki/Complete_information) game), find a suitable alternative; talk to your lab professor about the bonus possibility in this case
|
||||
## Best-of-FP Bonus (0.2P)
|
||||
- This bonus will be awarded to the very best implementations. To receive it, you need to implement both the **GUI** and **AI** bonuses, follow all implementation requirements, and have your work be selected by the laboratory professor
|
||||
- These implementations will be part of a separate GitHub repository that we aim to make publicly accessible in order to feature some of our students' best work during this semester
|
||||
|
||||
|
||||
## Problem Statements
|
||||
### Connect Four
|
||||
The game is described [here](https://en.wikipedia.org/wiki/Connect_Four)
|
||||
|
||||
### Gomoku
|
||||
The game is described [here](https://en.wikipedia.org/wiki/Gomoku)
|
||||
|
||||
### Obstruction
|
||||
The game is described [here](http://www.papg.com/show?2XMX)
|
||||
|
||||
### Battleship
|
||||
The game is described [here](https://en.wikipedia.org/wiki/Battleship_(game))
|
||||
|
||||
### Planes
|
||||
The game is described [here](https://ro.wikipedia.org/wiki/Avioane_(joc))
|
||||
|
||||
### Nine men's morris
|
||||
The game is described [here](https://en.wikipedia.org/wiki/Nine_men%27s_morris)
|
||||
|
||||
### Other games!
|
||||
You are free to implement a different board game, as long as its complexity is similar to those above. Talk to your laboratory professor to validate your idea before starting work!
|
||||
@@ -0,0 +1,14 @@
|
||||
from src.Game.game import Game
|
||||
from src.Board.board import Board
|
||||
from src.AI.ai import AI
|
||||
from src.UI.ui import UI
|
||||
|
||||
def main() -> None:
|
||||
board = Board()
|
||||
ai = AI()
|
||||
game = Game(board, ai)
|
||||
ui = UI(game)
|
||||
ui.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,79 @@
|
||||
from src.Board.board import Board
|
||||
import random
|
||||
|
||||
class AI:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:returns:
|
||||
None
|
||||
|
||||
This is the constructor for the AI class. It is called when the game class is initialized. It does not take any parameters.
|
||||
"""
|
||||
|
||||
def get_move(self, board_state: Board) -> int:
|
||||
"""
|
||||
:params:
|
||||
board_state: Board
|
||||
:returns:
|
||||
int
|
||||
|
||||
This function is called by the game class to get the AI's move. It should return an integer between 0 and 6. This integer represents the column the AI wants to play in.
|
||||
"""
|
||||
move=self.__check_imeadiate_win_or_loss(board_state,2)
|
||||
if move is not None:
|
||||
return move
|
||||
move=self.__check_imeadiate_win_or_loss(board_state,1)
|
||||
if move is not None:
|
||||
return move
|
||||
moves=self.__get_all_possible_moves(board_state)
|
||||
return random.choice(moves)
|
||||
|
||||
def __get_all_possible_moves(self, board_state: Board) -> list[int]:
|
||||
"""
|
||||
:params:
|
||||
board_state: Board
|
||||
:returns:
|
||||
list[int]
|
||||
|
||||
This function returns a list of all possible moves the AI can make. This is used to randomly select a move if the AI can't win or lose in the next move.
|
||||
"""
|
||||
moves=[]
|
||||
for i in range(7):
|
||||
if board_state.check_cell(5,i)==None:
|
||||
moves.append(i)
|
||||
return moves
|
||||
|
||||
def __check_imeadiate_win_or_loss(self, board_state: Board,player: int) -> int:
|
||||
"""
|
||||
:params:
|
||||
board_state: Board
|
||||
player: int
|
||||
:returns:
|
||||
int
|
||||
|
||||
This function checks if the AI can win or lose in the next move. If it can, it returns the column it should play in. If it can't, it returns None.
|
||||
"""
|
||||
for column in range(7):
|
||||
for row in range(6):
|
||||
if column + 3 < 7:
|
||||
to_check = [board_state.get_board()[column+i][row] for i in range(4)]
|
||||
if to_check.count(player) == 3 and to_check.count(None) == 1 and (row==0 or board_state.check_cell(row-1,column+to_check.index(None)) is not None):
|
||||
return column + to_check.index(None)
|
||||
if row + 3 < 6:
|
||||
to_check = [board_state.get_board()[column][row+i] for i in range(4)]
|
||||
if to_check.count(player) == 3 and to_check.count(None) == 1:
|
||||
return column
|
||||
if column + 3 < 7 and row + 3 < 6:
|
||||
to_check = [board_state.get_board()[column+i][row+i] for i in range(4)]
|
||||
if to_check.count(player) == 3 and to_check.count(None) == 1 and (row-to_check.index(None) == 0 or board_state.check_cell(row+to_check.index(None)-1,column+to_check.index(None)) is not None):
|
||||
return column + to_check.index(None)
|
||||
if column + 3 < 7 and row - 3 >= 0:
|
||||
to_check = [board_state.get_board()[column+i][row-i] for i in range(4)]
|
||||
if to_check.count(player) == 3 and to_check.count(None) == 1 and (row-to_check.index(None) == 0 or board_state.check_cell(row-to_check.index(None)-1,column+to_check.index(None)) is not None):
|
||||
return column + to_check.index(None)
|
||||
return None
|
||||
|
||||
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
class Board:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
None
|
||||
|
||||
This is the constructor for the Board class. It sets up the board and the empty spaces. It also sets up the unicode characters for the circles that will be used to represent the pieces.
|
||||
"""
|
||||
self.__green_circle= "\033[32m\u2b24\033[0m"
|
||||
self.__red_circle= "\033[31m\u2b24\033[0m"
|
||||
self.__board = [[None for _ in range(6)] for _ in range(7)]
|
||||
self.__empty_spaces = 42
|
||||
|
||||
def __str__(self) -> str:
|
||||
return """
|
||||
|
||||
| {5} | {11} | {17} | {23} | {29} | {35} | {41} |
|
||||
+---+---+---+---+---+---+---+
|
||||
| {4} | {10} | {16} | {22} | {28} | {34} | {40} |
|
||||
+---+---+---+---+---+---+---+
|
||||
| {3} | {9} | {15} | {21} | {27} | {33} | {39} |
|
||||
+---+---+---+---+---+---+---+
|
||||
| {2} | {8} | {14} | {20} | {26} | {32} | {38} |
|
||||
+---+---+---+---+---+---+---+
|
||||
| {1} | {7} | {13} | {19} | {25} | {31} | {37} |
|
||||
+---+---+---+---+---+---+---+
|
||||
| {0} | {6} | {12} | {18} | {24} | {30} | {36} |
|
||||
+---+---+---+---+---+---+---+
|
||||
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
|
||||
""".format(*[self.__green_circle if i==1 else self.__red_circle if i==2 else " " for row in self.__board for i in row])
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self)
|
||||
|
||||
def place(self, column: int, player: int) -> int:
|
||||
"""
|
||||
:params:
|
||||
column: int
|
||||
player: int
|
||||
:return:
|
||||
int
|
||||
|
||||
This method places a piece in the board. It will raise an exception if the column is full. It returns the row where the piece was placed.
|
||||
"""
|
||||
for row in range(6):
|
||||
if self.__board[column][row] == None:
|
||||
self.__board[column][row] = player
|
||||
self.__empty_spaces -= 1
|
||||
return row
|
||||
raise Exception("Column is full already!")
|
||||
|
||||
def is_full(self) -> bool:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
bool
|
||||
|
||||
This method returns True if the board is full, False otherwise.
|
||||
"""
|
||||
return self.__empty_spaces == 0
|
||||
|
||||
def reset(self) -> None:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
None
|
||||
|
||||
This method resets the board.
|
||||
"""
|
||||
self.__board = [[None for _ in range(6)] for _ in range(7)]
|
||||
self.__empty_spaces = 42
|
||||
|
||||
def get_board(self) -> list[list[int]]:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
list[list[int]]
|
||||
|
||||
This method returns the board as a list of lists of ints. 1 represents a green piece, 2 represents a red piece and None represents an empty space.
|
||||
"""
|
||||
return self.__board
|
||||
|
||||
def check_cell(self, row: int, column: int) -> int:
|
||||
"""
|
||||
:params:
|
||||
row: int
|
||||
column: int
|
||||
:return:
|
||||
int
|
||||
|
||||
This method returns the value of the cell in the given row and column. 1 represents a green piece, 2 represents a red piece and None represents an empty space.
|
||||
"""
|
||||
return self.__board[column][row]
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
from src.Board.board import Board
|
||||
from src.AI.ai import AI
|
||||
import random
|
||||
|
||||
class Game:
|
||||
def __init__(self, board: Board, ai: AI) -> None:
|
||||
"""
|
||||
:params:
|
||||
board: Board
|
||||
ai: AI
|
||||
:return:
|
||||
None
|
||||
|
||||
This is the constructor for the Game class. It takes a Board object and an AI object as parameters. It also sets the score to 0-0 and randomly chooses who goes first.
|
||||
"""
|
||||
self.__board = board
|
||||
self.__ai = ai
|
||||
self.__score = [0, 0]
|
||||
self.__first_player = random.choice(["Player", "AI"])
|
||||
if self.__first_player == "AI":
|
||||
self.__play_ai()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f" Connect 4 - Score : Player: {self.__score[0]} | AI: {self.__score[1]}"+str(self.__board)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self)
|
||||
|
||||
def play(self, column: int) -> None:
|
||||
"""
|
||||
:params:
|
||||
column: int
|
||||
:return:
|
||||
None
|
||||
|
||||
This function is called by the main class to play a move. It takes an integer between 0 and 6 as a parameter. This integer represents the column the player wants to play in.
|
||||
"""
|
||||
lpo = self.__play_human(column)
|
||||
if self.__check_win(lpo):
|
||||
self.__score[0] += 1
|
||||
return "Player"
|
||||
if self.__board.is_full():
|
||||
return "Draw"
|
||||
lpo = self.__play_ai()
|
||||
if self.__check_win(lpo):
|
||||
self.__score[1] += 1
|
||||
return "AI"
|
||||
if self.__board.is_full():
|
||||
return "Draw"
|
||||
return None
|
||||
|
||||
def get_score(self) -> list[int]:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
list[int]
|
||||
|
||||
This method returns the score as a list of ints. The first int represents the player's score and the second int represents the AI's score.
|
||||
"""
|
||||
return self.__score
|
||||
|
||||
def __check_win(self, lpo: tuple[int,int]) -> bool:
|
||||
"""
|
||||
:params:
|
||||
lpo: tuple[int,int]
|
||||
:return:
|
||||
bool
|
||||
|
||||
This method checks if the last piece played has won the game. It takes a tuple of ints as a parameter. This tuple represents the last piece played.
|
||||
"""
|
||||
return self.__check_column_win(lpo) or self.__check_row_win(lpo) or self.__check_diagonal1_win(lpo) or self.__check_diagonal2_win(lpo)
|
||||
|
||||
def __check_column_win(self, lpo: tuple[int,int]) -> bool:
|
||||
"""
|
||||
:params:
|
||||
lpo: tuple[int,int]
|
||||
:return:
|
||||
bool
|
||||
|
||||
This method checks if the last piece played has won the game by checking the column. It takes a tuple of ints as a parameter. This tuple represents the last piece played.
|
||||
"""
|
||||
check = 0
|
||||
for row in range(5):
|
||||
if self.__board.check_cell(row, lpo[1]) == self.__board.check_cell(row + 1, lpo[1]) and self.__board.check_cell(row, lpo[1]) != None:
|
||||
check += 1
|
||||
else:
|
||||
check = 0
|
||||
if check == 3:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __check_row_win(self, lpo: tuple[int,int]) -> bool:
|
||||
"""
|
||||
:params:
|
||||
lpo: tuple[int,int]
|
||||
:return:
|
||||
bool
|
||||
|
||||
This method checks if the last piece played has won the game by checking the row. It takes a tuple of ints as a parameter. This tuple represents the last piece played.
|
||||
"""
|
||||
check = 0
|
||||
for column in range(6):
|
||||
if self.__board.check_cell(lpo[0], column) == self.__board.check_cell(lpo[0], column + 1) and self.__board.check_cell(lpo[0], column) != None:
|
||||
check += 1
|
||||
else:
|
||||
check = 0
|
||||
if check == 3:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __check_diagonal1_win(self, lpo: tuple[int,int]) -> bool:
|
||||
"""
|
||||
:params:
|
||||
lpo: tuple[int,int]
|
||||
:return:
|
||||
bool
|
||||
|
||||
This method checks if the last piece played has won the game by checking the first diagonal. It takes a tuple of ints as a parameter. This tuple represents the last piece played.
|
||||
"""
|
||||
check = 0
|
||||
offset = -lpo[0] if lpo[0] < lpo[1] else -lpo[1]
|
||||
while lpo[0]+offset < 5 and lpo[1]+offset < 6:
|
||||
if self.__board.check_cell(lpo[0]+offset, lpo[1]+offset) == self.__board.check_cell(lpo[0]+offset+1, lpo[1]+offset+1) and self.__board.check_cell(lpo[0]+offset, lpo[1]+offset) != None:
|
||||
check += 1
|
||||
else:
|
||||
check = 0
|
||||
if check == 3:
|
||||
return True
|
||||
offset += 1
|
||||
|
||||
def __check_diagonal2_win(self, lpo: tuple[int,int]) -> bool:
|
||||
"""
|
||||
:params:
|
||||
lpo: tuple[int,int]
|
||||
:return:
|
||||
bool
|
||||
|
||||
This method checks if the last piece played has won the game by checking the second diagonal. It takes a tuple of ints as a parameter. This tuple represents the last piece played.
|
||||
"""
|
||||
check = 0
|
||||
offset = -lpo[0] if lpo[0] < 6-lpo[1] else lpo[1]-5
|
||||
while lpo[0]+offset < 5 and lpo[1]-offset > 0:
|
||||
if self.__board.check_cell(lpo[0]+offset, lpo[1]-offset) == self.__board.check_cell(lpo[0]+offset+1, lpo[1]-offset-1) and self.__board.check_cell(lpo[0]+offset, lpo[1]-offset) != None:
|
||||
check += 1
|
||||
else:
|
||||
check = 0
|
||||
if check == 3:
|
||||
return True
|
||||
offset += 1
|
||||
|
||||
|
||||
def reset_board(self) -> None:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
None
|
||||
|
||||
This method resets the board and chooses a random player to start the game.
|
||||
"""
|
||||
self.__board.reset()
|
||||
self.__first_player = random.choice(["Player", "AI"])
|
||||
if self.__first_player == "AI":
|
||||
self.__play_ai()
|
||||
|
||||
def reset_game(self) -> None:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
None
|
||||
|
||||
This method resets the game and the board.
|
||||
"""
|
||||
self.__score = [0, 0]
|
||||
self.reset_board()
|
||||
|
||||
def __play_ai(self) -> tuple[int,int]:
|
||||
"""
|
||||
:params:
|
||||
None
|
||||
:return:
|
||||
tuple[int,int]
|
||||
|
||||
This method plays the AI and returns the row and column of the last piece played.
|
||||
"""
|
||||
column = self.__ai.get_move(self.__board)
|
||||
row = self.__board.place(column, 2)
|
||||
return (row, column)
|
||||
|
||||
def __play_human(self, column: int) -> tuple[int,int]:
|
||||
"""
|
||||
:params:
|
||||
column: int
|
||||
:return:
|
||||
tuple[int,int]
|
||||
|
||||
This method plays the human and returns the row and column of the last piece played.
|
||||
"""
|
||||
row = self.__board.place(column-1, 1)
|
||||
return (row, column-1)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from src.Game.game import Game
|
||||
|
||||
class UI:
|
||||
def __init__(self, game: Game) -> None:
|
||||
self.game = game
|
||||
|
||||
def start(self) -> None:
|
||||
print("\nTo play, enter a column number. To quit, enter 'quit'. To reset the board, enter 'reset'. To reset the game, enter 'reset game'.\n")
|
||||
while True:
|
||||
print(self.game)
|
||||
command = input("Enter a column: ")
|
||||
if command == "quit":
|
||||
break
|
||||
elif command == "reset":
|
||||
self.game.reset_board()
|
||||
elif command == "reset game":
|
||||
self.game.reset_game()
|
||||
elif command.isnumeric() and 0 < int(command) and int(command) < 8:
|
||||
try:
|
||||
winner = self.game.play(int(command))
|
||||
if winner == "Draw":
|
||||
print(self.game)
|
||||
print("Draw!")
|
||||
input("Press enter to continue")
|
||||
self.game.reset_board()
|
||||
continue
|
||||
if winner is not None:
|
||||
print(self.game)
|
||||
print(f"{winner} won!")
|
||||
input("Press enter to continue")
|
||||
self.game.reset_board()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
else:
|
||||
print("Invalid command")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import unittest
|
||||
|
||||
from src.Board.board import Board
|
||||
from src.AI.ai import AI
|
||||
|
||||
class AI_Tester(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.ai=AI()
|
||||
self.board=Board()
|
||||
return super().setUp()
|
||||
def tearDown(self) -> None:
|
||||
self.ai=None
|
||||
return super().tearDown()
|
||||
def test_get_move_random(self):
|
||||
self.board.reset()
|
||||
for i in range(6):
|
||||
self.board.place(0,i%2+1)
|
||||
self.board.place(1,i%2+1)
|
||||
self.board.place(2,(i+1)%2+1)
|
||||
self.board.place(3,(i+1)%2+1)
|
||||
self.board.place(5,i%2+1)
|
||||
self.board.place(6,i%2+1)
|
||||
self.assertEqual(self.ai.get_move(self.board),4)
|
||||
def test_get_move_win(self):
|
||||
self.board.reset()
|
||||
self.board.place(0,2)
|
||||
self.board.place(1,2)
|
||||
self.board.place(2,2)
|
||||
self.assertEqual(self.ai.get_move(self.board),3)
|
||||
def test_get_move_block(self):
|
||||
self.board.reset()
|
||||
self.board.place(0,1)
|
||||
self.board.place(1,1)
|
||||
self.board.place(2,1)
|
||||
self.assertEqual(self.ai.get_move(self.board),3)
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import unittest
|
||||
|
||||
from src.Board.board import Board
|
||||
|
||||
class Board_Tester(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.board=Board()
|
||||
return super().setUp()
|
||||
def tearDown(self) -> None:
|
||||
self.board=None
|
||||
return super().tearDown()
|
||||
|
||||
def test_place(self):
|
||||
self.board.reset()
|
||||
for i in range(6):
|
||||
self.board.place(0,1)
|
||||
self.assertEqual(self.board.check_cell(i,0),1)
|
||||
self.assertRaises(Exception,self.board.place,0,1)
|
||||
def test_is_full(self):
|
||||
self.board.reset()
|
||||
for i in range(7):
|
||||
for j in range(6):
|
||||
self.assertFalse(self.board.is_full())
|
||||
self.board.place(i,1)
|
||||
self.assertTrue(self.board.is_full())
|
||||
def test_reset(self):
|
||||
self.board.reset()
|
||||
self.board.place(0,1)
|
||||
self.board.reset()
|
||||
self.assertEqual(self.board.check_cell(0,0),None)
|
||||
def test_get_board(self):
|
||||
self.board.reset()
|
||||
for i in range(7):
|
||||
for j in range(6):
|
||||
self.board.place(i,1)
|
||||
board_state=self.board.get_board()
|
||||
for i in range(7):
|
||||
for j in range(6):
|
||||
self.assertEqual(board_state[i][j],1)
|
||||
def test_check_cell(self):
|
||||
self.board.reset()
|
||||
for i in range(7):
|
||||
for j in range(6):
|
||||
self.board.place(i,1)
|
||||
for i in range(7):
|
||||
for j in range(6):
|
||||
self.assertEqual(self.board.check_cell(j,i),1)
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
from src.Game.game import Game
|
||||
from src.Board.board import Board
|
||||
from src.AI.ai import AI
|
||||
import unittest
|
||||
|
||||
class Game_Tester(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.board=Board()
|
||||
self.ai=AI()
|
||||
self.game=Game(self.board,self.ai)
|
||||
return super().setUp()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.game=None
|
||||
return super().tearDown()
|
||||
|
||||
def test_play(self):
|
||||
self.game.reset_game()
|
||||
self.board.reset()
|
||||
self.board.place(0,1)
|
||||
self.board.place(1,1)
|
||||
self.game.play(3)
|
||||
self.assertEqual(self.board.check_cell(0,0),1)
|
||||
self.assertEqual(self.board.check_cell(0,1),1)
|
||||
self.assertEqual(self.board.check_cell(0,2),1)
|
||||
self.assertEqual(self.board.check_cell(0,3),2)
|
||||
|
||||
def test_get_score(self):
|
||||
self.game.reset_game()
|
||||
self.assertListEqual(self.game.get_score(),[0,0])
|
||||
self.board.reset()
|
||||
self.board.place(0,1)
|
||||
self.board.place(1,1)
|
||||
self.board.place(2,1)
|
||||
self.game.play(4)
|
||||
self.assertListEqual(self.game.get_score(),[1,0])
|
||||
|
||||
def test_reset_board(self):
|
||||
self.game.reset_board()
|
||||
count=0
|
||||
for i in range(7):
|
||||
if self.board.check_cell(0,i)==None:
|
||||
count+=1
|
||||
self.assertGreaterEqual(count,6)
|
||||
def test_reset_game(self):
|
||||
self.game.reset_game()
|
||||
count=0
|
||||
for i in range(7):
|
||||
if self.board.check_cell(0,i)==None:
|
||||
count+=1
|
||||
self.assertGreaterEqual(count,6)
|
||||
self.assertListEqual(self.game.get_score(),[0,0])
|
||||
Reference in New Issue
Block a user