Accurate Guesser
Java desktop guessing game (GUI).
- ROLE
- Concept
- STATUS
- Published
OVERVIEW
Accurate Guesser is a classic number guessing game built with Java Swing that demonstrates fundamental desktop application development principles. Players attempt to guess a randomly generated number, receiving intelligent feedback after each attempt. Beyond being a fun game, this project serves as a comprehensive exploration of event-driven architecture, state management, and GUI programming patterns in Java.
The application showcases clean separation between game logic and presentation, implements thorough input validation, and provides clear user feedback through responsive UI. It shows how a simple concept can teach foundational programming patterns.
ARRIVED AS
Fun UI with simple state machine.
Accurate Guesser is a classic number guessing game built with Java Swing, designed to demonstrate fundamental GUI programming concepts and event-driven architecture. The player tries to guess a randomly generated number within a range, receiving feedback after each attempt. This project showcases clean separation of concerns between UI presentation, game logic, and state management, all essential principles for desktop application development.
WHAT I BUILT
- 01Swing GUI; input validation; scoring and replay.
WHAT CHANGED
- Lightweight demo of event-driven UI.
Decisions, with the cost of each.
A decision without its trade-off is marketing. Each row says what was chosen, why, and what it gave up.
Swing over JavaFX for UI framework
Chose Swing for its maturity, extensive documentation, and lower learning curve for fundamental GUI concepts. While JavaFX offers more modern features, Swing's simplicity made it ideal for demonstrating core event-driven programming patterns without framework-specific abstractions.
State machine for game flow control
Implemented explicit state management (INITIAL, PLAYING, FINISHED) to prevent invalid operations and ensure predictable behavior. This prevents edge cases like submitting guesses after winning or starting multiple games simultaneously. The state machine makes transitions explicit and testable.
Input validation at UI layer
Performed validation in InputPanel before reaching GameEngine to provide immediate user feedback and maintain clean separation of concerns. The UI handles presentation-level validation (numeric input, range checks), while GameEngine focuses purely on game logic. This reduces coupling and improves user experience.
Modal dialogs for game state changes
Used JOptionPane for win/loss notifications and replay confirmations to create clear breakpoints in game flow. Modal dialogs ensure users acknowledge outcomes before proceeding, preventing accidental game resets and making state transitions explicit.
The part that mattered.
The numbers behind the work, and the code that produced them.
public class GameEngine {
private int secretNumber;
private int attempts;
private GameState state;
private final int MIN_VALUE = 1;
private final int MAX_VALUE = 100;
public void startNewGame() {
this.secretNumber = (int)(Math.random() * MAX_VALUE) + MIN_VALUE;
this.attempts = 0;
this.state = GameState.PLAYING;
}
public GuessResult validateGuess(int guess) {
if (state != GameState.PLAYING) {
throw new IllegalStateException("No active game");
}
attempts++;
if (guess == secretNumber) {
state = GameState.FINISHED;
return new GuessResult(ResultType.CORRECT, attempts);
} else if (guess < secretNumber) {
return new GuessResult(ResultType.TOO_LOW, attempts);
} else {
return new GuessResult(ResultType.TOO_HIGH, attempts);
}
}
}
public class GameUI extends JFrame {
private GameEngine engine;
private JTextField guessInput;
private JLabel feedbackLabel;
private JLabel attemptsLabel;
private void initializeListeners() {
submitButton.addActionListener(e -> handleGuessSubmission());
newGameButton.addActionListener(e -> startNewGame());
}
private void handleGuessSubmission() {
try {
int guess = Integer.parseInt(guessInput.getText());
if (guess < 1 || guess > 100) {
showError("Please enter a number between 1 and 100");
return;
}
GuessResult result = engine.validateGuess(guess);
updateFeedback(result);
if (result.getType() == ResultType.CORRECT) {
handleGameWin(result.getAttempts());
}
} catch (NumberFormatException ex) {
showError("Invalid input. Please enter a valid number.");
} catch (IllegalStateException ex) {
showError("Start a new game first!");
}
}
}
// Advanced input validation preventing non-numeric entry
public class NumericDocumentFilter extends DocumentFilter {
@Override
public void insertString(FilterBypass fb, int offset,
String string, AttributeSet attr)
throws BadLocationException {
if (string != null && string.matches("\\d+")) {
super.insertString(fb, offset, string, attr);
}
}
@Override
public void replace(FilterBypass fb, int offset, int length,
String text, AttributeSet attrs)
throws BadLocationException {
if (text != null && text.matches("\\d*")) {
super.replace(fb, offset, length, text, attrs);
}
}
}
// Applied to input field
((AbstractDocument)guessInput.getDocument())
.setDocumentFilter(new NumericDocumentFilter());
✓ LEARNED
Event-Driven Architecture Fundamentals
Building with Swing taught the core principles of event-driven programming: loose coupling through listeners, callback-based control flow, and asynchronous user interactions. Understanding how ActionListeners decouple user actions from business logic became foundational knowledge applicable to modern frameworks like React and Vue, where similar event-driven patterns dominate.
State Management Complexity
Even a simple game reveals state management challenges. Without explicit state tracking (INITIAL, PLAYING, FINISHED), edge cases emerged: users submitting guesses before initialization, starting multiple games simultaneously, or continuing after winning. This experience highlighted why state management libraries (Redux, MobX) exist in complex applications, because manual state tracking does not scale.
Separation of Concerns in Practice
Implementing MVC in Swing demonstrated why architectural patterns matter. Initially mixing UI code with game logic created brittle code resistant to changes. Refactoring to separate GameEngine from GameUI made testing trivial, enabled UI redesigns without touching logic, and clarified responsibilities. This lesson applies universally across software development.
Input Validation Layers
Learned to implement defense in depth: UI-level validation for immediate feedback (DocumentFilter for numeric input), application-level validation for business rules (range checking), and domain-level validation in GameEngine (state checks). Each layer serves distinct purposes and prevents different failure modes. Relying on a single validation point creates fragility.
User Experience in Desktop Applications
Discovered that technical correctness doesn't equal good UX. Early versions worked but felt clunky: no visual feedback during input, unclear win conditions, and abrupt state changes. Adding color-coded hints, smooth transitions, confirmation dialogs, and immediate input validation transformed the experience. Small UI details have outsized impact on perceived quality.