0% found this document useful (0 votes)
14 views64 pages

Example Sample Logbook 3

coursework

Uploaded by

adilhussain606
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views64 pages

Example Sample Logbook 3

coursework

Uploaded by

adilhussain606
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 64

0

Birmingham City University

Command Line Interface


(CLI) Tic-Tac-Toe Program

CMP5361 Computer Mathematics and Declarative Programming

Alex Bugby
1

Contents
Introduction ............................................................................................................................................ 3
Initial Setup ............................................................................................................................................. 3
Rules of Tic-Tac-Toe ................................................................................................................................ 3
Requirements Analysis ............................................................................................................................ 3
Behaviour Driven Development (Gherkin Specifications) ...................................................................... 5
Data Models .......................................................................................................................................... 11
Token Model ..................................................................................................................................... 11
Grid Model ........................................................................................................................................ 12
Turn Model........................................................................................................................................ 13
Input Model ...................................................................................................................................... 13
Player Model ..................................................................................................................................... 14
Axiomatic Definitions and Functions .................................................................................................... 15
Player Name ...................................................................................................................................... 15
Statistics ............................................................................................................................................ 15
Token................................................................................................................................................. 16
Grid.................................................................................................................................................... 16
Parsing inputs.................................................................................................................................... 16
Initialisation of the grid ..................................................................................................................... 17
Argument conversion and Data Transformation Graphs.................................................................. 17
Evaluating a Winner .......................................................................................................................... 19
T2 Implementation ............................................................................................................................... 20
Creating the Token Type ................................................................................................................... 20
The Grid............................................................................................................................................. 21
Defining Cell .................................................................................................................................. 21
Creating the Coord Type ............................................................................................................... 21
Defining Grid ................................................................................................................................. 21
Initialising the Grid ........................................................................................................................ 22
Rendering the Grid ........................................................................................................................ 22
Player Choosing a Grid Location ....................................................................................................... 23
Player Class ....................................................................................................................................... 26
Token Input ....................................................................................................................................... 27
Name Input ....................................................................................................................................... 28
A Note on Statistics ........................................................................................................................... 28
Menu Choice ..................................................................................................................................... 28
Game Loop ........................................................................................................................................ 30

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
2

Checking the Win State ..................................................................................................................... 31


Checking the Draw State ................................................................................................................... 33
Testing ................................................................................................................................................... 34
Manual Testing.................................................................................................................................. 34
Automated Testing ........................................................................................................................... 49
Unit Tests ...................................................................................................................................... 49
Behaviour Driven Development (BDD) Tests ................................................................................ 50
Correctness of Unit Tests .................................................................................................................. 52
T4 A Discussion of Version Control Systems (Git) ................................................................................. 53
Case Study (CppKoans) ..................................................................................................................... 54
APPENDIX A ........................................................................................................................................... 57
Full Test Case Code ........................................................................................................................... 57
References ............................................................................................................................................ 63

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
3

Introduction
This logbook covers the design and software implementation of a Tic-Tac-Toe game using declarative
programming, interacted with by the user using a command-line interface (CLI). Within this
document, design paradigms will be shown thorough the use of gherkin specifications, set theory,
type definitions, and axiomatic definitions. Along with graph theory explanations, and justification of
behaviours. The system will be created in C++ following the specifications previously set. Manual and
automated testing will be undertaken to ensure the requirements laid out are met.

Initial Setup
The development environment consists of a Linux desktop Ubuntu installation, using Visual Studio
Code as the Integrated Development Environment. Where C++ will be the language used, using the
GNU Compiler Collection (GCC) to convert the program to an executable format. Alternative choices
of Python or F# could be used as the code base. However, due to having experience in Python
previously, I chose to work with C++ to broaden my understanding of other languages further.

The software projects aims to be a simple Tic-Tac-Toe game using a Command Line Interface (CLI) to
allow up to two players play the game simultaneously, whether that be two human players or one
human player and a computer opponent.

Rules of Tic-Tac-Toe
• The rules of Tic-Tac-Toe are as follows:
• The game is played on a 3x3 grid totalling 9 squares.
• One player chooses X or O as their token, where the other player receives the remaining
option. For example: If player one chooses X, then player two will be O.
• Players take it in turns to place their token in a square within the grid.
• A player cannot put a token in a square already occupied by a token.
• The first player to get 3 of their tokens in a row vertically, horizontally, or diagonally is
declared the winner.
• If no winner is decided before the 9 squares are filled with a token, then the game is a draw.

Requirements Analysis
The system needs to fulfil the following criteria:

• Allow for a user to interact with it using command line arguments.


• Allow a player to start a new 2-player game.
• Allow for each player to take turns placing tokens.
• Allow the game to judge when a given player (either X or O) has won the game.
• Allow a player to start a new single-player game against the CPU.

Additionally, the system will fulfil the following criteria:

• Allow a user to be assigned a name.


• Allow a user to have a record of wins, draws, and losses.

From the above requirements, user input through a command line will be a necessary component.
To facilitate this, the user will be inputting string values into the command line to be parsed by the

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
4

program, wherein they will be converted to the appropriate types before being passed to their
relevant functions.

This however presents an issue, due to the program performing input and output (I/O) operations
the functions will be fundamentally impure in several cases due to not being able to have complete
precision over the input and output tuples behaviour. However, they are still able to be totalised in
most cases as it is possible to define when and where a function will return a value.

Boolean checks will need to be undertaken to evaluate that the win criteria has been met, it is
possible to assign this as a state in the program. However, Boolean values are very easy to handle
and will be the approach taken.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
5

Behaviour Driven Development (Gherkin Specifications)


Methods of user interaction can be initially explained using Gherkin Specifications. Following the
requirements analysis, the interactable components of the system are as follows:

• Allow a player to choose a name


• Allow a player to start a new game
• Allow a player to choose a token
• Allow a player to place their token within the playing grid

FEATURE: ENTERING THE NAME OF A USER


AS A CLI I want to enter my name
USER/PLAYER So that there is a record of me playing the game
SCENARIO User Successfully enters their name
Given the player has selected the main menu option to enter their name
And the player has specified a name in string format
And the name does not already exist in the system
When the player confirms their name
Then the player is assigned that name
And the selected name is printed to stdout alongside the message string “You are playing as
<name>”
And the main menu is displayed again
SCENARIO User enters a name that already exists
Given the player has started the program
And the player has selected the main menu option to enter their name
And the player has specified a name in string format
And the name already exist in the system
When the player confirms their name
Then the player is not assigned that name
And the selected name is printed to stdout, with the following message string “Name already
exists, please enter a different name”
And the prompt to enter another name is displayed again
Table 1: Gherkin Specification for Selecting the Name of a User

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
6

FEATURE: SELECTING THE GAME TYPE TO BE PLAYED


AS A CLI I want to select the game type
USER/PLAYER So that I can begin playing the specified game type
SCENARIO User chooses to start a new 2-player game
Given the human player has started the program
And the human player has selected the main menu option to start a game
And the human player has specified the game type they would like to play as a valid string “2”
from the game-type menu
When the human player confirms their choice
Then the game begins and prompts the player to select their name via stdout
SCENARIO User chooses to start a single-player game against a CPU
Given the player has started the program
And the player has selected the main menu option to start a game
And the player has specified the game type they would like to play as a valid string “1” from the
game-type menu
When the player confirms their choice
Then the game begins and prompts the player to select their token via stdout
SCENARIO User attempts to start an invalid game type
Given the player has started the program
And the player has selected the main menu option to start a game
And the player has specified the game type they would like to play as an invalid string “5” from
the game-type menu
When the player confirms their choice
Then an error message string is printed to stdout informing the player “that is an invalid choice,
please try again”
And the player is prompted to choose a game type again
Table 2: Gherkin Specification for Selecting the Game Type to be Played

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
7

FEATURE: SELECTING THE TOKEN USED BY A PLAYER


AS A CLI I want to select my token
USER/PLAYER So that that I have a token to play the game with
SCENARIO User selects a valid token
Given the player has started the program
And the player has selected the main menu option to start a game
And the player has specified the game type they would like to play as a valid string from the
game-type menu
When the player confirms their choice
Then the game begins and prompts the player to select their token via stdout with the message
“Please select from the following Tokens: X O”
When the player has specified the Token they would like as a valid string of “X/x” or “O/o”
Then the player is assigned that token, and the remaining player is assigned the remaining Token
And the system informs the player of their choice via stdout with the message “You have chosen
<Token>”
And the game begins
SCENARIO User attempts to specify an invalid token
Given the player has started the program
And the player has selected the main menu option to start a game
And the player has specified the game type they would like to play as a valid string from the
game-type menu
When the player confirms their choice
Then the game begins and prompts the player to select their Token via stdout with the message
“Please select from the following Tokens: X O”
When the player has specified the Token they would like as an invalid string of “y”
Then the player is not assigned that Token
Then an error message string is printed to stdout informing the player “that is an invalid choice,
please try again”
And the player is prompted to choose a Token again
Table 3: Gherkin Specification for Selecting a Token

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
8

FEATURE: ADD A TOKEN TO A GRID SQUARE


AS A CLI I want to add a Token to a grid square
USER/PLAYER So that that square has my Token in it
SCENARIO Adding a Token to an empty grid square
Given it is the current human player’s turn
And the current player has previously setup a valid profile
And the current grid state is:

And the current player has specified a valid coordinate <coord>


When the current player confirms their coordinate choice via the return / enter key
Then the player’s Token is placed in that grid coordinate
And the updated grid is printed to stdout
SCENARIO Attempting to add a Token to an occupied grid square
Given it is the player’s turn
When the player specifies a grid square number
And the grid square number is occupied
Then the player’s Token is not placed in that grid square
And an error message string is printed to stdout informing the player “ERROR: that grid square is
already occupied”
And the updated grid is printed to stdout
And the player is prompted to choose a grid square number again
SCENARIO Attempting to add a Token to an invalid grid square value
Given it is the player’s turn
When the player specifies a grid square as a value that is <1 or >9, or is a value that cannot be
converted to a whole number within the formerly defined range of 1-9
Then the player’s Token is not placed in that grid square
And an error message string is printed to stdout informing the player “that is an invalid choice,
please try again”
And the updated grid is printed to stdout
And the player is prompted to choose a grid square number again
Table 4: Gherkin Specification for Adding a Token to a Grid Square

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
9

FEATURE: A PLAYER WINS THE GAME


AS A CLI I want to add a Token to a grid square
USER/PLAYER So that that square has my Token and I win the game
SCENARIO Adding a Token to an empty grid square, causing the game to end as a win/loss
Given it is the current human player’s turn
And the current player has previously setup a valid profile
And the current grid state is:

X O O

X O

And the current player has specified a valid coordinate <coord> of 4


When the current player confirms their coordinate choice via the return / enter key
Then the player’s Token is placed in that grid coordinate
And the updated grid is printed to stdout
And “<current player> is the winner, <other player> has lost” is printed to stdout
Table 5: Gherkin Specification for Winning a Game

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
10

FEATURE: THE GAME IS A DRAW


AS A CLI I want to add a Token to a grid square
USER/PLAYER So that that square has my Token and all grid squares are now full, resulting in a
draw
SCENARIO Adding a Token to an empty grid square, causing the game to end in a draw
Given it is the current human player’s turn
And the current player has previously setup a valid profile
And the current grid state is:

X O X

X X O

O X

And the current player has specified a valid coordinate <coord> of 9


When the current player confirms their coordinate choice via the return / enter key
Then the player’s Token is placed in that grid coordinate
And the updated grid is printed to stdout
And “the game is a draw!” is printed to stdout
Table 6: Gherkin Specification for Drawing a Game

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
11

Data Models
• Input
o User Input (Standard Input)
• Output Message
o Menus
o Selected Name
o Game Grid Updates
o Win
o Loss
o Draw
o Statistics of a player
• Error
o Error Values
▪ Invalid argument
o Error Message
o Exit Code
• Exit Code

Token Model
The modelling of the game will begin with the model of the playing pieces, referred to as ‘Tokens’.
The Tokens used to play with are selected by the player when starting a valid game type. The Token
type contains two values of X and O, and a player is only allowed to choose values from this
definition. By defining Token as being a set of these two values, it becomes easier to check validity of
inputs, as the valid values are effectively constrained. As an example, say that Token was defined as
the character values of ‘X’ and ‘O’; in this case Token is now effectively a subset of all possible
Characters, which could cause issues in cases where the input being evaluated is not part of the
subset. A user inputting the value ‘U’ would have to be checked against for example.

With the above in mind, an expression of a Token could be defined as being a set of X and O:

𝑙𝑒𝑡 𝑇𝑜𝑘𝑒𝑛 = {X,O}


Broken down further, it can be seen that Token has a cardinality of two, and is a Union of an X and
and an O value.

#𝑇𝑜𝑘𝑒𝑛 = 2

{𝑋} ∪ {𝑂} = {𝑋, 𝑂} = 𝑇𝑜𝑘𝑒𝑛

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
12

Grid Model
The grid for the tic-tac-toe game as defined is a 3x3 square, diagrammatically presented below when
printed to standard output:

Figure 1: A Representation of the Playing Grid as printed to standard output to the user

The grid may also be presented with numeric values instead of characters. It is possible to make the
grid larger, or smaller, as requirements define. So, a malleable approach should be taken to allow for
re-use of code once implemented. For this purpose, each of the sub squares of the grid will be
denoted as cells. Where each cell is comprised of a Token or nothing (indicating an empty cell), to
represent this an option type can be used, the reason a None value is required is for the initial state
of the board, before any Tokens are placed. By having the values in each grid square location being
initially set to none, it is easy to check whether these values have changed later.

𝑙𝑒𝑡 𝐶𝑒𝑙𝑙 = 𝑂𝑝𝑡𝑖𝑜𝑛 < 𝑡𝑜𝑘𝑒𝑛 >


Broken down further, the optional Token contains the values of Token, and an additional value of
None, where Token and None are united to form a set.

𝑇𝑜𝑘𝑒𝑛 ∪ { 𝑁𝑜𝑛𝑒 } = {𝑋, 𝑂} ∪ {𝑁𝑜𝑛𝑒}


{𝑋, 𝑂} ∪ {𝑁𝑜𝑛𝑒} = {𝑋, 𝑂, 𝑁𝑜𝑛𝑒}
{𝑋, 𝑂, 𝑁𝑜𝑛𝑒} = 𝑂𝑝𝑡𝑖𝑜𝑛 < 𝑇𝑜𝑘𝑒𝑛 >

To correctly map these cells to a grid, a coordinate value will be used. A coordinate will be a set of
values from A – I. Paired to a cell with a dictionary data structure as key value pairs representing a
tile, where the coordinate is the key, and the cell the value. This is an effective way of allowing for
comparison of values. Note that the map is an ordered map, not unordered. This is due to values
needing to be presented to the user at some point in a specific order in a grid to make the interface
readable.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
13

𝑙𝑒𝑡 𝐶𝑜𝑜𝑟𝑑 = {𝐴, 𝐵, 𝐶, 𝐷, 𝐸, 𝐹, 𝐺, 𝐻, 𝐼}


𝑙𝑒𝑡 𝐺𝑟𝑖𝑑 = 𝐶𝑜𝑜𝑟𝑑 → 𝐶𝑒𝑙𝑙
𝑙𝑒𝑡 𝑔 = {(𝐴, 𝑁𝑜𝑛𝑒)}
𝑙𝑒𝑡 𝐺𝑟𝑖𝑑 = 𝑑𝑖𝑐𝑡 < 𝐶𝑜𝑜𝑟𝑑, 𝐶𝑒𝑙𝑙 >

Turn Model
Since a game will involve two players, who take turns in order, it is viable to represent the players as
a sequence. Where the player at the head of the sequence is considered the active player. This will
also allow for modifications in the future, for example, perhaps a game would require 3 or 4 players.
Using a sequence allows for the number of players to be changed. The player sequence is
represented by the following:

𝑙𝑒𝑡 𝑃𝑙𝑎𝑦𝑒𝑟𝑠 = 𝑠𝑒𝑞 < 𝑃𝑙𝑎𝑦𝑒𝑟 >

The player type is defined in the Player Model section.

Input Model
Players needs to be able to provide input to the system to select from presented options. An
example of an input would be the player passing in a string of X or O to select the Token they would
like to play as. However, any inputs will be component parts of pre-defined functions, and therefore
do not need a custom type defined for them. Any conversion can be done after inputs are validated.
For the sake of preciseness this can be defined as being a sequence of characters:

𝑙𝑒𝑡 𝐼𝑛𝑝𝑢𝑡 = 𝑠𝑒𝑞 𝐶ℎ𝑎𝑟

For the menu options, it is possible to define the options as values of a custom type that the user can
select from:

𝑙𝑒𝑡 𝑀𝑒𝑛𝑢𝐶ℎ𝑜𝑖𝑐𝑒 = {𝑆𝑖𝑛𝑔𝑙𝑒𝑝𝑙𝑎𝑦𝑒𝑟, 𝑀𝑢𝑙𝑡𝑖𝑝𝑙𝑎𝑦𝑒𝑟, 𝑉𝑖𝑒𝑤𝑆𝑐𝑜𝑟𝑒, 𝐸𝑥𝑖𝑡}


Each of these values can be evaluated and allow for the program to branch into different run paths.
For example, the Singleplayer game works slightly differently to a Multiplayer game due to the CPU
component in the Singleplayer version not requiring human user inputs.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
14

Player Model
A player is comprised of a name, and a set of statistics, the name component of a player will be a
sequence of Characters, further defined in the Axiomatic Definition of Player Name.

𝑙𝑒𝑡 𝑁𝑎𝑚𝑒 = 𝑠𝑒𝑞 𝐶ℎ𝑎𝑟


𝑁𝑎𝑚𝑒 ∶ 𝑃 𝑠𝑒𝑞 < 𝐶ℎ𝑎𝑟 >

The statistics component contains a record of all wins, losses, and draws of the player. The wins,
losses, and draws will be stored as natural numbered integers, each up to a score of 1000 as
negative values are not required. This can be represented as a 32-bit unsigned integer. The equation
for which is stated below:

𝑙𝑒𝑡 𝑈𝑖𝑛𝑡 = {𝑥: ℕ | 𝑥 ≤ (232 − 1)}

For each of the win, loss, and draw types a constraint needs to be placed on the 32-bit unsigned
integer so that the values are only valid up to the integer value of 1000. Represented below:

𝑙𝑒𝑡 𝑊𝑖𝑛𝑠 = {𝑥: 𝑈𝑖𝑛𝑡 | 𝑥 ≤ 1000}


𝑙𝑒𝑡 𝐿𝑜𝑠𝑠𝑒𝑠 = {𝑥: 𝑈𝑖𝑛𝑡 | 𝑥 ≤ 1000}
𝑙𝑒𝑡 𝐷𝑟𝑎𝑤𝑠 = {𝑥: 𝑈𝑖𝑛𝑡 | 𝑥 ≤ 1000}

The Players game statistics therefore can be represented as a cartesian product of one of each wins,
losses and draws. For any one player, a player will have one triple of statistics. Represented below:

𝑙𝑒𝑡 𝑆𝑡𝑎𝑡𝑖𝑠𝑡𝑖𝑐𝑠 = 𝑊𝑖𝑛𝑠 × 𝐿𝑜𝑠𝑠𝑒𝑠 × 𝐷𝑟𝑎𝑤𝑠

With the above declarations, a player object can be defined as being a cartesian product of a Token,
a name, and a single set of statistics.

𝑙𝑒𝑡 𝑃𝑙𝑎𝑦𝑒𝑟 = 𝑇𝑜𝑘𝑒𝑛 × 𝑁𝑎𝑚𝑒 × 𝑆𝑡𝑎𝑡𝑖𝑠𝑡𝑖𝑐𝑠

Whereas every player in the system can be stored in a sequence to be referenced later.

𝑙𝑒𝑡 𝑃𝑙𝑎𝑦𝑒𝑟𝐷𝐵 = 𝑁𝑎𝑚𝑒 ⇸ 𝑝𝑙𝑎𝑦𝑒𝑟

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
15

Axiomatic Definitions and Functions

Player Name
From the criteria set in the requirements analysis, the system needs to allow for a player to set their
name, this will be done through a function called “playerName” which takes no parameters and will
prompt the player to enter a name which is a sequence of characters and will then return this
sequence as name. The implementation of this function will be partial, as there cannot be a mapped
matching pair to every possible input due to the variation of names that any user can have.

𝑔𝑒𝑡𝑃𝑙𝑎𝑦𝑒𝑟𝑁𝑎𝑚𝑒𝐼𝑛𝑝𝑢𝑡 ∶ 𝑣𝑜𝑖𝑑 → 𝑛𝑎𝑚𝑒

The name of a player also needs to be checked, as each name must be unique to be used as an
identifier for that player. Therefore, a player cannot enter and use a name that is already in the
system. The check is a pure function that returns a Boolean value based on whether the name exists
in the database (true) or if it does not (false).

𝑐ℎ𝑒𝑐𝑘𝑃𝑙𝑎𝑦𝑒𝑟𝑁𝑎𝑚𝑒 = 𝑛𝑎𝑚𝑒 → 𝑏𝑜𝑜𝑙


𝑙𝑒𝑡 𝑐ℎ𝑒𝑐𝑘𝑃𝑙𝑎𝑦𝑒𝑟𝑁𝑎𝑚𝑒 = 𝜆𝑥 ⋅ 𝑥 ∈ 𝑃𝑙𝑎𝑦𝑒𝑟𝐷𝐵

Statistics
Furthermore, each player has a set of statistics associated to them based on their play history.
Initially these values will be set to 0, however each win, loss, or draw by the specified player will
increment the value of the relevant parameter by 1. The statistics will be a collection of all these
types together. The incrementing functions will be named “addWin”, “addLoss”, and “addDraw”
respectively.
𝑎𝑑𝑑𝑊𝑖𝑛 = 𝑈𝑖𝑛𝑡 → 𝑈𝑖𝑛𝑡
𝑙𝑒𝑡 𝑎𝑑𝑑𝑊𝑖𝑛 = 𝜆𝑥 . 𝑥 + 1
𝑎𝑑𝑑𝐿𝑜𝑠𝑠 = 𝑈𝑖𝑛𝑡 → 𝑈𝑖𝑛𝑡
𝑙𝑒𝑡 𝑎𝑑𝑑𝐿𝑜𝑠𝑠 = 𝜆𝑥 | 𝑥 ≥ 0 . 𝑥 + 1
𝑎𝑑𝑑𝐷𝑟𝑎𝑤 = 𝑈𝑖𝑛𝑡 → 𝑈𝑖𝑛𝑡
𝑙𝑒𝑡 𝑎𝑑𝑑𝐷𝑟𝑎𝑤 = 𝜆𝑥 | 𝑥 ≥ 0 . 𝑥 + 1

It is also possible to get and set these values, below is an axiomatic definition for the win value.

𝑔𝑒𝑡𝑊𝑖𝑛𝑠 ∶ 𝑆𝑡𝑎𝑡𝑖𝑠𝑡𝑖𝑐𝑠 → 𝑊𝑖𝑛𝑠


𝑔𝑒𝑡𝑊𝑖𝑛𝑠 (𝑥, 𝑦, 𝑧) = 𝑥
𝑠𝑒𝑡𝑊𝑖𝑛𝑠: 𝑊𝑖𝑛𝑠 𝑋 𝑆𝑡𝑎𝑡𝑖𝑠𝑡𝑖𝑐𝑠 → 𝑆𝑡𝑎𝑡𝑖𝑠𝑡𝑖𝑐𝑠
𝑠𝑒𝑡𝑊𝑖𝑛𝑠 (𝑤𝑖𝑛, (𝑥, 𝑦, 𝑧)) = (𝑤𝑖𝑛 + 𝑥, 𝑦, 𝑧)

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
16

Token
Players are also able to select a Token, this will be accomplished via a function called
“getPlayerTokenInput” where the player Token initially is set to void and is set to either X or O after
the player chooses from available options.
𝑔𝑒𝑡𝑃𝑙𝑎𝑦𝑒𝑟𝑇𝑜𝑘𝑒𝑛𝐼𝑛𝑝𝑢𝑡: 𝑣𝑜𝑖𝑑 → 𝑇𝑜𝑘𝑒𝑛
𝑙𝑒𝑡 𝑔𝑒𝑡𝑃𝑙𝑎𝑦𝑒𝑟𝑇𝑜𝑘𝑒𝑛𝐼𝑛𝑝𝑢𝑡 = {(𝑣𝑜𝑖𝑑, 𝑋),(void, O)}
When a player has selected a Token, the available Token values available to any other proceeding
player are all the Token values that are not a Token value that has already been assigned. This is
implemented in the “assignTokenPlayer2” function.
𝑎𝑠𝑠𝑖𝑔𝑛𝑇𝑜𝑘𝑒𝑛𝑃𝑙𝑎𝑦𝑒𝑟2: 𝑇𝑜𝑘𝑒𝑛 → 𝑇𝑜𝑘𝑒𝑛
𝑙𝑒𝑡 𝑎𝑠𝑠𝑖𝑔𝑛𝑇𝑜𝑘𝑒𝑛𝑃𝑙𝑎𝑦𝑒𝑟2 = 𝜆𝑥 ⋅ ∉ 𝑔𝑒𝑡𝑃𝑙𝑎𝑦𝑒𝑟𝑇𝑜𝑘𝑒𝑛𝐼𝑛𝑝𝑢𝑡

Grid
A coordinate can be selected by the user through the argument x, provided the given argument is
convertible to one of the values that is a member of the coordinate type.

𝑝𝑙𝑎𝑦𝑒𝑟𝐺𝑟𝑖𝑑𝐶ℎ𝑜𝑖𝑐𝑒𝑉𝑎𝑙𝑢𝑒: 𝑣𝑜𝑖𝑑 → 𝑖𝑛𝑡


𝑙𝑒𝑡 𝑝𝑙𝑎𝑦𝑒𝑟𝐺𝑟𝑖𝑑𝐶ℎ𝑜𝑖𝑐𝑒𝑉𝑎𝑙𝑢𝑒 = 𝑥 ≥ 1 ∧ 𝑥 ≤ 9
When the player at the head of players selects a cell through the use of playerGridChoiceValue, then
it is possible to assign a Token to that tile.
𝑝𝑙𝑎𝑐𝑒𝑇𝑜𝑘𝑒𝑛: 𝑝𝑙𝑎𝑦𝑒𝑟𝑇𝑜𝑘𝑒𝑛 × 𝑝𝑙𝑎𝑦𝑒𝑟𝐺𝑟𝑖𝑑𝐶ℎ𝑜𝑖𝑐𝑒𝑉𝑎𝑙𝑢𝑒
𝑙𝑒𝑡 𝑝𝑙𝑎𝑐𝑒𝑇𝑜𝑘𝑒𝑛 = {𝑣𝑜𝑖𝑑, 𝑡𝑜𝑘𝑒𝑛}

However, each tile must not already have a Token inside of it. To check this, the values within the
tile dictionary can be checked. In the event that the tile value is set to a Token then the player
should not be allowed to place their Token in that tile.
𝑐ℎ𝑒𝑐𝑘𝑂𝑐𝑐𝑢𝑝𝑖𝑒𝑑𝑇𝑖𝑙𝑒: 𝑡𝑖𝑙𝑒 → 𝑏𝑜𝑜𝑙
𝑙𝑒𝑡 𝑐ℎ𝑒𝑐𝑘𝑂𝑐𝑐𝑢𝑝𝑖𝑒𝑑𝑇𝑖𝑙𝑒 = {(𝑣𝑜𝑖𝑑, 𝑓𝑎𝑙𝑠𝑒}, (𝑋, 𝑡𝑟𝑢𝑒), (𝑂, 𝑡𝑟𝑢𝑒)}

Parsing inputs
Inputs taken from a user need to be validated so that they can be converted to the appropriate
types. This can be defined as the following:
𝑝𝑎𝑟𝑠𝑒𝑀𝑒𝑛𝑢𝐼𝑛𝑝𝑢𝑡: 𝑣𝑜𝑖𝑑 → 𝑜𝑝𝑡𝑖𝑜𝑛𝑎𝑙 < 𝑀𝑒𝑛𝑢𝐶ℎ𝑜𝑖𝑐𝑒 >
𝑙𝑒𝑡 𝑝𝑎𝑟𝑠𝑒𝑀𝑒𝑛𝑢𝐼𝑛𝑝𝑢𝑡 = {(𝑣𝑜𝑖𝑑, 𝑆𝑖𝑛𝑔𝑙𝑒𝑃𝑙𝑎𝑦𝑒𝑟), (𝑣𝑜𝑖𝑑, 𝑀𝑢𝑙𝑡𝑖𝑝𝑙𝑎𝑦𝑒𝑟), (𝑣𝑜𝑖𝑑, 𝑆𝑡𝑎𝑡𝑖𝑠𝑡𝑖𝑐𝑠), (𝑣𝑜𝑖𝑑, 𝐸𝑥𝑖𝑡)}
𝑔𝑒𝑡𝑃𝑙𝑎𝑦𝑒𝑟𝑀𝑒𝑛𝑢𝐼𝑛𝑝𝑢𝑡: 𝑣𝑜𝑖𝑑 → 𝑀𝑒𝑛𝑢𝐶ℎ𝑜𝑖𝑐𝑒

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
17

Initialisation of the grid


The grid should be initialised as a map of Coord to Cells. Each cell will initially be set as a null value to
allow for modification later. This is defined below:

𝑖𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑠𝑒𝐺𝑟𝑖𝑑: 𝑣𝑜𝑖𝑑 → 𝐺𝑟𝑖𝑑


𝑙𝑒𝑡 𝑖𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑠𝑒𝐺𝑟𝑖𝑑 = 𝑑𝑖𝑐𝑡 < 𝐶𝑜𝑜𝑟𝑑, 𝐶𝑒𝑙𝑙 >

Argument conversion and Data Transformation Graphs


As arguments come in from the user as a sequence of characters, they must be converted to the
relevant types to be parsed by the system. This can be achieved through a data transformation
pipeline. For example, to convert an argument to a Token, the argument must be input and then
converted through a function to a valid Token of the Token type. Below is an example of the
definition to convert user Input to a Token.

𝑐𝑜𝑛𝑣𝑒𝑟𝑡𝑇𝑜𝑇𝑜𝑘𝑒𝑛: 𝐼𝑛𝑝𝑢𝑡 ⇸ T𝑜𝑘𝑒𝑛


𝑙𝑒𝑡 𝑐𝑜𝑛𝑣𝑒𝑟𝑡𝑇𝑜𝑇𝑜𝑘𝑒𝑛𝑁𝑜𝑑𝑒 = {𝐼𝑛𝑝𝑢𝑡, 𝑇𝑜𝑘𝑒𝑛}
𝑡𝑜𝑘𝑒𝑛𝑇𝑓𝑃𝑖𝑝𝑒𝑙𝑖𝑛𝑒: ℙ (convertToTokenNode × convertToTokenNode)
𝑡𝑜𝑘𝑒𝑛𝑇𝑓𝑃𝑖𝑝𝑒𝑙𝑖𝑛𝑒 = {(𝐼𝑛𝑝𝑢𝑡, 𝑇𝑜𝑘𝑒𝑛)}

Below is a data transformation graph of the above pipeline, using the getPlayerTokenInput()
axiomatic definition.

Figure 2: The Data Transformation Graph to Convert User Input to a Token

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
18

Using this Token type value, it is possible to pass it as a parameter to another function to
automatically assign the other possible value to the second player without requiring user input.

Figure 3: The Data Transformation Graph to get one Token Value from Another

The method for conversion from a Token to a String for standard output to present the user requires
a Token value to be provided, which is then converted to a string representation of that value by
matching it to a Token constructor.

Figure 4: The Data Transformation Graph to Convert a Token to a String

The menu input works in a similar manner, converting the input to an optional menu type, before
retrieving the value and converting to a MenuChoice.

Figure 5: The Data Transformation Graph to Convert User Input to a MenuChoice

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
19

As the grid cells are enumerated types, they can directly interpret integer values converted
from user character inputs. For example, the Coord B is equivalent to 1. So, when placing a
Token, a user enters a numerical value which is mapped to a coordinate.

Figure 6: The Data Transformation Graph to Convert User Input to a Coord

Evaluating a Winner
A winner is declared when the following rule is met:

A row, column, or diagonal all contain the same Token Value such that the total number of Token
values are equivalent to the square root of the total number of grid spaces.

In the event of a 3x3 grid where all 9 nodes are filled with a Token and the rule stated above does
not apply then the game is considered a draw.

With the above rules, there are 8 total possible combinations of nodes in a 3x3 grid that are
equivalent to a win state for a player, as follows:

{{A,B,C} , {A, E, I} , {A, D, G}, {B, E, H}, {C, E, G}, {C, F, I}, {D, E, F}, {G, H, I}}

The following undirected graph is a representation of a win state for the player assigned the Token
X.

Figure 7: A Diagrammatic Representation of a Win State

In this representation the following subset of Coord which equates to a win condition has been met:

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
20

{A,E,I}

The following undirected graph is a representation of a draw, or tie.

Figure 8: A Diagrammatic Representation of a Draw/Tie

In a draw state it can be seen that all the None values have been replaced with a Token value of
either X or O. However, none of the sets defined as being win conditions have been met, thus a draw
state has been met.

T2 Implementation
Using the previous specifications, implementation of the program can begin. When looking at
functions, there are two types that can be followed, pure or impure. Pure functions are functions
that do not modify the program state outside of their own scope. Whereas impure functions do
modify the program state. Along with this, there are totalised and non-totalised functions. Where
totalised functions have every possible input, value mapped to a valid output, where non-totalised
functions do not. With this in mind, it would be preferable to make pure, totalised functions where
possible.

Creating the Token Type


One of the initial things to create is the Token that will be used to play the game. For this, two
separate structs defining X and O were created (Figure x), these structs were then used to declare a
variant type named Token. The bool operator is used for comparison purposes when evaluating win
states and is discussed later in checking the win state.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
21

struct X {
bool operator==(const X& other) const {
return true;
}
};
struct O {
bool operator==(const O& other) const {
return true;
}
};

using Token = std::variant<X, O>;


Figure 9: Creation of the Token Struct

The decision to use structs and variants was eventually reached after first creating the Token as an
Enumerated type. Though still functionally usable; since Enumerated types map any values to an
integer value it could lead to issues with error handling. This has an upside, where it is possible to
use switch cases. However, Regex is more comprehensive than switch cases and works with structs
too.

The Grid
Defining Cell
When it comes to creating a cell, it is possible to use an optional type. An optional type allocates a
third, “empty” value, designated as nullopt in the case of C++.

using Cell = std::optional<Token>;


Figure 10: Implementation of the Cell optional type

Creating the Coord Type


With the token and cell created, a coordinate system is required to be able to map these values to a
grid. The chosen path was to create the coordinates as an enumerated type (Figure x), this had its
use as it was possible to map the coordinates as the key to a cell value (figure X), while
simultaneously being able to use their enumerated representation when printing the grid to the
player through standard output.

enum coord {
A, B, C, D, E, F, G, H, I
};
Figure 11: Creation of the coord Enumerated Type

Defining Grid
using Grid = std::map<coord, cell>;
Figure 12: A map containing a coordinate as a key, and a cell as a value

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
22

Initialising the Grid


With this structure defined, it is possible to create an initial state of the grid. This was implemented
using the function “initializeGrid”, where each coord value was mapped to the nullopt value
available within the Cell optional type.

//build the initial playing board state for the program.


Grid initializeGrid(){
Grid board {
{A, std::nullopt},{B,std::nullopt},{C,std::nullopt},
{D,std::nullopt},{E,std::nullopt},{F,std::nullopt},
{G,std::nullopt},{H,std::nullopt}, {I,std::nullopt}
};
return board;
};
Figure 13: Implementing the Initial state of the grid

It is possible to create a value as being the nullopt option from Cell, but it seemed redundant to use
a typedef for something that is only used in this section of the code.

Rendering the Grid


The grid can also be rendered to standard output for the end user to see the current state of the
board itself. This is an impure function, due to it printing to standard output.

//check the what the value is in that map (grid) coordinate and print
out the value based on that
//if there's no X or O, then print the index value
//actually really helped me understand how to access values!
void renderBoard(const std::map<coord, cell>& grid) {
int counter = 0;
for (const auto& [key, value] : grid) {
counter++;
std::cout << "|";
std::cout << /*'[' << key << "] = " <<*/ " " <<
(value.has_value()? tokenToString(value.value()) :
std::to_string(key+1)) << " ";
if (counter % 3 == 0) {
std::cout << "|";
if (counter != 9) {
std::cout << "\n-------------\n";
}
}
}
std::cout << "\n";
};
Figure 14: Implementation of the Render of the Board to Standard Output

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
23

Figure 15: An example of the Board as it appears to the User

Player Choosing a Grid Location


With the grid created, the focus moves to creating ways to interact with the grid to change these
values. A player should be able to select a location on the grid, and then be able to place their Token
in that location if that location does not already contain a Token. This is comprised of two separate
functions. The first is a function that takes an integer value and returns an integer value if it is within
a valid range. This function is impure due to throwing an error message on an invalid input.

//Takes in a playing grid value chosen by player between 1 and 9


int playerGridChoiceValue(int value) {
if (value < 1 || value > 9) {
throw std::out_of_range("That is an invalid choice, choice must
be a whole number between 1 and 9");
}
return value;
};
Figure 16: Implementation of the Function to take User Input as an Integer for Use as a Grid Coordinate

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
24

If the value entered is valid, then the grid key matching that value is checked. If the value is currently
nullopt, then the value is changed to that players Token. Otherwise the player is asked to choose
another location.

//player choosing where to place their token


void placeToken(std::map<coord, cell>& grid, Player player) {
Token tkn = player.getToken();
while (true) {
std::cout << "Enter the location (1-9) where you want to place
your token: ";
int input;
std::cin >> input;
try {
input = playerGridChoiceValue(input);
} catch (const std::out_of_range& e) {
std::cout << e.what() << '\n';
//was having issues with it not clearing the information
from input, this removes previous inputs and errors.
std::cin.clear();

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
continue;
}
coord c = static_cast<coord>(input - 1);
if (grid[c]) {
std::cout << "That location already contains a token.
Please choose a different location.\n";
} else {

grid[c] = tkn;
break;
}
}
};
Figure 17: Implementation of the placeToken Function

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
25

Within the game types, there is an option to play a single player game, which pits the player against
a CPU opponent. The CPU opponent should only be able to enter Tokens into cells that do not
already contain a Token. This check can be done by adding all the cells that have nullopt as a value to
a list, and generating a random value within this range.

void cpuPlaceToken(std::map<coord, cell>& grid, Player player) {


Token tkn = player.getToken();
coord c;
//create an array of choices that the CPU can pick from
std::vector<coord> validCPUChoices;
//checks grid for all nullopt values and adds the keys for those to
the above array.
for (const auto& [key, value] : grid) {
if (value == std::nullopt)validCPUChoices.push_back(key);
}
//random number generation within the populated array, assign this
as the cpu's choice.
if (!validCPUChoices.empty()) {
srand(time(0));
int n = rand() % validCPUChoices.size();
c = validCPUChoices[n];
grid[c] = tkn;
}
};
Figure 18: Implementation of the cpuPlaceToken Function

Previously, implementation of this was accomplished by just allowing the function to perform
random number generation between 1 and 9. However, this led to issues where the CPU would take
a long time to reach a valid value when the majority of cells on the grid were already occupied.
Causing a delay.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
26

Player Class
But to do any of this, a player must be created. The player itself is an object created from the
following class template:

class Player{
private:
Token tk;
std::string name;
Statistics statistics;

public:
Player(Token tk, std::string name, Statistics statistics ) {
this->tk = tk;
this->name = name;
this->statistics = statistics;
}

Player() = default;

public:
Token getToken() {
return tk;
}

public:
void setToken(Token t) {
tk = t;
}

public:
std::string getName() {
return name;
}

public:
void setName(std::string n) {
name = n;
}

public:
Statistics getStatistics() {
return statistics;
}

};
Figure 19: Class Definition of Player

This template specifies that a Player object is comprised of a Token, a name, and a set of statistics.
The first of these two parameters (Token and name) are gathered via standard input requests.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
27

Whereas statistics are initially created as a default set of values all equating to 0. In this case, due to
the amount of data within a player object, a class definition is an effective way to handle the data.

Token Input
For the input request for the Token, the input is taken as a string, and is converted to an optional
Token via regex pattern matching. In the event the player does not enter a valid value, the system
will repeatedly ask for input until a valid value is entered. This is totalised function that handles any
input it is given and converts it to either a Token or prints to standard error.

Token getPlayerTokenInput() {
// 1. Prompt the user for token input
// 2. read the string input
// 3. attempt to parse the string input
// 4. if successfully parsed to token
// 4a. then return the Token value
// 4b1. otherwise print to stderr an error message "ERROR: Invalid
token, cannot parse"
// 4b2. goto 1
std::string input;
std::cout << "Please select a valid token (O or X): ";
std::cin >> input;
std::optional<Token> tkn = parsePlayerToken(input);
if (tkn.has_value()){
return tkn.value();
} else {
std::cerr << "ERROR: Invalid token value, please try again." <<
std::endl;
return getPlayerTokenInput();
}
}
Figure 20: Implementation of obtaining the Player Input for a Token

The parsing component is undertaken in the “parsePlayerToken” function, a totalised, pure function
taking the parameterised string input from the user and returning the matching struct value, or
nullopt if no valid match is found.

//pattern match string and return token value if matches


std::optional<Token> parsePlayerToken(std::string s) {
const std::regex xRePattern("X|x");
const std::regex oRePattern("O|o");
if (std::regex_match(s, xRePattern)){
return X();
} else if (std::regex_match(s, oRePattern)) {
return O();
}
return std::nullopt;
}
Figure 21: Implementation of the Parse for the Player Token

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
28

However, only one of the players gets to select their Token. The other player must be given the
remaining Token as two players cannot have the same Token at any one time.

//accessing variant values!!


Token assignTokenPlayer2(Token t) {
if (std::holds_alternative<O>(t)) {
return X();
} else if (std::holds_alternative<X>(t)) {
return O();
}
};
Figure 22: Returning the Opposite Token to the Input Argument

In this totalised, pure function the argument “Token t” above is the Token assigned to player 1. This
simple check just returns the opposite struct to the input argument, where the input argument is of
the variant Token. For example, if player 1 selected X as their Token, the above function would
return O.

Name Input
Players can also have a name. Similar to the getPlayerTokenInput function, this function reads a
string given from a standard input prompt and returns that name. Below is the function
implementing this.

std::string getPlayerNameInput() {
std::string playerName;
std::cout << "Please enter your username: ";
std::cin >> playerName;
return playerName;
Figure 23: Returning a player name from a string input

A Note on Statistics
The statistics portion of the program has been discontinued, due to it not being part of the
specification, and was initially probed as a supplementary component. Learning C++ and Functional
Programming at the same time caused more issues than expected, and it was more important to get
the base program working rather than trying to add additional unnecessary features.

Menu Choice
When moving to the main of the program. The first thing the program will do is ask for the user to
choose options from a menu. For this, another enumerated type was created to cover the possible
options that the user could enter into the system.

enum MenuChoice {Singleplayer, Multiplayer, ViewScore, Exit};


Figure 24: Implementation of the MenuChoice Type

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
29

The parseMenuInput function is a totalised, pure function that uses an optional type, similar to the
way that parsePlayerToken function was implemented. In this case, the user input is matched via
regex to numerical string values. Where each value is mapped to the MenuChoice type.

std::optional<MenuChoice> parseMenuInput(std::string s) {
const std::regex oneRePattern("(1|Singleplayer|singleplayer)");
const std::regex twoRePattern("(2|Multiplayer|multiplayer)");
const std::regex threeRePattern("(3|Exit|exit)");
//const std::regex fourRePattern("4");
if (std::regex_match(s, oneRePattern)){
return Singleplayer;
} else if (std::regex_match(s, twoRePattern)) {
return Multiplayer;
} else if (std::regex_match(s, threeRePattern)) {
return Exit;
}
return std::nullopt;
}
Figure 25: Implementation of the parseMenuInput Function

The getPlayerMenuInput function returns a value of the MenuChoice type, given a valid input has
been provided by the user. An impure, yet totalised function of which the input is checked with the
“has_value” method after the input has been parsed by the previously defined “parseMenuInput”
function. If no valid input has been given, an error message is printed to standard output and the
getPlayerMenuInput function is called again.

MenuChoice getPlayerMenuInput() {
std::string input;
std::cout << "Please select a Menu Option:" << std::endl;
std::cout << "1. Singleplayer" << std::endl;
std::cout << "2. Multiplayer" << std::endl;
std::cout << "3. View Score" << std::endl;
std::cout << "4. Exit" << std::endl;
std::cin >> input;
std::optional<MenuChoice> mChoice = parseMenuInput(input);
if (mChoice.has_value()){
return mChoice.value();
} else {
std::cerr << "ERROR: Invalid menu choice, please try again." <<
std::endl;
return getPlayerMenuInput();
}
};
Figure 26: Implementation of the getPlayerMenuInput Function

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
30

Game Loop
Finally, to implement how the game is played in practice, a function referred to as “gameLoop” was
created.

void gameLoop(std::map<coord, cell> grid, Player currentPlayer) {


while (checkWinState(players[0],grid,3) == false)
{
renderBoard(grid);
if(players[0].getName() == "CPU") {
cpuPlaceToken(grid, players[0]);
} else {
placeToken(grid, players[0]);
}
bool winner = checkWinState(players[0], grid, 3);
if(winner == true) {
renderBoard(grid);
std::cout << players[0].getName() << " has won!\n";
std::cout << players[1].getName() << " has lost!";
break;
}
else if(checkDrawState(grid, 9) == true) {
renderBoard(grid);
break;
}
else {
changeCurrentPlayer(players);
currentPlayer = players[0];
}
}

}
Figure 27: The Implementation of the gameLoop Function

This function takes a previously initialised map created with the “initialiseGrid” function, as well as
the current player, which is the 0 index value of the players list. Which is initialised as a standard
vector as follows:

//list of players
std::vector<Player> players;
Figure 28: Implementation of the Player Sequence

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
31

To change the active player, the changeCurrentPlayer function is called, which takes in an argument
of a list of players and swaps the header with the next player in line.

//list of players, that can handle any number of players, each turn the
next player in the list from the previous becomes current, until the
end of the list.
void changeCurrentPlayer(std::vector<Player>& players) {
Player finalPlayer = players.back();
players.pop_back();
players.insert(players.begin(), finalPlayer);
std::cout << "Current Player is: " << players.at(0).getName() << ",
With Token: " << (tokenToString(players.at(0).getToken())) << "\n";
}
Figure 29: Implementation of Changing the Active Player in the System

Checking the Win State


The first check of gameLoop uses the checkWinState function, where if the returned value of this
function is false, then the loop continues. The initial value of checkWinState should always be false,
as long as the final argument is above 0. Before continuing, we will cover the checkWinState
function in its entirety.

There are several approaches to evaluating a win condition, one such method would be the use of
the “Magic Square” formula. Where the “sum of the 𝑛 numbers in any horizontal, vertical,
or main diagonal line is always the same number” (Weisstein, 2023.) In the case of a 3x3 grid, the
magic sum would be 15. And, for example, for a 5x5 grid the magic sum would be 65. This is also
referred to as the magic constant, which can be mathematically represented as the following:

Figure 30: Magic Constant (Weisstein, 2023)

However, though this method is mathematically sound. The implementation is rather difficult.

Another method is using “bit-boarding”, where everything can be stored as a binary integer and
evaluated in a single step, creating a time complexity of O(1). While incredibly efficient, this
approach was not discovered until late into the implementation of the system and would require
refactoring of a large part of the code to make it functional.

Therefore, a different approach was undertaken, where checkWinState takes three arguments, the
first being the current player (or player at index 0 of the players list), the previously initialised grid,
and the number of matching values required for a win condition to occur. In the case of a default
game of tic-tac-toe with a 3x3 grid, the matching values required are equivalent to 3, and the value
to match against is the Token of the player argument given to the function, acquired via the
getToken method of the player Class. Each column, row, or diagonal of this board have win
conditions if any 3 matching values are found in them individually. An easy way to evaluate this is to
initialise each of these as 0 and increment them when a match is found.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
32

The pure function loops over each key within the grid map, and if a value paired to that key in the
map matches the given Token, then the previously initialised values are incremented by 1. If this
incrementation reaches the value of 3 then the loop ends, and the function returns True. Note that
this function contains nested loops, to correctly iterate to simulate columns, rows, and diagonals.
This does trade complexity of implementation for computational time, resulting in a time complexity
of O(𝑛2 ).

bool checkWinState(Player player, Grid grid, int n) {


//checks win state of the board, called after each player's turn
//initialise column, row, and diagonals
int col = 0;
int row = 0;
int lDiag = 0;
int rDiag = 0;
//grab current player token for matching
Token tkn = player.getToken();
for (int i = 0; i < n; i++) {
//check the column
col = 0;
//nested loop to check each column
for (int j = 0; j < n; j++) {
if (grid[static_cast<coord>(i + j * n)] == tkn) col++;
}
if (col == n) break;
//check row
row = 0;
for (int j = 0; j < n; j++) {
if (grid[static_cast<coord>(i * n + j)] == tkn) row++;
}
if (row == n) break;
//check left diagonal
if (grid[static_cast<coord>(i * n + i)] == tkn) lDiag++;
//check right diagonal
if (grid[static_cast<coord>((i + 1) * (n - 1))] == tkn)
rDiag++;
}
//OR check to see if anything is a win condition
if (col == n || row == n || lDiag == n || rDiag == n) {
//std::cout << "winstate \n";

return true;

} else {
return false;
}
};
Figure 31: Implementation of the Function to Evaluate whether a Win Condition has been met

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
33

Checking the Draw State


Evaluation of the draw state is easier, this pure function, totalised function returns a True or False
Boolean based on if the value of the second argument given in the function call is reached. In the
case of a 3x3 tic-tac-toe board, if the value 9 is reached then the function returns True. In practice,
this would mean that if the checkWinState function has not returned True after the turn in which all
coordinates in the grid have been assigned a value which is not nullopt, then it would be a draw
condition.

bool checkDrawState(std::map<coord, Cell> grid, int n) {


//draw condition, iterates over all the values, if any are empty
then end the game
int counter = 0;
for (const auto& [key, value] : grid) {
if(value != std::nullopt) counter++;
}
if (counter == n) {
//std::cout << "Draw! \n";
return true;
} else {
return false;
}
};
Figure 32: Implementation of the Function to Evaluate whether a Draw Condition has been met

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
34

Testing
Both manual and automated tests must be undertaken to ensure features work as expected based on the gherkin specifications and other planning
components. The manual testing will use the software implementation to evaluate whether expected outputs are returned based on specific user inputs,
while the automated testing will use the Catch2 suite for unit tests and behavioural testing. Catch2 is being used as it is very simple to set up, only requiring
the importing of a header file containing the definitions, and a test case file that can access the Catch2 header file. Other options require the installation
and use of other external libraries and can be difficult to get working properly within an environment that has already been set-up without them.

Manual Testing
Test Case ID 1 Passed/Failed
Software Feature User Selects Singleplayer from the Menu Options
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully and
prompts for a menu choice in the form of user
input.

In the prompt “Please Select a A valid input of “1” is entered by the user, the
Menu Option” Enter: 1 system validates this input and prompts for a
username.
35

Test Case ID 2 Passed/Failed


Software Feature User Selects an Invalid option from the Menu Options
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully and
prompts for a menu choice in the form of user
input.

In the prompt “Please Select a An invalid input of “5” is entered by the user,
Menu Option” Enter: 1 the system validates this input and prints an
error message “ERROR: Invalid menu choice ‘5’
please try again” to standard error, and
prompts the user to select a menu option.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
36

Test Case ID 3 Passed/Failed


Software Feature User Selects a Valid Token
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully and
prompts for a menu choice in the form of
user input.

In the prompt “Please Select A valid input of “1” is entered by the user,
a Menu Option” Enter: 1 the system validates this input and prompts
for a username.

In the prompt “Please enter A valid input of “Alex” is entered by the user,
your username: “ Enter: Alex the system validates this input and prompts
for a Token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
37

In the prompt “Please select A valid input of “O” is entered by the user,
a valid Token (O or X): “ the system validates this input and prints
Enter: O that “the singleplayer game has begun”, that
“the current player is Alex” and the playing
grid to standard output and prompts for a
location input.

Test Case ID 4 Passed/Failed


Software Feature User Select an Invalid Token
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully and
prompts for a menu choice in the form of user
input.

In the prompt “Please Select a A valid input of “1” is entered by the user, the
Menu Option” Enter: 1 system validates this input and prompts for a
username.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
38

In the prompt “Please enter A valid input of “Alex” is entered by the user,
your username: “ Enter: Alex the system validates this input and prompts for
a token.

In the prompt “Please select a An invalid input of “y” is entered by the user,
valid Token (O or X): “ Enter: O the system validates this input and prints an
error message “ERROR: Invalid token value,
please try again.” And prompts the user for a
token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
39

Test Case ID 5 Passed/Failed


Software Feature User Selects a Valid Location to Place their Token on the Grid
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully
and prompts for a menu choice in the
form of user input.

In the prompt “Please A valid input of “1” is entered by the user,


Select a Menu Option” the system validates this input and
Enter: 1 prompts for a username.

In the prompt “Please enter A valid input of “Alex” is entered by the


your username: “ Enter: user, the system validates this input and
Alex prompts for a token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
40

In the prompt “Please select A valid input of “O” is entered by the user,
a valid Token (O or X): “ the system validates this input and prints
Enter: O the playing grid to standard output and
prompts for a location input.

In the prompt “Enter the A valid input of “1” is entered by the user,
location (1-9) where you the system validates this input and places
want to place your token: “ the users token in the grid at the location
Enter: 1 and prints the updated board to standard
output.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
41

Test Case ID 6 Passed/Failed


Software Feature User Selects a Location on the Grid to Place their Token that is already Occupied by a Token
Steps Expected Actual Result
Run Program Tic-Tac-Toe program
starts successfully and
prompts for a menu
choice in the form of user
input.

In the prompt A valid input of “1” is


“Please Select a entered by the user, the
Menu Option” system validates this
Enter: 1 input and prompts for a
username.

In the prompt A valid input of “Alex” is


“Please enter your entered by the user, the
username: “ system validates this
Enter: Alex input and prompts for a
token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
42

In the prompt A valid input of “O” is


“Please select a entered by the user, the
valid Token (O or system validates this
X): “ Enter: O input and prints the
playing grid to standard
output and prompts for a
location input.

In the prompt A valid input of “1” is


“Enter the entered by the user, the
location (1-9) system validates this
where you want input and evaluates that
to place your the location already
token: “ Enter: 1 contains a token. The
system then prints the
error “ERROR: That
location already contains
a token. Please choose a
different location.” And
then prompts the user for
a location input.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
43

Test Case ID 7 Passed/Failed


Software Feature User Selects an Invalid Location to Place Their Token
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts
successfully and prompts
for a menu choice in the
form of user input.

In the prompt A valid input of “1” is


“Please Select a entered by the user, the
Menu Option” system validates this input
Enter: 1 and prompts for a
username.

In the prompt A valid input of “Alex” is


“Please enter your entered by the user, the
username: “ Enter: system validates this input
Alex and prompts for a token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
44

In the prompt A valid input of “O” is


“Please select a entered by the user, the
valid Token (O or system validates this input
X): “ Enter: O and prints the playing grid
to standard output and
prompts for a location
input.

In the prompt An invalid input of “10” is


“Enter the location entered by the user, the
(1-9) where you system validates this input
want to place your and prints “ERROR: That is
token: “ Enter: 1 an invalid choice, choice
must be a whole number
between 1 and 9.” To
standard out, and prompts
for a location input.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
45

Test Case ID 8 Passed/Failed


Software Feature User Places a Token in a Grid Location that Results in that User Winning the Game
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully
and prompts for a menu choice in the
form of user input.

In the prompt “Please A valid input of “1” is entered by the


Select a Menu Option” user, the system validates this input and
Enter: 1 prompts for a username.

In the prompt “Please A valid input of “Alex” is entered by the


enter your username: “ user, the system validates this input and
Enter: Alex prompts for a token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
46

In the prompt “Please A valid input of “O” is entered by the


select a valid Token (O or user, the system validates this input and
X): “ Enter: O prints the playing grid to standard
output and prompts for a location input.

In the prompt “Enter the A valid input of “3” is entered by the


location (1-9) where you user, the system validates this input and
want to place your token: “ places the users token in the grid at the
Enter: 1 location and prints the updated board to
standard output. The system prints that
the current user has won, and the other
user has lost. The system waits for user
input to exit the program.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
47

Test Case ID 9 Passed/Failed


Software Feature User Places a Token in a Grid Location that Results in a Draw
Steps Expected Actual Result
Run Program Tic-Tac-Toe program starts successfully
and prompts for a menu choice in the form
of user input.

In the prompt “Please Select A valid input of “1” is entered by the user,
a Menu Option” Enter: 1 the system validates this input and
prompts for a username.

In the prompt “Please enter A valid input of “Alex” is entered by the


your username: “ Enter: user, the system validates this input and
Alex prompts for a token.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
48

In the prompt “Please select A valid input of “O” is entered by the user,
a valid Token (O or X): “ the system validates this input and prints
Enter: O the playing grid to standard output and
prompts for a location input.

In the prompt “Enter the A valid input of “5” is entered by the user,
location (1-9) where you the system validates this input and places
want to place your token: “ the users token in the grid at the location
Enter: 1 and prints the updated board to standard
output. The system prints that the game is
a draw. The system waits for user input to
exit the program.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
49

Automated Testing
The automated testing is comprised of both unit tests, and behaviour driven development (BDD)
style tests. The automated testing mainly covers areas where user input could cause issues with the
system and ensuring that the system handles these inputs correctly. Not all cases will be covered in
this section, in-depth test cases can be found in Appendix A.

Unit Tests
The layout of unit testing in Catch2 requires each unit test to be labelled as TEST_CASE with a
descriptor. Where assertions can be of various definitions, such as REQUIRES, REQUIRES FALSE,
CHECK_THROWS and so on.

Checking the validity of the Token the player has chosen can be checked by asserting the value
returned from the parsePlayerToken function is of a valid Token type. Here the test case is described
as an assertion of whether the Token is valid, with the string in square brackets being what is
expected as a result of the test case. In this event, a valid token should be returned.

TEST_CASE( "Token is valid", "[token]") {


Token t;
REQUIRE( parsePlayerToken("X") == t);
}
Figure 33: A Unit Test for Conversion of Valid User Input to a Token

It is also important to test that invalid inputs return the correct values. In the case of a Token, if the
user inputs anything that cannot be matched to X or O, then a null value is returned instead.

TEST_CASE ( "Token is invalid" , "[token]") {


Token t;
REQUIRE( parsePlayerToken("Y") == std::nullopt);
}
Figure 34: A Unit Test for Conversion of Invalid User Input to a Token

The same Unit tests are undertaken for Menu Choice and when selecting a location on the grid to
place a Token, as they are similar in structure they will not be listed here and can instead be found in
Appendix A.

When initialising the grid, the map structure must contain all the values of coordinate as the keys,
and all values as an optional null value, as a cell (or square) of the grid should be able to contain null,
X, or O as its value. By asserting the grid size, and looping through the grid, the keys and values of
the newly created grid can be checked one by one. The unit test should return as true.

TEST_CASE("Grid initialisation is valid for 3x3" "[Grid]") {


Grid g = initializeGrid();
REQUIRE (g.size() == 9);
for (const auto& [key, value] : g) {
REQUIRE (key >= A);
REQUIRE (key <= I);
REQUIRE (value == std::nullopt);
}
}
Figure 35: A Unit Test for Checking all the Initialised Values in the Grid are Correct
50

Furthermore, it is possible to check if the win state is functioning correctly by running an assertion
on the checkWinState() function. By creating a new grid, and manually setting the values of the map
of Keys A, B, and C to the same Token, and creating a new Player object who also has been assigned
that Token.

TEST_CASE ("Win Condition is met" "[WinCondition]") {


Grid g = {{coord::A, X()},{coord::B,X()},{coord::C,X()},
{coord::D,std::nullopt},{coord::E,std::nullopt},{coord::F,std::nullopt
},
{coord::G,std::nullopt},{coord::H,std::nullopt},{coord::I,std::nullopt
}
};

Token tkn = X();


Player player1(tkn, "Alex", Statistics(0,0,0));
REQUIRE(checkWinState(player1, g, 3) == true);
}
Figure 36: A Unit Test for Checking a Win State

A similar method can be followed for the draw state, though since the checkDrawState checks
against the number of turns taken no player data needs to be passed to the function. By setting
every value in the grid to a valid Token, where no win condition subsets are met, the draw state
should evaluate as true.

TEST_CASE ("Draw Condition is met" "[DrawCondition]") {


Grid g = {{coord::A, X()},{coord::B,O()},{coord::C,X()},
{coord::D,X()},{coord::E,X()},{coord::F,O()},
{coord::G,O()},{coord::H,X()}, {coord::I,O()}};
REQUIRE(checkDrawState(g, 9) == true);
}
Figure 37: A Unit Test for Checking a Draw State

Behaviour Driven Development (BDD) Tests


Through use of Catch2, BDD Tests can also be created. Using the information from the previously
created Gherkin Specifications, a similar layout can be written as code to automate the test. Below is
an example of a user selecting the game type to be played.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
51

SCENARIO( "Selecting the Game Type to be Played (User chooses to start a new
2-player game)", "[gameType]" ) {

GIVEN( "the human player has specified the game type they would like to
play as a valid string “1” from the game-type menu" ) {
std::optional<MenuChoice> mChoice = parseMenuInput("2");
WHEN( "player menu choice is confirmed" ) {
if (mChoice.has_value()){
REQUIRE(mChoice.value() == Multiplayer);
}

THEN( "the game begins and prompts the player to select their
token via stdout" ) {

}
}
}
}
Figure 38: BDD Scenario of a User Selecting a Game Type to be Played

The setup for each of these scenarios requires that certain values be set manually, so that input is
not required for them to run. This can be seen above in the example line
“std::optional<MenuChoice> mChoice = parseMenuInput(“2);”, where in the actual program
getPlayerMenuInput would be the function that is called to obtain the “2” value.

For another example of a BDD Scenario, the user selects an invalid Token to play with. Again, being
like the Gherkin specification, by evaluating if the string given can be parsed as a Token the scenario
can be automated and be made to return an error message.

//BDD invalid Token user input Scenario


SCENARIO( "Selecting the desired Token (User chooses y)", "[Token]" ) {

GIVEN( "the human player has specified the Token they would like to play
with as an invalid string 'y'") {
Token t;
std::string userInput = "y";
WHEN( "player menu choice is confirmed" ) {
REQUIRE_FALSE( parsePlayerToken(userInput) == t);
}
THEN( "the system prints ERROR: Invalid token value, please try
again. to standard out, and the user is prompted to pick a valid token." )
{
}
}
}
Figure 39: BDD Scenario of a User Selecting an Invalid Token

A final example that will be shown is a user adding a Token to a valid grid location. Here, objects
must be first initialised, such as the grid, the token to check against, and the player. When the

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
52

playerGridChoiceValue function is called, if the casted coordinate value is nullopt then the new
Token value is assigned to it.

SCENARIO( "Adding a Token to an empty Grid Square", "[Token]" ) {

GIVEN( "the human player has specified a valid coordinate choice '1'") {
Grid g = initializeGrid();
Token tkn = X();
Player player1(tkn, "Alex", Statistics(0,0,0));
int input = playerGridChoiceValue(1);
coord c = static_cast<coord>(input - 1);
WHEN( "player menu choice is confirmed" ) {
for (const auto& [key, value] : g) {
if (g[c] == std::nullopt) {
g[c] = tkn;
}
REQUIRE (g[A] == tkn);
}
THEN( "the system prints the updated board, and prompts the next
player for their turn." ) {
}
}
}
}
Figure 40: BDD Scenario of Adding a Token to an Empty Grid Square

Correctness of Unit Tests


Assuming that the Unit Tests are evaluating the correct values, then every assertion in every Unit
Test should pass. Below is the terminal output of the Test Case run result.

Figure 41: Test Cases have Passed

This shows that all tests validated and returned the desires result over 95 assertions across 19
different test cases.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
53

T4 A Discussion of Version Control Systems (Git)


Documentation can be of great benefit to a project. Proper documentation allows for a full
explanation of “what a codebase does and how it can be used” (Meza, 2018). It also allows for
secondary developers to review the documentation to understand the rules to approaches, naming
conventions, comments etc. to allow for properly maintainable code.

Version control is a system that keeps a record of “changes to a file or set of files over time”(Chacon
and Straub, 2022) so that specific versions can be recalled later in time. For example, if a merge
request occurred that inadvertently broke functionality within the program it would be possible to
revert to a previous version of the repository before these changes are made. However, there are
many approaches to version control. Where something simple like copying files to another directory
may be a common approach; automation of this approach through tools like git are preferable as the
former method is “incredibly error prone”(Chacon and Straub, 2022.) When working as a team,
version control systems like Git are incredibly useful to track changes to a codebase. When using
software such as GitHub, “version control identifies the problem areas so that team members can
quickly revert changes to a previous version, compare changes, or identify who committed the
problem code through the revision history” (GitLab, 2023.)

Git facilitates several functionalities within its toolset. One such functionality is “clone”. Cloning a
repository will place the data in a new directory, while simultaneously creating “remote-tracking
branches for each branch in the cloned repository (visible using git branch --remotes)” (Git, 2022.)
This is quite often the first step when attempting to modify a repository, as It allows for changes to
be main without affecting the master/main repository/branch and the files therein. Allowing for
code to be simultaneously edited while still having a previous version still being live.

Once changes have been made, it is possible to “commit” these changes to the main branch, a
commit is effectively a save of the version of the codebase at that point in time. Each commit can be
used as a point to go back to at a later stage of the product in the event the user wishes to make a
change, or encounters a bug at some point in the future after a commit.

Push and pull are somewhat similar terms in Git but have a distinct difference in their use. A push
would take code create on a local machine and send it to the Git repository. A pull would update the
local machine with the version stored in Git. When performing a pull request what is effectively
happening is a team member asking for other team members to take the changes made and review
them before merging them to the main repository.

Finally, merging is used to combine changes together within a single branch. However, merging will
cause a conflict if the file being updated has been modified outside of the current merge. For
example, if one team member tries to merge a change to a file, but that file has been removed by
another team member during the interim then a merge conflict will occur until the issue is resolved.

If a version control system like git was used for the Tic-Tac-Toe project it would allow for storage of
different stages of the program as it progressed. Allowing for easy resolution of issues where a bug
was introduced, or data loss occurred. A very basic form of version control is saving files in different
folders manually at regular intervals, but using a software-based solution both saves time and
ensures consistent file backups. Moving forward, it would be beneficial to use software solutions
such as GitHub to allow for easier management of program versions. Giving access to the project
from anywhere from online storage, easily cloned in to an Integrated Development Environment.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
54

Case Study (CppKoans)


For a case study example of documentation and use of Git, the “CppKoans” which is a port based on
the pre-existing Python and Ruby Koans. Python Koans being a way to learn a programming language
by making test cases pass. For example, an assertion error that has incorrect syntax will be identified
to indicate where it needs to be fixed to make the test work correctly. CppKoans can be found at:

https://github.com/torbjoernk/CppKoans

The commit history for CppKoans can be easily accessed by clicking the commits symbol when
navigating GitHub, underlined in red in figure X.

Figure 42: The Commit History Navigation Button of CppKoans

Commits are listed in order, with the most recent commit being at the top of the list. By scrolling
down, or selecting older, it is possible to see the oldest commit made to the repository. In the case
of the CppKoans repository, it can be seen that the oldest commit was titled “initial commit” by the
user torbjoernk on the 20th April 2012.

Figure 43: The Earliest Commit made to CppKoans

Within this commit, the README.md file was modified, to give a description of what the repository
is about. As well as the .gitignore file, which “specifies intentionally untracked files that Git should
ignore” [https://git-scm.com/docs/gitignore]. It can be seen that all filesnames ending in .slo, .lo, .o,
.so, .lai, .la, and .a are requested to be ignored by git. Any files that end with the former will not be
listed in documented changes going forward.

Looking at a different commit, which can be found at:

https://github.com/torbjoernk/CppKoans/commit/edc687c7361293fb4fdbe182aaf8cafa993a5d56

There are more substantial changes made to the repository. Torbjoernk made this commit on the
26th April 2012 to the main branch. Within this commit several koans were defined to handle other
types that had not already been covered by the codebase already. As can be seen in the
koan03_further_types.hpp file, this koan handles string values.

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
55

Figure 44: The Code Modifications made to the koan03_further_types.hpp file

This change also modifies other files within the codebase to accommodate the new koan, such as
including the koan_further_types.hpp (header) file in the all_koans.hpp file, and increasing the
number of tests in the koan02_character_types.hpp file to allow for the inclusion of the further
types koan.

Figure 45: The Code Modifications made to the koan02_character_types.hhpp file

Within the issue tracker for the project, which can be navigated to via the project navigation bar it
can be seen that there are three issues, only one of which has been closed. Looking at the open and
closed issues, it can be seen that the repository owner has not responded to any issues raised for the
entirety of the project lifespan. This is likely in part to the current README.md stating not to open
tickets on GitHub and instead post to the Cucumber discussion group.

Figure 46: The Open Issues in the CppKoans Issue Tracker

The closed issue, submitted by the user SuperCool83 on the 1st of August 2018 is an issue with
getting cmake to install correctly. Which is unrelated to the project scope. The user who submitted

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
56

the issue closed it themselves on the 3rd August 2018, managing to resolve their issue within a Linux
environment.

In terms of Pull requests it can be seen that there have been six in total across the lifespan of the
repository, of which 3 are still open, and 3 have been closed. The pull request submitted by the user
mewmew on the 16th September 2015 was merged on the 18th November 2015 by torbjoaernk into
the main repository. This pull request refers to a “typo” being made in the Koan_01 variable, this
syntax was modified by the pull requester to change the type from an integer to a float.

Figure 47: The Modified Code as Shown in the Pull Request by user mewmew

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
57

APPENDIX A

Full Test Case Code

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "game.h"
#include "playingGrid.h"

//reference material
//https://github.com/catchorg/Catch2/blob/v2.x/docs/tutorial.md#top

//Check if string input by user can be converted to a valid Token


TEST_CASE( "Token is valid", "[token]") {
Token t;
REQUIRE( parsePlayerToken("X") == t);
}

//Check if string input by user is invalid to be converted to a Token


TEST_CASE ( "Token is invalid" , "[token]") {
Token t;
REQUIRE( parsePlayerToken("Y") == std::nullopt);
}

//Check if string input by user can be converted to a valid MenuChoice


TEST_CASE ( "Menu Choice is valid" "[MenuChoice]") {
std::optional<MenuChoice> mChoice = parseMenuInput("1");
if (mChoice.has_value()) {
REQUIRE(mChoice.value() == MenuChoice::Singleplayer);
}
}

//Check if string input by user is invalid to be converted to a MenuChoice


TEST_CASE ( "Menu Choice is invalid" "[MenuChoice]") {
REQUIRE (parseMenuInput("5") == std::nullopt);
}

//Check if int input by user is valid to be converted to a gridChoice


TEST_CASE ("Player grid choice is valid" "[GridChoice]") {
REQUIRE (playerGridChoiceValue(1) == 1);
}

//Check if int input by user is invalid to be converted to a gridChoice


TEST_CASE ("Player grid choice is invalid" "[GridChoice]") {
CHECK_THROWS (playerGridChoiceValue(10));
}

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
58

//Check grid initial state is valid (all keys are of Coord, all values are
nullopt)
TEST_CASE("Grid initialisation is valid for 3x3" "[Grid]") {
Grid g = initializeGrid();
REQUIRE (g.size() == 9);
for (const auto& [key, value] : g) {
REQUIRE (key >= A);
REQUIRE (key <= I);
REQUIRE (value == std::nullopt);
}
}

//Check grid initial state is invalid (key initialised as having a Token


value)
TEST_CASE("Grid initalisation in invalid" "[Grid]") {
Grid g = initializeGrid();
g[A] = X();
for (const auto& [key, value] : g) {
REQUIRE (key >= A);
REQUIRE (key <= I);
REQUIRE_FALSE(g[A] == std::nullopt);
}
}

//Check user can place a Token in a grid coordinate with a nullopt value
TEST_CASE("Adding a Token to an empty Grid Square" "[Grid]") {
Grid g = initializeGrid();
Token tkn = X();
Player player1(tkn, "Alex", Statistics(0,0,0));
int input = playerGridChoiceValue(1);
coord c = static_cast<coord>(input - 1);
for (const auto& [key, value] : g) {
if (g[c] == std::nullopt) {
g[c] = tkn;
}
REQUIRE (g[A] == tkn);
}

//Check user cannot place a Token in a grid coordinate that is already


occupied by a token
TEST_CASE("Attempting to add a Token to an occupied Grid Square" "[Grid]") {
Grid g = initializeGrid();
g[A] = O();
Token tkn = X();
Player player1(tkn, "Alex", Statistics(0,0,0));

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
59

int input = playerGridChoiceValue(1);


coord c = static_cast<coord>(input - 1);
for (const auto& [key, value] : g) {
if (g[c] != std::nullopt) {
REQUIRE_FALSE(g[A] == tkn);
}
}
}

//Check grid win condition validates


TEST_CASE ("Win Condition is met" "[WinCondition]") {
Grid g = {{coord::A, X()},{coord::B,X()},{coord::C,X()},
{coord::D,std::nullopt},{coord::E,std::nullopt},{coord::F,std::nullopt
},
{coord::G,std::nullopt},{coord::H,std::nullopt},
{coord::I,std::nullopt}};

Token tkn = X();


Player player1(tkn, "Alex", Statistics(0,0,0));
REQUIRE(checkWinState(player1, g, 3) == true);
}

//Check grid draw condition validates


TEST_CASE ("Draw Condition is met" "[DrawCondition]") {
Grid g = {{coord::A, X()},{coord::B,O()},{coord::C,X()},
{coord::D,X()},{coord::E,X()},{coord::F,O()},
{coord::G,O()},{coord::H,X()}, {coord::I,O()}};
REQUIRE(checkDrawState(g, 9) == true);
}

//BDD Valid MenuChoice user input Scenario


SCENARIO( "Selecting the Game Type to be Played (User chooses to start a new
2-player game)", "[gameType]" ) {

GIVEN( "the human player has specified the game type they would like to
play as a valid string “2” from the game-type menu" ) {
std::optional<MenuChoice> mChoice = parseMenuInput("2");
WHEN( "player menu choice is confirmed" ) {
if (mChoice.has_value()){
REQUIRE(mChoice.value() == Multiplayer);
}

THEN( "the system prompts the player to select their token via
stdout" ) {

}
}
}

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
60

//BDD invalid MenuChoice user input Scenario


SCENARIO( "Selecting the Game Type to be Played (User chooses an invalid
option)", "[gameType]" ) {

GIVEN( "the human player has specified the game type they would like to
play as an invalid string “5” from the game-type menu" ) {
std::optional<MenuChoice> mChoice = parseMenuInput("5");
WHEN( "player menu choice is confirmed" ) {
REQUIRE(mChoice == std::nullopt);
}

THEN( "ERROR: Invalid menu choice is printed to standard out, and


the user is prompted to please try again.\n" ) {

}
}
}

//BDD Valid Token user input Scenario


SCENARIO( "Selecting the desired Token (User chooses x)", "[Token]" ) {

GIVEN( "the human player has specified the Token they would like to play
with as a valid string 'x'") {
Token t;
std::string userInput = "x";
WHEN( "player token choice is confirmed" ) {
REQUIRE( parsePlayerToken(userInput) == t);
}
THEN( "the game either prompts the next user for their name, or
the game begins" ) {
}
}
}

//BDD invalid Token user input Scenario


SCENARIO( "Selecting the desired Token (User chooses y)", "[Token]" ) {

GIVEN( "the human player has specified the Token they would like to play
with as an invalid string 'y'") {
Token t;
std::string userInput = "y";
WHEN( "player token choice is confirmed" ) {
REQUIRE_FALSE( parsePlayerToken(userInput) == t);
}

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
61

THEN( "the system prints ERROR: Invalid token value, please try
again. to standard out, and the user is prompted to pick a valid token." )
{
}
}
}

//BDD user places a Token in a valid Grid Location Scenario


SCENARIO( "Adding a Token to an empty Grid Square", "[Token]" ) {

GIVEN( "the human player has specified a valid coordinate choice '1'") {
Grid g = initializeGrid();
Token tkn = X();
Player player1(tkn, "Alex", Statistics(0,0,0));
int input = playerGridChoiceValue(1);
coord c = static_cast<coord>(input - 1);
WHEN( "player grid choice is confirmed" ) {
for (const auto& [key, value] : g) {
if (g[c] == std::nullopt) {
g[c] = tkn;
}
REQUIRE (g[A] == tkn);
}
THEN( "the system prints the updated board, and prompts the next
player for their turn." ) {
}
}
}
}

//BDD user places a Token in a grid coordinate that is already occupied by a


Token Scenario
SCENARIO( "Adding a Token to an occupied Grid Square", "[Token]" ) {

GIVEN( "the human player has specified a valid coordinate choice '1'") {
Grid g = initializeGrid();
Token tkn = X();
Player player1(tkn, "Alex", Statistics(0,0,0));
int input = playerGridChoiceValue(1);
coord c = static_cast<coord>(input - 1);
WHEN( "player grid choice is confirmed" ) {
for (const auto& [key, value] : g) {
if (g[c] != std::nullopt) {
REQUIRE_FALSE(g[A] == tkn);
}
}

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
62

THEN( "the system prints 'That location already contains a token.


Please choose a different location.' to standard out, and prompts the user to
choose again." ) {
}
}
}
}

//BDD user attempts to add a Token to an invalid grid square value Scenario
SCENARIO( "Adding a Token to an invalid Grid Square", "[Token]" ) {

GIVEN( "the human player has specified an invalid coordinate choice '10'")
{
int input = 10;
WHEN( "player grid choice is confirmed" ) {
CHECK_THROWS(playerGridChoiceValue(10));
THEN( "the system prints 'That location already contains a token.
Please choose a different location.' to standard out, and prompts the user to
choose again." ) {
}
}
}
}

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming
63

References
Meza, F. , 2018. The Value of Code Documentation. Available at:
https://www.olioapps.com/blog/the-value-of-code-documentation [Accessed: 17/04/2023]

Weisstein, E. , 2023. Magic Square. Available at: https://mathworld.wolfram.com/MagicSquare.html


[Accessed 10/05/2023]

Chacon, S. and Straub, B. (2022) About Version Control. Available at: https://git-
scm.com/book/en/v2/Getting-Started-About-Version-Control [Accessed: 17/05/2023]

Git. (2022) Git - git-clone Documentation. Available at: https://git-scm.com/docs/git-clone


[Accessed: 18/05/2023]

GitLab. (2023) What is Version Control? Available at: https://about.gitlab.com/topics/version-


control/ [19/05/2023]

Alex Bugby – S21185648


CMP5361 Computer Mathematics and Declarative Programming

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy