Mastermind is a code-breaking game for two players.
Introduction
Traditionally, the game is played using:
-
a decoding board, with a shield at one end covering a row of four large holes, and twelve additional rows containing four large holes next to a set of four small holes
-
code pegs of six different colours, which will be placed in the large holes on the board
-
small key pegs, some coloured black, some white, which will be placed in the small holes on the board
source: Wikipedia
In the conventional Mastermind game there are two players:
-
CodeMaker - chooses a pattern of four code pegs and places them in the four holes covered by the shield. This secret code is only visible to the CodeMaker.
-
CodeBreaker - tries to guess the secret code withing twelve turns, in both order and colour. Each guess is made by placing a row of code pegs on the decoding board. Once placed, the CodeMaker provides feedback by placing zero to four key pegs in the small holes of the row with the guess.
Feedback is provided with key pegs:
-
a black key peg is placed for each code peg from the guess which is correct in both colour and position
-
a white key peg is placed for each code peg from the guess which is correct in colour but in a wrong position
-
if there are duplicate colours in the guess, they can only be awarded a key peg if they correspond to the same number of duplicate colours in the secret code
Overview
System Context
The system context diagram below presents the Mastermind system in the context of users and other systems.
Functional Overview
The Mastermind software is a single player code-breaking game. This is different to the classic Mastermind which is a two player game. In the digitalised version the CodeMaker player is replaced by the system.
In the game of Mastermind, the CodeBreaker has the capability to:
-
Start a new game - a new secret code is randomly generated each time the game is started.
-
Make a guess - CodeBreaker has a number of attempts to break the code. Feedback is given on each attempt.
Expectations
Quality Attributes
Since this is a pet project the expectations below are mostly made up.
Performance
-
The system needs to handle 1,000 simultaneous games every hour for consecutive 8hrs.
Scalability
-
The system needs to cope with 10,000 games a day for the next 5 years.
Availability
-
The game should be available to the end users 24x7.
-
Occasional downtime of less than 1hr a week can be tolerated.
Failover
-
There’s no need for a failover.
Security
-
The game should be anonymous and open to everyone.
Audit
-
The following events must be recorded in the system audit log:
-
Game started
-
Move Made
-
Fault tolerance and resilience
-
All errors should be logged.
Internationalization and localization
-
all user interfaces, messages, and communication should be English only
Monitoring and management
-
an e-mail notification should be sent in case:
-
a fatal error happens
-
a system healthcheck fails
-
Data retention and archiving
-
data should be retained indefinitely
-
data should be archived daily
Interoperability
-
No interoperability requirements
Constraints
Time, budget, resources
-
since this is a pet project, there’s no time, budget, nor resource constraints.
Approved technology lists
-
JVM/Kotlin
-
PostgreSQL
-
vlingo
Target deployment platform
Anywhere that runs docker.
Candidates:
-
DigitalOcean
-
GCP
-
AWS
Standard protocols
-
JSON based HTTP API
Principles
-
Follow test driven development
-
Favour automation over documentation
-
Have fun and learn
Implementation
Software architecture
Container view
The diagram below focuses on high level responsibilities and technology choices within the Mastermind system.
Component view
The diagram below drills into the Mastermind API container to illustrate its main components.
Code
This section describes the significant implementation details of the Mastermind system.
Event Sourcing Implementation
vlingo/lattice
is used
for implementation of event sourcing.
Lattice is part of the vlingo/PLATFORM project. It’s an Open Source platform that aims to simplify the creation of event-driven reactive architectures.
More can be found on the platform’s website: https://docs.vlingo.io.
Data
Development environment
-
Java 11
-
Docker >= 19.03
-
IntelliJ IDEA with Kotlin 1.3
Executing tests
Tests can be triggered from IntelliJ or with gradle:
./gradlew test
Running the application
The simplest way to run the application is by executing the main
function from IntelliJ.
The main
function can be found in the Application
class.
The application can also be run in command line with gradle:
./gradlew run
The port number and configuration can be optionally passed as arguments:
./gradlew run --args 'classpath:in-memory.properties 9090'
A custom configuration file can also be loaded with:
./gradlew run --args 'path/to/my.properties'
There are two persistence strategies to choose from to run the application:
-
in-memory (default) -
classpath:in-memory.properties
-
postgresql -
classpath:postgresql.properties
A running postgresql server is needed for the postgresql
strategy.
It can be started with the provided docker-compose
configuration:
docker-compose up -d
Building the application jar
To build a fat jar with all the necessary dependencies run:
./gradlew shadowJar
Now the jar can be executed with:
java -jar build/libs/mastermind-1.0-SNAPSHOT.jar
Arguments can be provided as well:
java -jar build/libs/mastermind-1.0-SNAPSHOT.jar classpath:postgresql.properties 9090
HTTP API
Game Resource
Start a game
POST /games
Accept: application/json
HTTP/1.1 201 Created
Content-Length: 49
Content-Type: application/json
Location: /games/b2ee7966-6c58-41ae-905c-11176bafe910
{
"gameId": "b2ee7966-6c58-41ae-905c-11176bafe910"
}
http POST http://localhost:8080/games
Make a guess
POST /games/b2ee7966-6c58-41ae-905c-11176bafe910
Accept: application/json
Content-Type: application/json
{
"guess": ["RED", "GREEN", "BLUE", "YELLOW"]
}
HTTP/1.1 200 OK
Content-Length: 103
Content-Type: application/json
{
"feedback": {
"outcome": "IN_PROGRESS",
"pegs": [
"WHITE"
]
},
"gameId": "b2ee7966-6c58-41ae-905c-11176bafe910"
}
HTTP/1.1 400 Bad Request
Content-Length: 111
Content-Type: application/json
{
"error": "The code is 3 colours long but expected it to be 4.",
"gameId": "b2ee7966-6c58-41ae-905c-11176bafe910"
}
http POST http://localhost:8080/games/b2ee7966-6c58-41ae-905c-11176bafe910 guess:='["RED", "GREEN", "BLUE", "YELLOW"]'
View a decoding board
GET /games/b2ee7966-6c58-41ae-905c-11176bafe910
Accept: application/json
HTTP/1.1 200 OK
Content-Length: 136
Content-Type: application/json
{
"gameId": "b2ee7966-6c58-41ae-905c-11176bafe910",
"maxMoves": 12,
"moves": [
{
"feedback": [
"WHITE"
],
"guess": [
"RED",
"GREEN",
"BLUE",
"YELLOW"
]
}
]
}
http GET http://localhost:8080/games/b2ee7966-6c58-41ae-905c-11176bafe910
Operation
Infrastructure architecture
Deployment
Operation and support
History
Decision log
This section lists all the significant architecture and technology choices.
1. Record architecture decisions
Date: 2020-01-23
Status
Accepted
Context
We need to record the architectural decisions made on this project.
Decision
We will use Architecture Decision Records, as described by Michael Nygard.
Consequences
See Michael Nygard’s article, linked above. For a lightweight ADR toolset, see Nat Pryce’s adr-tools.
2. Choose software architecture
Date: 2020-01-23
Status
Accepted
Context
The sole purpose of this project is to practice CQRS and Event Souring architecture patterns with the https://docs.vlingo.io/vlingo-lattice/entity-cqrs:vlingo/lattice library.
Decision
By definition this project has to use event sourcing.
Consequences
Software architecture of the project will follow what vlingo’s documentation and examples demonstrate. Decoupling from vlingo is not going to be the focus at this point.
3. Choose the database software
Date: 2020-01-23
Status
Accepted
Context
The application needs two types of persistence:
-
event store - to persist the stream of events
-
view store - to persist views generated by projections
vlingo supports many popular database systems via its JDBC driver. Some database systems got its dedicated driver.
Decision
PostgreSQL will be used for both stores as it’s a popular, easy to set up, and efficient database system that the team has experience with.
Consequences
As a consequence the project can be started quickly, with PostgreSQL running in a docker container. Shall the need ever arise, the decision can be revisited at a later stage to migrate one of the stores to a different system.