Solution Submission
Overview
The solution submission system allows players to accuse a character as the culprit of the mystery. This feature implements a complete workflow from submission prerequisites validation to the final reveal of the solution, with proper state management throughout the MVC architecture.
Implementation
Model Layer
The model layer defines the core data structures for tracking submission state and validation results through enumerations.
SubmissionState Enum
The SubmissionState enum tracks the current state of the player's accusation submission:
enum SubmissionState:
case NotSubmitted
case Submitting(character: Character)
case Submitted(result: ValidationResult)- NotSubmitted: Initial state when no accusation has been made
- Submitting: Transient state while processing an accusation for a specific character
- Submitted: Final state containing the validation result
ValidationResult Enum
The ValidationResult enum represents the outcome of an accusation validation:
enum ValidationResult:
case PrerequisitesNotMet
case CorrectSolution(culprit: Character, motive: String)
case IncorrectSolution(
accusedCharacter: Character,
actualCulprit: Character,
motive: String
)- PrerequisitesNotMet: Player attempted to accuse before meeting the requirements
- CorrectSolution: Player correctly identified the culprit, including the motive
- IncorrectSolution: Player accused the wrong character, revealing both the accusation and the actual solution
SolutionConfig
Configuration constants for submission prerequisites:
object SolutionConfig:
val PrerequisiteCoverageThreshold: Double = 1.0
val TimeElapsedThreshold: Double = 0.85- PrerequisiteCoverageThreshold: Required coverage (100%) of prerequisite knowledge to accuse
- TimeElapsedThreshold: Minimum time elapsed (85%) before allowing accusation
Validation Logic
The validation is performed by checking if the accused character matches the actual culprit:
object ValidationResult:
def validateSolution(
accusedCharacter: Character,
solution: Solution
): ValidationResult =
if accusedCharacter == solution.culprit then
CorrectSolution(solution.culprit, solution.motive)
else
IncorrectSolution(accusedCharacter, solution.culprit, solution.motive)Controller Layer
The GameBoardController manages the submission workflow and prerequisite validation.
Prerequisite Validation
The canAccuse method determines if a player can make an accusation by checking two conditions (OR logic):
override def canAccuse: Boolean = (model.state.investigativeCase, model.state.currentGraph, model.state.timer) match
case (Some(currentCase), Some(graph), Some(timer)) =>
val prerequisitesMet =
graph.coverage(currentCase.solution.prerequisite) >= SolutionConfig.PrerequisiteCoverageThreshold
val timeThresholdMet = model.getRemainingTime match
case Some(remaining) =>
val elapsed = timer.totalDuration - remaining
val elapsedPercentage = elapsed.toMillis.toDouble / timer.totalDuration.toMillis.toDouble
elapsedPercentage >= SolutionConfig.TimeElapsedThreshold
case None => false
prerequisitesMet || timeThresholdMet
case _ => falseThe method leverages existing functionality:
- Coverage calculation: Uses the
coveragemetric fromMetricto compare the player's knowledge graph against the case's prerequisite graph - Time tracking: Uses the
Timercomponent to calculate elapsed time percentage
Accusation Submission
The submitAccusation method orchestrates the submission process:
override def submitAccusation(character: Character): ValidationResult =
model.updateState(_.withSubmissionState(SubmissionState.Submitting(character)))
val result = validateSubmission(character)
model.updateState(_.withSubmissionState(SubmissionState.Submitted(result)))
model.state.timer.foreach(_.stop())
resultSteps:
- Update state to
Submittingwith the accused character - Validate if prerequisites are met
- If met, validate the accusation; otherwise return
PrerequisitesNotMet - Update state to
Submittedwith the result - Stop the game timer
- Return the validation result
Available Suspects
The controller provides the list of characters that can be accused (all except the victim):
override def getAvailableSuspects: Set[Character] =
model.state.investigativeCase.map(_.characters).getOrElse(Set.empty).filter(character =>
!character.role.equals(CaseRole.Victim)
)View Layer
The view layer implements the accusation and game end popups in GameBoardScene. Two modal popups are created:
- Accusation Popup: Displays a dropdown with available suspects and allows the player to submit their accusation
- Game End Popup: Shows the final result (win/lose message, culprit name, and motive)
Workflow
- Player clicks Accuse button: The view checks if
controller.canAccuseis true - Accusation popup appears: Shows dropdown with available suspects (all characters except victim)
- Player selects character and submits:
- Controller updates state to
Submitting - Controller validates prerequisites
- Controller validates accusation against solution
- Controller updates state to
Submittedwith result - Controller stops timer
- Controller updates state to
- Game end popup appears: Shows win/lose message, culprit, and motive
- Game ends: Timer is stopped, no further gameplay possible
Design Decisions
Reusing Existing Components
The implementation leverages existing model components:
- Coverage metric: The
coverageextension method fromMetric.scalacalculates how much of the prerequisite graph the player has discovered - Timer: The existing
Timercomponent tracks elapsed time without modification - GameState: Extended with
submissionStatefield to track the submission workflow
State Management
The SubmissionState enum provides clear state tracking:
- Prevents multiple simultaneous submissions
- Allows the View to display submission progress
Prerequisites Logic
The OR condition for prerequisites (prerequisitesMet || timeThresholdMet) ensures:
- Skilled players can accuse early by discovering all clues
- All players eventually get a chance to accuse as time runs out
- The game remains accessible while rewarding thorough investigation
Complete Class Diagram
A comprehensive class diagram illustrating the solution submission feature within the MVC architecture:
Note on UML Diagram: the diagrams focus on the essential architectural elements and key methods that define the contracts between components. Helper methods, factory object methods, and internal implementation details are selectively omitted to emphasize the design patterns and polymorphic relationships. Complete method signatures and implementations can be found in the source code documentation.
Figure 1: Solution submission complete class diagram