Skip to content

Lab Exercise 4 (Connect-Tac-Toe)

Overview

For this exercise, you are to create an MVC-based implementation of Connect-Tac-Toe in Python as a terminal game.

This activity is intended to be done with the randomized partner assigned to you during your lab class.

This lab has checkpoint tasks; these tasks must be done during your lab session for your attendance to be counted.

Connect-Tac-Toe

Connect-Tac-Toe is a two-player game played on a grid with six rows and seven columns that incorporates rules of both Connect Four and Tic-Tac-Toe.

Each player takes turns placing a token they own on one of the unoccupied cells of the grid. The rest of the rules are defined by which ruleset of the game is used.

A ruleset consists of two parts: the win condition and the token physics.

Win condition

There are two variants of the win condition: Tic-Tac-Toe and Not-Connect-Four.

For the Tic-Tac-Toe variant, the game ends when one of the players is able to have at least one row or column fully occupied with only their own tokens. The player able to do this is declared the winner.

For the Not-Connect-Four variant (as this is not the original Connect Four win condition), the game ends the moment one of the players has a group of tokens (or at least four) where each token shares an edge (i.e., they are next to each other in a cardinal direction; exclude diagonals) with at least one more token in the said group.

Clarification on when the win condition should be checked

The win condition must be checked only after the token physics mechanic has been applied.

Token physics

There are three variants of the token physics: Floating, Strong Gravity, and Weak Gravity.

For the Floating variant, tokens placed on the grid will permanently stay on the cell chosen.

For the Strong Gravity variant, tokens placed on the grid will "fall" towards the lowest possible row before the next player's turn (i.e., it will stay on the same column chosen, but will move to the row with the lowest unoccupied cell).

For the Weak Gravity variant, all tokens on the grid will simultaneously move to the cell directly under it before the next player's turn if the cell is unoccupied. As the tokens move simultaneously, a token will be able to move downward even if another token is currently under it if that token is simultaneously also able to move downward.

Common types

You must have a file in common_types.py containing exactly the following:

from enum import Enum, auto

class Player(Enum):
    P1 = auto()
    P2 = auto()

class WinConditionType(Enum):
    NOT_CONNECT_FOUR = auto()
    TIC_TAC_TOE = auto()

class TokenPhysicsType(Enum):
    FLOATING = auto()
    STRONG_GRAVITY = auto()
    WEAK_GRAVITY = auto()

If you need to define more types that are accessible across the different MVC parts, create a new file instead of editing common_types.py.

Model

The model component of your program should be in model.py which should contain at least a class called ConnectTacToeModel with at least the following methods:

  • def __init__(self, ...) initializer

    • You are free to define all parameters of the ConnectTacToeModel initializer
  • @property
    def current_player(self) -> Player:

    • Must return the current player
  • @property
    def winner(self) -> Player | None:

    • Must return winning player, or None if no winner yet or if both players win at the same time
  • @property
    def is_game_done(self) -> bool:

    • Must return whether the game is done
    • Used in case of draws as winner alone cannot disambiguate ties and still-running games
    • The game is considered done when there is a winner, the grid is fully occupied, or both players win at the same time
  • def choose_cell(self, row: int, col: int) -> bool:

    • If the game is ongoing and the cell referred to by row and col (zero_indexed) is unoccupied, then place a token there owned by the current player, end the current player's turn, and return True

    • Return False otherwise

  • @property
    def row_count(self) -> int:

    • Must return 6 (hardcoding this is acceptable)
  • @property
    def col_count(self) -> int:

    • Must return 7 (hardcoding this is acceptable)
  • def get_owner(self, row: int, col: int) -> Player | None:

    • Must return the player owning the token at the cell referred to, or None if the cell is unoccupied

Additional methods

While you are free to define additional methods for your controller to use, only the above methods will be called directly during checking.

Open-Closed Principle

Also, kindly ensure that your model satisfies the Open-Closed Principle (OCP).

Tester

  • def make(win_condition_type: WinConditionType, token_physics_type: TokenPhysicsType) -> ConnectTacToeModel:

    • Must return a ConnectTacToeModel with a ruleset corresponding to the given WinConditionType and TokenPhysicsType values

    • We will use this to automate the checking of your work; you do not need to use WinConditionType and TokenPhysicsType elsewhere in your code

Runner

Which win condition variants and token physics to be used will be given via command-line flags with possible values as follows:

  • w: notconnectfour and tictactoe
  • p: floating, strong, and weak

You are expected to use the ArgumentParser class of the argparse module with documentation as follows in your main.py: https://docs.python.org/3/library/argparse.html

As an example, this invocation initializes the game with the Tic-Tac-Toe win condition and Weak Gravity token physics:

python3 main.py -p weak -w tictactoe

Note that flags may appear in any order. You may expect for each flag to appear exactly once in the commands used during checking.

View

The view should display the game on the terminal. All view-related code must be in view.py.

The following must be satisfied:

  • The grid must be displayed as 6 rows by 7 columns. You may choose the characters used to represent empty cells and the tokens for each player. As a default, empty cells are represented as ., and the tokens represented as A and B, for Player 1 and Player 2, respectively.

  • The current player must be clearly displayed.

  • For each player and for each turn that the game is still ongoing, two integers (1-indexed) will be prompted as input separately, indicating the row and column of the cell where a token will be placed.

    • If the given cell coordinates are out-of-bounds, the game state must not be changed, and input will be prompted again.

    • If the given cell coordinates corresponed to an occupied cell, the game state must not be changed, and input will be prompted again.

  • As soon as there is a winner:

    • The game should clearly display the winner. The current player text should be replaced with text saying which player is the winner.
    • Input prompts should stop.
    • The game state must not be altered by any further actions.
    • If both players win at the same time:

      • A message saying so should replace the current player text
  • If there are no more unoccupied cells on the grid and there is no winner:

    • A message saying so should replace the current player text

Unit tests

The model part of the code must have 100% code coverage.

Lab report [20 πŸ”΄]

Place your answers to the following questions in lab04.pdf (placing it in the root of your repository):

  1. [8 πŸ”΄] Provide a class diagram containing all the classes in your submission using either UML (quick guide) or informal UML-like notation with arrows (ensuring that interfaces are distinguishable from concrete classes). Additionally, provide a visual indicator of which classes belong to the model, view, and controller. Include all public fields and methods.

  2. [12 πŸ”΄] Show how your code follows the Open-Closed Principle for the following scenarios by providing working code snippets that implement them and showing how ConnectTacToeModel should be instantiated to accomodate them (do not add them to your .py files; no need to maximize code reuse; no need to change WinConditionType, TokenPhysicsType, and make):

    a. [6 πŸ”΄] A new Connect Four win condition variant is introduced in which the first player who has four or more tokens in a contiguous configuration horizontally, vertically, or diagonally wins

    b. [6 πŸ”΄] A new Two Sides token physics variant is introduced that has tokens move towards the topmost row if placed in the first three rows, or the bottommost row if placed in the last three rows (i.e., same effect as Strong Gravity for the last three rows)

Rubric for code [80 πŸ”΄]

  • 15 πŸ”΄: 100% code coverage for model part

    • Points given will be scaled using actual coverage value
  • 15 πŸ”΄: The code follows the MVC architecture described in class

    • 6 πŸ”΄ for proper model
    • 6 πŸ”΄ for proper view
    • 3 πŸ”΄ for proper controller
  • 15 πŸ”΄: All token physics variants work (model part)

    • 4 πŸ”΄ for Floating
    • 4 πŸ”΄ for Strong Gravity
    • 7 πŸ”΄ for Weak Gravity
  • 15 πŸ”΄: All win condition variants work (model part)

    • 5 πŸ”΄ for Tic-Tac-Toe
    • 10 πŸ”΄ for Not-Connect-Four
  • 20 πŸ”΄: Other game aspects are correct

    • Non-exhaustive list: Turn order, player tokens, grid mapping, postgame interaction, query parameter support

Submission

Checkpoint

6-7 Tic-Tac-Toe (20 ❀️)

Before the lab session ends, you should be able to implement a working Tic-Tac-Toe game on a 6 x 7 playing grid, implemented using MVC, using the Model interface described above.

For this 6-7 Tic-Tac-Toe checkpoint task, the game ends when one of the players is able to have at least one row or column of three (3) consecutive tokens. The player able to do this first is declared the winner.

The tokens will follow the Floating physics mechanic (just like normal Tic-Tac-Toe).

The use of argparse is not required for this checkpoint task.

Call the attention of your lab instructor once you are done with the checkpoint task. Once your 6-7 Tic-Tac-Toe game has been verified to work five (5) minutes before the lab session ends, you will gain 20 ❀️ points, and your attendance will be recorded (and you may leave early).

If you are not able to finish the checkpoint task within the lab time, your attendance will be recorded at the very end of the lab session.

Checkpoint submission: See Google Classroom

Final submission

  • Deadline: March 6 (F), 11:12 PM