By: Team SErebros
Since: Aug 2019
Licence: MIT
1. Setting up
Refer to the guide here.
2. Design
2.1. Architecture
The Architecture Diagram given above explains the high-level design of Dukemon. Given below is a quick overview of each component.
The .puml files used to create diagrams in this document can be found in the diagrams folder.
Refer to the Using PlantUML guide to learn how to create and edit diagrams.
|
-
At app launch: Initializes the components in the correct sequence, and connects them up with each other.
-
At shut down: Shuts down the components and invokes cleanup method where necessary.
Commons
represents a collection of classes used by multiple other components.
The following class plays an important role at the architecture level:
-
LogsCenter
: Used by many classes to write log messages to the App’s log file.
The rest of Dukemon contains seven componenets.
-
UI
:
The Graphical UI of Dukemon that interacts with the user. -
AppManager
:
The buffer between the User and Dukemon’s internal components. -
Timer
:
The internal Timer that triggers events based on time elapsed. -
Logic
:
The main command executor and performer of operations. -
Model
:
Holds the non-game data in-memory. -
Game
:
Holds the data of live game sessions in-memory. -
Storage
:
Reads data from, and writes data to, the local hard disk.
For the components UI, Logic, Model, Timer, Storage and Game:
-
Defines its API in an
interface
with the same name as the Component. -
Exposes its functionality using a
{Component Name}Manager
class.-
ie.
StorageManager
implementsStorage
,GameTimerManager
implementsGameTimer
.
-
The sections below give more details of each component.
2.2. UI component
API : Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, ModularDisplay
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
As our application also comprises a dynamically changing UI in replacement of the PersonListPanel
, ModularDisplay
contains multiple other Panel
classes in itself in order to render the proper screens during runtime. This rendering is controlled by UpdateUi
in MainWindow
.
The UI
component uses JavaFx UI framework. The layouts of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
-
Executes user commands using the
AppManager
component. -
Listens for changes to
Model
data andTimer
through theAppManager
so that the UI can be updated correspondingly.
2.3. AppManager component
The AppManager
component serves as a Facade layer and communication hub between the internal components of Dukemon and the UI components.
Using this extra layer provides better abstraction between the UI
and the internal components, especially between the Timer
and the UI
.
AppManager
communicates with both the Logic
and Timer
components to send feedback to the UI
to display back to the user.
-
Gets feedback for commands by through
Logic
-
Starts and Stops the
Timer
when required. -
Makes call-backs to the
UI
to update variousUI
components. -
Initiates collection of
Statistics
by pulling data (eg. Time Elapsed) fromTimer
andLogic
.
2.4. Timer component
API :
GameTimer.java
The Timer
consists of a GameTimer
that will keep track of time elapsed via an internal countdown timer
and notify the AppManager
, who will notify the UI
components.
-
Dealing with the internal countdown timer that runs during a game session.
-
Periodically triggering callbacks that will notify the
AppManager
component. -
Gets timestamps to trigger
Hints
via aHintTimingQueue
Due to the fact that the Timer
has to work closely with the UI
and AppManager
(without being
coupled directly), it is separated from the Logic
, Model
and Game
components.
2.5. Logic component
This section breakdown the logic package into its internal components
Logic is primarily built by two segments: Command and Parser.
Command
Command is an abstract class.
Four other abstract classes (WordBankCommand, CardCommand, GameCommand and SettingsCommand) extend Command.
Concrete Command classes with an execute method implementation extend one of the above four abstract classes.
Parser
ParserManager holds reference to two SpecificModeParsers
The SpecificModeParsers change based on current application mode.
They hold references to all concrete Parser and Command Classes with the help of ClassUtil
Logic fulfils its contracts with other packages through two interfaces: Logic and UiLogicHelper
2.5.3. Interaction through Logic Interface
Examples of transactions promised by Logic API include command execution, command result and update statistics.
-
Command Execution through
Logic Interface
-
A String from Ui package gets to
ParserManager
and gets converted into aCommand
object which is executed by theLogicManager
. -
The command execution can affect the
Model
(e.g. adding a word meaning pair into wordbank). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
andAppManager
. -
In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
-
2.5.4. Interaction through UiLogicHelper Interface
UiLogicHelper APIs is a subset of Logic APIs and only contains transactions for AutoComplete. It exposes the functionalities through the following getter methods:
-
List<AutoFillAction>#getMenuItems(String text)
— Gets a List of AutoFillActions to fill up AutoComplete display based on current user input given in text -
ModeEnum#getMode()
— Retrieves the application mode to display visually to the user (represented by enumeration object ModeEnum) -
List<ModeEnum>#getModes()
— Retrieves the possible modes the user can transition to from current mode
The following sequence diagram shows how the AutoComplete operation runs when user keys in "st" into command box.
API :
Logic.java
UiLogicHelper.java
2.6. Model component
API : Model.java
The Model
,
-
contains information that the game requires at run time. They include:
WordBankList
,WordBankStatisticsList
,GlobalStatistics
,Game
,AppSettings
,UserPrefs
. -
does not depend on any of the higher level components. i.e.
Ui
,Timer
,AppManager
,Logic
,Storage
. -
has a direct reference to a user selected
WordBank
. -
exposes an unmodifiable
ObservableList<Card>
that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
2.7. Game component
The Game
component,
-
stores a shuffled
List<Card>
that is cloned/copied from aReadOnlyWordBank
. -
maintains an
Index
to keep track of the state of the game. -
has an associated
DifficultyEnum
that dictates the time allowed for each question. -
verifies
Guess
that are sent byLogic
(User’s guesses)
2.8. Storage component
API : Storage.java
The Storage
component,
-
contains multiple types of distinct storage system.
-
does not depend on any of the higher level components. i.e.
Ui
,Timer
,AppManager
,Logic
,Model
. -
handles function calls directly to the computer’s system.
-
can save data objects in json format and read it back.
2.9. Statistics component
The Statistics component includes 2 main subcomponents:
-
A
GlobalStatistics
, containing the user’s total number of games played and the number of games played in the current week. -
A
WordBankStatisticsList
, which is a collection ofWordBankStatistics
, one for eachWordBank
.
The class diagram of the Statistics component is shown below:
2.10. Common classes
Classes used by multiple components are in the seedu.Dukemon.commons
package.
3. Implementation
This section describes some noteworthy details on how certain features are implemented.
3.1. AutoComplete/Parser Feature
This section explains how the design choice of Dynamic Parsers fulfils AutoComplete and Command Execution.
ParserManager dynamically changes parser depending on current mode the game is at. This is modeled using the Strategy Pattern. https://en.wikipedia.org/wiki/Strategy_pattern.
Instead of choosing a single parser to use at compile time, they are chosen at runtime depending on runtime state. This supports a variety of benefits which are explained under design considerations.
The above implementation empowers the application with the following features :
-
Every user keystroke only auto completes the right commands
-
Only the right commands get parsed and executed. What are the right commands? They are the commands that belong to the current mode and switch commands when preconditions are met.
3.1.1. Implementation details of ParserManager
-
ParserManager
instance has reference to twoSpecificModeParser
objects -
When user enters a keystroke, the
SpecificModeParser
which holds switch commands orSpecificModeParser
which holds current mode commands are accessed based on internal state. -
Internal State consists of booleans: gameIsOver, bankLoaded and enumeration ModeEnum: HOME, OPEN, GAME, SETTINGS
-
Boolean algebra is used to derive the four overall states.
The below activity diagram demonstrates four possible states and a typical user flow.
-
Definitions of Switch and Mode in table above
-
SwitchCommands = (commands that change mode)
-
ModeCommands = (commands that belong to a specific mode ie Home, Open, Game and Settings)
-
3.1.2. Implementation details of SpecificModeParser
-
SpecificModeParsers use ClassUtil to handle instantiation of Parser and Command objects.
-
ClassUtil holds a list of references to Command and Parsers classes. In Java class references are passed using .class attribute. Example: AddCommand.class
-
Internally, ClassUtil employs java reflections to find attributes of classes without instantiating them. Code for it is succinct and shown in the snippet linked here.
-
Also, when a command needs to be executed, it instantiates the Parser object (if any) and Command object at runtime.
-
Here is a snippet is from ParserManager. Just one line of code is necessary to include a new command with its parser. Example:
temp.add(NewCommand.class, NewCommandParser.class);
3.1.3. Design Considerations
Alternative 1 |
Alternative 2 |
|
Aspect 1: |
Use java reflections to hold a list of classes and iterate through them to pick the matching classes |
Use switches to match the command word with the right parsers |
Why did we choose Alternative 1: |
||
Aspect 2: |
Using a ParserManager to dynamically switch between Parsers based on current state |
Use a single parser |
Why did we choose Alternative 1: |
3.2. Settings Feature
3.2.1. Implementation
AppSettings
is a class that was created to be integrated into the Model
of the app. It currently contains these functionalities:
-
difficulty [EASY/MEDIUM/HARD]
to change the difficulty of the game. -
hints [ON/OFF]
to turn hints on or off. -
theme [DARK/LIGHT]
to change the theme of the app. Currently only supporting dark and light themes.
This feature provides the user an interface to make their own changes to the state of the machine. The settings set by the user will also be saved to a .json
file under data/appsettings.json
.
The activity diagram below summarizes what happens in the execution of a settings command:
Take note that "mode" as defined in our project is the state in which the application is able to take commands specific to that mode. |
Given below is a step by step walk-through of what happens when a user executes a difficulty command while in settings mode:
Step 1:
Let us assume that the current difficulty of the application is "EASY". The object diagram above shows the current state of AppSettings
.
Step 2:
When the user enters difficulty hard
, the command gets passed into Ui first, which executes AppManager#execute()
, which passes straight to LogicManager#execute()
without any logic conditions to determine its execution path.
Step 3:
At LogicManager#execute()
however, the command gets passed into a parser manager which filters out the DifficultyCommand
as a non-switch command and it creates a DifficultyCommand
to be executed.
Step 4:
Upon execution of the DifficultyCommand
, the state of the model is changed such that the DifficultyEnum
in AppSettings
is now set to HARD
.
Step 5:
Since the main function of the difficulty
command is accomplished and all that is left is to update the ui, the CommandResult
that is produced by the execution of the command goes back to Ui
without much problem.
Step 6:
Assuming that there were no errors thrown during the execution of the difficulty
command, the execution calls updateModularDisplay
in UpdateUi
. In here, the ModeEnum.SETTINGS
is registered and it updates the settings display to properly reflect the change in difficulty.
The state of appSettings is then as follows:
3.2.2. Design Considerations
There were a few considerations for implementing an interface that essentially allows users to touch a lot of parts of the application through settings and some of these methods break software design principles. These are the considerations we came across:
Alternative 1 |
Alternative 2 |
|
Aspect 1: |
Effecting the change inside the |
Effecting the change in the part of the architecture that the setting is affecting. E.g, Changing the theme inside Ui or changing the difficulty inside model |
Why did we choose Alternative 2: |
||
Aspect 2: |
Storing it inside the enumerations that make up the choices for the settings |
Storing it inside the classes that implement the settings |
Why did we choose Alternative 1: |
3.3. Timer-based Features
Timer
component in action.3.3.1. Implementation Overview - Timer
The Timer
component utilizes the java.util.Timer
API to simulate a stopwatch that runs for each Card in a Game
. It relies on
using Functional Interfaces as callbacks for the TImer to periodically notify other components in the system without directly holding a reference to those
components.
Internally, the Timer
works by using the method java.util.Timer.schedule()
that schedules java.util.TimerTasks
at a fixed rate (every 50ms).
An Observer Pattern is loosly followed between the Timer
and the other components. As opposed to defining an
Observable interface, the AppManager
simply passes in method pointers into the Timer
to callback when an
event is triggered by the Timer
.
To avoid
synchronization issues, all
callbacks to change UI components are forced to run on the JavaFX Application Thread using
Platform.runLater() .
|
The three main events that are currently triggered by the Timer
component which require a callback are:
-
Time has elapsed, callback to
AppManager
to update and display the new timestamp on theUI
. -
Time has run out (reached zero), callback to
AppManager
to skip over to next Card. -
Time has reached a point where
Hints
are to be given to the User, callback toAppManager
to retrieve aHint
and display accordingly on theUI
.
The callbacks for each of these events are implemented as nested Functional Interfaces
within the GameTimer
interface, which is implemented by the GameTimerManager
.
3.3.2. Implementation Overview - Hints
Hints
and its relationships to other components. (Some details omitted)In order to display the Hints
component to the user in a Hangman-esque style, string formatting has to be performed.
-
Each Card contains a
FormattedHintSupplier
that suppliesFormattedHints
ready to be shown to the user. -
Each
FormattedHintSupplier
contains aFormattedHint
that is periodically updated. -
Each
FormattedHintSupplier
contains ajava.util.List
ofHint
to update theFormattedHint
with. -
Each
FormattedHint
maintains achar[]
array that it’stoString()
method uses to format the outputHint
string with. -
Each
Hint
encapsulates aCharacter
and anIndex
which theCharacter
is to be shown in theFormattedHint
.
The Timer
component triggers a request to update Hints
to the AppManager
, who then updates and retrieves the updated FormattedHint
from
the current Game
via the Logic
component.
3.3.3. Flow of Events - Hints
Disabled
This section describes the general sequence of events in the life cycle of a single GameTimer
object with no hints.
GameTimer interface uses a factory method to create GameTimerManager instances. This behavior
is omitted in the above diagram for simplicity.
|
A new GameTimer
instance is created by the AppManager
for every Card of a Game
.
The AppManager
provides information regarding the duration in which the GameTimer
should run for, and whether
Hints
are enabled.
-
UI
component first registers callbacks with theAppManager
. -
When a Game is started,
AppManager
initializes aGameTimer
instance for the first Card. -
AppManager
registers callbacks with theGameTimer
component. -
AppManager
starts theGameTimer
. -
Periodically, the
GameTimer
notifies theAppManager
to update theUI
accordingly. -
AppManager
is notified byGameTimer
, and then notifiesUI
to actually trigger theUI
change. -
GameTimer
finishes counting down (or is aborted). -
AppManager
repeats Steps 2 to 7 for each Card while the Game has not ended.
Using this approach of callbacks provides better abstraction between the UI
and Timer
.
3.3.4. Flow of Events - Hints
Enabled
Hints
feature in action.run()
method of an instance of GameTimerManager
when Hints
are enabled.-
In the diagram as shown above, the internal
Timer
is started whenGameTimerManager
calls the.schedule()
method of its internaljava.util.Timer
, which schedulesTimerTasks
immediately, every 50 milliseconds until thejava.util.Timer
is cancelled. The fieldtimeLeft
is initialized to be the amount of time allowed per Card (in milliseconds), and is updated every 50ms. -
The behavior of
Timer
whenHints
are enabled is largely still the same. -
When
Hints
are enabled,AppManager
initializes aHintTimingQueue
in theGameTimer
for each Card.HintTimingQueue
is a class that contains ajava.util.Queue
of timestamps (in milliseconds).GameTimer
polls from theHintTimingQueue
and checks against these polled timestamps to update theHints
provided periodically.
3.3.5. Design Considerations
There were a few reasons for designing the Timer
and Hints
this way.
Alternative 1 |
Alternative 2 |
|
Aspect 1: |
Holding a reference to Pros: Cons: |
Using Functional Interfaces as Call-backs to notify components indirectly. Pros: Cons: |
Why we chose Alternative 2: |
Alternative 1 |
Alternative 2 |
|
Aspect 2: |
Move retrieval of individual Hint characters and all formatting outside of the Game component completely: Pros: Cons: |
Perform formatting at the lowest level possible, using a Pros: Cons: |
Why we chose Alternative 2: |
3.4. WordBank Management Feature
This section discusses the implementation of WordBank Management in various levels of detail.
This can be split into four complimentary distinct sections.
They are:
-
WordBank’s data structure and its storage system
-
User Commands
-
Drag and drop
-
Revision WordBank
3.4.1. WordBank's data structure and its storage system
Allows developers to use and extend this architecture to streamline their feature implementation.
Allows user to save and load their WordBanks.
The attributes and methods of the following class diagrams have been carefully written.
They describe and explain WordBank's data structure and its storage system in detail.
We start from the lowest level - Card
.
Card
.A Card
contains a unique id
, a word
, a unique meaning
, a set of tags
.
id
: for statistical tracking
word
: answer to the question (meaning)
meaning
: the question that will appear in the game
tags
: optional tags to classify Cards
Cards with the same meaning are duplicates, and is disallowed.
|
Next, the second level - WordBank
.
WordBank
.A WordBank
contains a UniqueCardList
and a unique name
.
UniqueCardList
: prevent duplicate Cards
name
: unique name of the WordBank
WordBank exposes an unmodifiable ObservableList<Card> that can be 'observed'.
The UI can be bound to this list so that the UI automatically updates when the Cards in the list
change.
WordBanks with the same name are duplicates, and is disallowed.
|
Now the third level - WordBankList
WordBankList
.A WordBankList
contains a UniqueWordBankList
.
UniqueWordBankList
: prevent duplicate WordBanks
WordBankList exposes an unmodifiable ObservableList<WordBank> that can be 'observed'.
The UI can be bound to this list so that the UI automatically updates when the WordBanks in the list
change.
|
In Dukemon, there is should only be one WordBankList
, which is created upon Storage
initialisation.
Model
holds a reference to that specific WordBankList
.
WordBank's storage system integration.
WordBankList
within Storage
and Model
.WordBankListStorage
consists of robust methods in which developers can use and extend upon easily.
Alongside with WordBank's data structure, they lay the foundation for the other complementary sections of WordBank Management.
On top of that, they serve as a essential foundation for Dukemon. As such, these data structures and methods were required by the team, to build individual features. (Statistics, Game, Settings)
3.4.2. User Commands
Allows user to customise Cards and group them according to topics (WordBanks).
User commands edits and manipulates Cards and WordBanks heavily.
As mentioned previously, user commands will extend and utilise WordBank's data structure and storage heavily. You can refer to it to enhance your understanding of this implementation. |
Let us first introduce you how these commands are implemented and structured in Logic
.
Logic
with emphasis on CardCommands
and WordBankCommands
.Commands
reside in Logic
. They work on Model
and Storage
through Logic
.
To segregate Cards
according to their function, we distinguished the following:
CardCommands
work on Cards
.
WordBankCommands
work on WordBanks
.
Walkthrough - ImportCommand
.
WordBankCommand
through different components.
Most of these methods utilised can be found in WordBank's data structure and storage class diagrams. The emphasis here is to show how these commands utilise WordBank's data structure and storage. |
We will see the case where an ImportCommand
is valid.
A valid input could be: "import w/bank1, f/~/downloads"
-
Depending on the input, a specific
Command
type is returned byParserManager
. i.e.ImportCommand
. -
Each type of
Command
executes with slight variance.ImportCommand
executes and checks inModel
to check ifWordBankList
already containsWordBank
. -
Relevant information is stored in a specific
CommandResult
and is returned back toLogicManager
. i.e.ImportCommandResult
. -
Each type of
CommandResult
updates the storage with slight variance.ImportCommand
calls theimportWordBank
method. -
JsonWordBankListStorage
contains the abstracted details of how acommandResult
should be handled. ForimportWordBank
method,addWordBank
andsaveWordBank
private methods are called. -
Within
addWordBank
method,WordBank
is added into the underlyingUniqueWordBankList
.
Two synchronisation happens here.
Firstly, asModel
contains the sameWordBankList
, the two list contains synchronised data.
Secondly,WordBankList
exposes an unmodifiableObservableList<WordBank>
that can be 'observed'.UI
was bounded to this list upon initialisation. Hence, it allows the user sees the updatedWordBank
automatically. -
Within
saveWordBank
method, an even lower levelsaveJsonFile
function is called to write to the disk. This is performed through the common class:JsonUtil
. -
It returns back to
LogicManager
, and a success message is passed back toAppManager
, then to theUI
to notify the user.
3.4.3. Drag and drop
Allows user to export their WordBank out of their computer simply by dragging it out of Dukemon.
Likewise, it allows user to import a WordBank file from their computer by dragging it into Dukemon.
Improves user experience by making it easy to share WordBanks with friends.
As mentioned previously, drag and drop will extend and utilise WordBank's data structure and storage heavily. You can refer to it to enhance your understanding of this implementation. |
→ →
From HOME
mode, you can view your WordBanks.
Simply drag and drop a WordBank json file from your computer into the Dukemon GUI.
→ →
Likewise, drag and drop a WordBank
out of the application, into say,
your desktop, or chat applications.
Walkthrough - Drag in.
ImportCommand
and thus the WordBank's storage.LoadBankPanel
is the corresponding class and the FXML file that displays the WordBanks for the user.
It is deeply nested within UI
and only has access to an ObservableList<WordBank>
.
This means it has no way to perform commands
, update model
or update storage
.
-
To work around this, a functional callback is registered within
LoadBankPanel
. -
LoadBankPanel
registers JavaFX’s UI drag detection and drag dropped methods, with the callback. -
After which, the callback essentially performs an
ImportCommand
, to load the WordBank.
It is also noteworthy to mention that, dragging into Dukemon functionality is well guarded against:
-
Not json file format.
-
Json file but data in wrong format.
-
Json file with correct format but contains duplicate cards within.
User receives apt feedback through the command box for different cases.
This is possible with careful exceptions handling within the ImportCommand
itself.
3.4.4. Revision WordBank
Allows user to visit a centralised WordBank that automatically collects cards for revision.
Cards that were answered wrongly are automatically added to this revision bank.
Likewise, cards that were answered correctly during game play are automatically removed from this revision bank.
Improves user learning experience by helping the user to collate cards that require revision.
As mentioned previously, revision WordBank will extend and utilise WordBank's data structure and storage heavily. You can refer to it to enhance your understanding of this implementation. |
Revision WordBank was one of the essential and dominant features we wanted to implement since early development, however
it had to be implemented last because it required multiple components working together.
These components include:
-
AppManager
-
StorageManager
-
LogicManager
-
GameStatistics
Given that well-developed methods reside in each of these components, we then require an overview of revision bank implementation. The activity diagram above is able to detail the thought process and implementation.
-
AppManager
gets first notification that aGame
session has ended. -
StorageManager
creates revision word bank if necessary. -
GameStatistics
gives the required information: correct and wrongCards
. -
LogicManager
manages the processing of theseCards
, with some slight variance depending on situation, into revision bank. -
StorageManager
saves it back into hard disk.
3.4.5. Design Considerations
Alternative 1 |
Alternative 2 |
|
Aspect 1: |
Create classes for both Pros: Cons: |
Create a generic data structure class, and let both Pros: Cons: |
Why did we choose Alternative 1: |
Alternative 1 |
Alternative 2 |
|
Aspect 2: |
Store one single large json file with WordBank names as keys and its WordBank data as values: Pros: Cons: |
Store each WordBank as a json file. Pros: Cons: |
Why did we choose Alternative 2: |
Alternative 1 |
Alternative 2 |
|
Aspect 3: |
All types of commands extends a single abstract class Pros: Cons: |
Distinguishing Pros: Cons: |
Why did we choose Alternative 2: |
Alternative 1 |
Alternative 2 |
|
Aspect 4: |
Updates Pros: Cons: |
Using Functional Interfaces as Call-backs to call an Pros: Cons: |
Why did we choose Alternative 2: |
3.5. Statistics Feature
3.5.1. Implementation
The work of the Statistics component can be neatly captured and explained using a common series of user actions when operating the app.
User action | Statistics work | UI Statistics updates |
---|---|---|
User opens the app. |
User’s |
User is shown their |
User selects a word bank. |
The selected |
|
User opens the selected word bank. |
In open mode, User is shown the |
|
User plays the game. |
A |
|
User finishes the game. |
|
|
We will discuss each step with its implementation details primarily on the statistics work.
1. User opens the app
When the user opens the app, their GlobalStatistics
and WordBankStatisticsList
are loaded into Model
by
MainApp
.
2. User selects a word bank
When the user selects a word bank, the selected WordBankStatistics
from the WordBankStatisticsList
is loaded
into Model.
It is necessary to set the active WordBankStatistics
in the Model
such that when the user opens the WordBank
, the
WordBankStatistics
can be found in Model
and shown in the UI.
3. User opens the selected word bank
In open mode, the user is shown the WordBankStatistics
of the opened word bank, which is set in Model
at step 2.
4. User plays the game
A GameStatisticsBuilder
is used to record user actions during the game.
When the user starts the game by calling a StartCommand
, the GameStatisticsBuilder
is initialized.
Additionally, the GameStatisticsBuilder
is updated with every GuessCommand
or SkipCommand
made during the game.
It receives the timestamp from the GameTimer
which also resides in AppManager
.
5. User finishes the game
When the user finishes the game, a GameStatistics
is created from the GameStatisticsBuilder
. The GameStatistics
is shown to the user in the game result page.
The GameStatistics
is used to update its corresponding WordBankStatistics
, which is then saved to disk.
Additionally, the GlobalStatistics
is also updated and saved to disk.
The work done in step 4 and 5 is executed in AppManager
and the checks to decide what to do are done in
the same method updateGameStatisticsBuilder(CommandResult)
.
AppManager
receives a CommandResult
(Details unrelated to statistics are omitted).3.5.2. Design Considerations
There were some design considerations on implementing the statistics.
Alternative 1 |
Alternative 2 |
|
Aspect 1: |
Store in a separate file from the Example: Pros: Cons: |
Store Pros: Cons: |
Why we decided to choose Alternative 1: |
3.6. Logging
We are using java.util.logging
package for logging. The LogsCenter
class is used to manage the logging levels and logging destinations.
-
The logging level can be controlled using the
logLevel
setting in the configuration file (See Section 3.7, “Configuration”) -
The
Logger
for a class can be obtained usingLogsCenter.getLogger(Class)
which will log messages according to the specified logging level -
Currently log messages are output through:
Console
and to a.log
file.
Logging Levels
-
SEVERE
: Critical problem detected which may possibly cause the termination of the application -
WARNING
: Can continue, but with caution -
INFO
: Information showing the noteworthy actions by the App -
FINE
: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
3.7. Configuration
Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json
).
Appendix A: Product Scope
Target user profile:
-
Students in formal educational institutions.
-
Students in informal educational contexts.
-
Users who are familiar with the keyboard, able to type fast.
-
Users who enjoy interactive learning.
-
Users who are famiiar and used to CLI-based apps.
Value proposition: Making Learning and Memorization Game-like, Fun and Engaging.
Appendix B: User Stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
|
teacher |
add, edit, and delete questions in the word banks |
make corrections on what my students are supposed to learn |
|
teacher |
give customised word banks and definitions |
can let my students practice specific problems. |
|
user |
list all my word banks |
|
|
user |
give titles to word banks |
recognise them better |
|
user |
delete word banks |
free up some memory when I don’t need it anymore |
|
user |
see the content of the word bank |
study beforehand/make changes |
|
young student |
trivia questions to be gamified |
enjoy the process |
|
student |
create my own question banks |
tailor fit to my learning |
|
computer science student |
have a manual of the commands available |
refer to them when I am lost |
|
frequent user |
easily access my most recently attempted question sets |
can quickly resume my revision |
|
studious student |
set and complete goals |
have something to work towards |
|
student |
see my test statistics |
track my progress/improvement |
|
student |
choose different kinds of time constraints |
can simulate exam conditions |
|
student |
categorise my question sets |
easily look for relevant materials |
|
student |
mark question sets as important/urgent |
know how to prioritise my revision |
|
module coordinator |
export lessons |
send to their students |
|
student |
share and compare my results with my classmates |
know where I stand |
|
student |
partition the trivia |
attempt questions that I’m comfortable with |
|
weak student |
have the option to see hints |
won’t get stuck all the time |
|
computer science student |
practise typing bash commands into the CLI |
strengthen my bash skills |
|
teacher |
export statistics |
can compare performance across different students |
|
computer science student |
customize my “terminal” |
changing themes/ background/ font size/ font colour, so that I feel comfortable working on it |
|
teacher |
protect tests with passwords |
let my students do them in lessons together when password is released |
|
teacher |
protect the files |
doesn’t get tampered when distributing to students |
|
student |
have smaller sized files |
have more space on my computer |
Appendix C: Use Cases
(For all use cases below, the System is the Dukemon
and the Actor is the User
, unless specified otherwise)
Use case: Add a Card
MSS
-
User selects and opens a WordBank.
-
Dukemon shows a list of Cards.
-
User adds Word and Meaning pair.
-
Dukemon adds a Card into WordBank.
Use case ends.
Use case: Play a game
MSS
-
User selects and opens a WordBank.
-
Dukemon shows a list of Cards.
-
User starts a game.
-
Dukemon shows the Meaning of a Card.
-
User guesses Word of the corresponding Card.
Steps 4-5 are repeated till all Cards are played.
-
Game ends. Dukemon shows statistics of game.
Use case ends.
Extentions
2a. User opens settings
2a1. User changes hints to off.
Use case resumes from step 3.
Appendix D: Non Functional Requirements
-
Compatible any mainstream OS as long as it has Java
11
or above installed. -
User has above average typing speed for regular English text (i.e. not code, not system admin commands); able to accomplish most of the tasks faster using commands than using the mouse.
-
Users can export and import their wordbanks or Statistics.
-
Feedback shown to user must be fast (< 0.2s) especially during a Game.
Appendix E: Glossary
- Mainstream OS
-
Windows, Linux, Unix, OS-X, Ubuntu and etc.
- WordBank
-
Data structure that contains a list of several Cards.
- Card
-
Analogue to a physical Flashcard- contains a Word and a Meaning.
- Word
-
The component of a Card that is to be guessed by the user during a Game.
- Meaning
-
The meaning represented by the Word of a Card; is shown to the user during a Game.
- Game
-
A game session that runs on a specific WordBank of Cards.
- Callback
-
A piece of executable code that is passed as an argument to other another code that is expected to callback (execute) the argument at a given time. (Adapted from Wikipedia)
- GUI
-
Graphical User Interface, the primary interface of which Dukemon's feedback to the user can be seen.
Appendix F: Instructions for Manual Testing.
Given below are instructions for the testing of Dukemon manually.
The below instruction are only intended as starting points for testers to work on; testers are expected to do more exploratory testing. |
F.1. Initial Start-up and Editing of a WordBank.
-
Initial clean launch
-
Download the
Dukemon.jar
file and copy into an empty folder -
Double-click the
.jar
file
Expected: GUI appears with some sample WordBanks. Default window size may not be optimum.
-
-
Selecting and viewing a default WordBank
-
Enter
select trivia
to choose the Trivia WordBank.
Expected: GUI shows feedback "Selected word bank: trivia" in the ResultDisplay. -
Enter
open
.
Expected: GUI switches to Open mode, Cards belonging to Trivia are shown.
-
-
Adding a new Card to a WordBank
-
Enter
add w/Damith m/Lecturer of CS2103T t/Easy
Expected: Feedback shown that a new Card with the above details are added. List of Cards on GUI’s right panel reflects the addition of a new Card. -
Enter
exit
and relaunch Dukemon. Enterselect triva
and thenopen
upon restarting
Expected: The new Card ("Damith") added previously is correctly stored in the Triva WordBank.
-
F.2. Starting and Terminating a Game session without Hints.
-
Starting a Game after a target WordBank has been selected.
-
Prerequisite: A WordBank with 3 or more Cards has been selected using the
select
command. -
Enter
settings
Expected: GUI switches to Settings mode, and various configurable parameters are listed. (ie. Theme, Hints etc) -
Enter
hints off
Expected: GUI feedback indicates that Hints are turned OFF -
Enter
start easy
Expected: GUI switches to Game mode with two panels stacked on top of each other. A 15s-countdown Timer is started at the top right of the GUI. A random Meaning of a Card belonging to the selected WordBank is shown on the upper panel. The bottom panel indicated that there are noHints
. When time is running out, the Timer region changes color. -
Enter
stop
(before all Cards are shown)
Expected: Game stops, feedback informs that "Game has been forcibly stopped". Timer stops running and disappears. -
Enter
guess abc
to attempt to make aGuess
after Game ends.
Expected: Noguess
is processed, feedback indicates that "This command does not work right now" -
Enter
skip
to attempt to skip over to another Card.
Expected: No skipping over occurs, feedback indicates that "This command does not work right now"
-
Other possible Difficulties to start with are start medium and start hard , allowing 10s and 5s
respectively for each Card. Other invalid commands will yield similar results after stopping the Game.
|
F.3. Importing and Exporting a WordBank using Drag-and-Drop.
-
Exporting a WordBank using Drag-and-Drop
-
Prerequisite: There exists at least one WordBank in Dukemon's data storage. Let W be any WordBank that currently exists in Dukemon's storage.
-
(Single) Click on Panel on the right half of the GUI that indicates WordBank W
Expected: WordBank W's panel on the GUI becomes highlighted. -
Use mouse cursor to click-and-drag highlighted panel outside of Dukemon into an empty folder.
Expected: A file with name W.json appears in the folder where it was dragged into.
-
-
Importing a WordBank using Drag-and-Drop
-
Prerequisite: WordBank to be imported is in valid .json format, and all of its Cards are valid within constraints. Let V be the WordBank to be imported.
-
(Single) Click on V.json in your file directory. Start Dukemon and drag V.json into the panel on the right side (where the list of WordBanks is shown.)
Expected: WordBank V is successfully added into the list of WordBanks, feedback indicates location where file was imported from.
-
F.4. Changing and Saving Dukemon Settings
-
Changing Color Theme of Dukemon
-
Prerequisite: A WordBank has already been selected from the Home mode, and no Game session is in progress.
-
Enter
settings
Expected: GUI switches to the Settings screen. -
Enter
theme light
Expected: GUI’s overall color theme switches to a lighter color. -
Enter
exit
, then restart Dukemon (do not change any configuration or data files)
Expected: GUI’s color theme remains light after restarting.
-