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

  1. 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.

Game Engine Core Logicjava
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);
        }
    }
}
Swing Event Handler Integrationjava
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!");
        }
    }
}
Input Validation with DocumentFilterjava
// 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.