Skip to content

Commit

Permalink
Add BetterEngine
Browse files Browse the repository at this point in the history
- tries to play in the centre if possible
- otherwises behaves like Engine
  • Loading branch information
lkeegan committed Jul 16, 2024
1 parent 806ec04 commit 91fb3b1
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 10 deletions.
10 changes: 10 additions & 0 deletions src/effective_software_testing/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,13 @@ def make_move(self) -> bool:
if self._board.make_move(row, col, self._player):
return True
return False


class BetterEngine(Engine):
"""A slightly better tic-tac-toe engine that plays in the centre if possible"""

def make_move(self) -> bool:
"""Play in the centre if possible, otherwise make a random valid move and return True."""
if self._board.make_move(self._board.n // 2, self._board.n // 2, self._player):
return True
return super().make_move()
4 changes: 2 additions & 2 deletions tests/test_board_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ def test_empty_board_mouse_click_on_square(qtbot: QtBot) -> None:
# for each square, click in center & wait for square_clicked signal
for row, col in np.ndindex(n, n):
point = _row_col_to_qpoint(row, col, n, widget_size)
with qtbot.wait_signal(board_widget.square_clicked) as blocker:
with qtbot.wait_signal(board_widget.square_clicked) as square_clicked_signal:
qtbot.mouseClick(board_widget, Qt.MouseButton.LeftButton, pos=point)
assert blocker.args == [row, col]
assert square_clicked_signal.args == [row, col]
27 changes: 19 additions & 8 deletions tests/test_engine.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from __future__ import annotations
from effective_software_testing.player import Player
from effective_software_testing.board import Board
from effective_software_testing.engine import Engine
from effective_software_testing.engine import Engine, BetterEngine
from typing import Optional
import pytest
import numpy as np

players = [player for player in Player]
engine_types = [Engine, BetterEngine]


def _count_squares(board: Board, player: Optional[Player]) -> int:
Expand All @@ -16,11 +17,12 @@ def _count_squares(board: Board, player: Optional[Player]) -> int:
)


@pytest.mark.parametrize("engine_type", engine_types)
@pytest.mark.parametrize("player", players)
def test_1x1_board_engine_plays_first(player: Player) -> None:
def test_1x1_board_engine_plays_first(player: Player, engine_type: type) -> None:
# empty 1x1 board
board = Board(1)
engine = Engine(player, board)
engine = engine_type(player, board)
assert board.square(0, 0) is None
assert board.game_is_over is False
assert board.game_winner is None
Expand All @@ -33,12 +35,13 @@ def test_1x1_board_engine_plays_first(player: Player) -> None:
assert engine.make_move() is False


@pytest.mark.parametrize("engine_type", engine_types)
@pytest.mark.parametrize("n", [2, 3, 4, 5])
def test_engine_plays_second_makes_valid_move(n: int) -> None:
def test_engine_plays_second_makes_valid_move(n: int, engine_type: type) -> None:
for row, col in np.ndindex(n, n):
# empty board
board = Board(n)
engine = Engine(Player.CROSS, board)
engine = engine_type(Player.CROSS, board)
assert _count_squares(board, Player.CROSS) == 0
assert _count_squares(board, Player.CIRCLE) == 0
# circle makes a valid initial move at (row,col)
Expand All @@ -53,15 +56,16 @@ def test_engine_plays_second_makes_valid_move(n: int) -> None:
assert engine.make_move() is False


@pytest.mark.parametrize("engine_type", engine_types)
@pytest.mark.parametrize("n", [1, 2, 3, 4, 5])
@pytest.mark.parametrize("rng_seed_cross", range(5))
@pytest.mark.parametrize("rng_seed_circle", range(5))
def test_two_engines_finish_game(
n: int, rng_seed_cross: int, rng_seed_circle: int
n: int, rng_seed_cross: int, rng_seed_circle: int, engine_type: type
) -> None:
board = Board(n)
engine_cross = Engine(Player.CROSS, board, rng_seed_cross)
engine_circle = Engine(Player.CIRCLE, board, rng_seed_circle)
engine_cross = engine_type(Player.CROSS, board, rng_seed_cross)
engine_circle = engine_type(Player.CIRCLE, board, rng_seed_circle)
assert _count_squares(board, Player.CROSS) == 0
assert _count_squares(board, Player.CIRCLE) == 0
num_cross = 0
Expand All @@ -81,3 +85,10 @@ def test_two_engines_finish_game(
assert _count_squares(board, None) >= 0
assert engine_cross.make_move() is False
assert engine_circle.make_move() is False


def test_better_engine_first_move_in_centre() -> None:
board = Board(3)
engine = BetterEngine(Player.CROSS, board)
assert engine.make_move() is True
assert board.square(1, 1) == Player.CROSS

0 comments on commit 91fb3b1

Please sign in to comment.