Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/javadoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ jobs:

- name: Generate JavaDoc
run: ./gradlew javadoc

- name: Build TeaVM (JavaScript compilation)
run: ./teavm.sh

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ NumberGuessingGame-macos/
NumberGuessingGame-linux/
NumberGuessingGame-windows.zip
NumberGuessingGame-macos.zip
NumberGuessingGame-linux.tar.gz
NumberGuessingGame-linux.tar.gz

# TeaVM generated JavaScript (build artifact)
teavm/classes.js
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
A simple number guessing game where you try to guess a randomly generated number. The game features both a user-friendly graphical interface (GUI) and a classic console mode. The GUI provides visual feedback with color-coded hints and tracks your progress in real-time.

**Features:**
- 🎮 Modern Swing-based GUI (default)
- 💻 Classic console mode (use `--console` flag)
- 🎯 Visual feedback with color coding
- 📊 Real-time guess counter
- ⌨️ Keyboard shortcuts for quick navigation
- 🌍 Cross-platform compatible (Windows, macOS, Linux)
- Swing-based GUI (default)
- Console mode (use `--console` flag)
- Web browser version (available on GitHub Pages)
- High score tracking with usernames
- Persistent score storage
- Cross-platform

## Installation & Running

Expand Down Expand Up @@ -62,7 +62,7 @@ Download the `archive.zip` from the [latest release](https://github.com/project5

#### Requirements

- Java 8 or higher (may require Java 17+ in future versions)
- Java 11 or higher

#### How to Run

Expand All @@ -86,10 +86,13 @@ The game now features both a graphical user interface (GUI) and a console mode.
- Orange text indicates your guess was too high
- Green text indicates you guessed correctly!
5. The number of guesses is displayed and updated in real-time
6. When you guess correctly, click "New Game" to play again
7. Use the menu bar for additional options:
6. When you guess correctly, you'll be prompted to enter your username
7. After entering your username, your score will be saved and the top high scores will be displayed
8. Click "New Game" to play again
9. Use the menu bar for additional options:
- File → New Game (Ctrl+N): Start a new game
- File → Exit: Close the application
- View → High Scores: View the top high scores
- Help → About: View game information

#### Console Mode
Expand All @@ -108,6 +111,18 @@ In console mode:
2. The game will tell you if your guess is too high or too low
3. Keep guessing until you find the correct number
4. The game will display how many guesses it took you
5. Enter your username when prompted to save your score
6. The top high scores will be displayed after saving your score

### High Scores

The game automatically tracks high scores (games won with the fewest guesses). High scores are stored persistently in your home directory at `~/.numberguessinggame/highscores.properties`.

- The top 10 scores are kept
- Scores are sorted by the number of guesses (fewest is best)
- Each score includes the username and number of guesses
- High scores persist across game sessions
- View high scores from the GUI menu (View → High Scores) or after completing a game

## Development

Expand Down
17 changes: 16 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ plugins {

//Spotless
id 'com.diffplug.spotless' version '8.0.0'

// TeaVM plugin for JavaScript compilation
id 'io.github.zebalu.teavm-gradle-plugin' version '1.0.0'
}

repositories {
Expand All @@ -14,6 +17,9 @@ repositories {
dependencies {
// This dependency is used by the application.
implementation libs.guava

// TeaVM JSO APIs for web development (needed at compile time for WebGUI)
compileOnly 'org.teavm:teavm-jso-apis:0.10.2'
}

testing {
Expand All @@ -35,7 +41,10 @@ java {
}

tasks.withType(JavaCompile) {
options.release.set(8)
// Changed from 8 to 11 to support TeaVM for web deployment.
// TeaVM 0.10.2+ requires Java 11 as minimum target.
// This change affects the minimum JRE required to run the JAR.
options.release.set(11)
}

application {
Expand All @@ -61,3 +70,9 @@ jar {
)
}
}

// TeaVM configuration for compiling Java GUI to JavaScript
teavm {
// Main class to compile
mainClass = 'io.github.project516.NumberGuessingGame.WebGUI'
}
85 changes: 78 additions & 7 deletions app/src/main/java/io/github/project516/NumberGuessingGame/GUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class GUI extends JFrame {
private RandomNumber randomGenerator;
private CheckGuess guessChecker;
private GameInfo gameInfo;
private HighScoreManager highScoreManager;

// UI Components
private JTextField guessField;
Expand All @@ -38,6 +39,15 @@ public GUI() {
guessChecker = new CheckGuess();
gameInfo = new GameInfo();

// Initialize high score manager
try {
highScoreManager = new HighScoreManager();
} catch (Exception e) {
System.err.println(
"Warning: Could not initialize high score system: " + e.getMessage());
highScoreManager = null;
}

// Set up the frame
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 400);
Expand Down Expand Up @@ -76,6 +86,14 @@ private void createMenuBar() {
exitItem.addActionListener(e -> System.exit(0));
fileMenu.add(exitItem);

// View menu
JMenu viewMenu = new JMenu("View");
viewMenu.setMnemonic(KeyEvent.VK_V);

JMenuItem highScoresItem = new JMenuItem("High Scores", KeyEvent.VK_H);
highScoresItem.addActionListener(e -> showHighScores());
viewMenu.add(highScoresItem);

// Help menu
JMenu helpMenu = new JMenu("Help");
helpMenu.setMnemonic(KeyEvent.VK_H);
Expand All @@ -85,6 +103,7 @@ private void createMenuBar() {
helpMenu.add(aboutItem);

menuBar.add(fileMenu);
menuBar.add(viewMenu);
menuBar.add(helpMenu);

setJMenuBar(menuBar);
Expand Down Expand Up @@ -223,13 +242,8 @@ private void submitGuess() {
newGameButton.setVisible(true);
newGameButton.requestFocus();

// Show congratulations dialog
String message =
String.format(
"You guessed the number in %d %s!",
numberOfGuesses, numberOfGuesses == 1 ? "guess" : "guesses");
JOptionPane.showMessageDialog(
this, message, "Congratulations!", JOptionPane.INFORMATION_MESSAGE);
// Handle high score
handleHighScore();
}

guessField.setText("");
Expand Down Expand Up @@ -270,6 +284,63 @@ private void showAboutDialog() {
this, message, "About Number Guessing Game", JOptionPane.INFORMATION_MESSAGE);
}

/**
* Handles high score submission after a successful game. Prompts the user for their username
* and saves the score if applicable.
*/
private void handleHighScore() {
if (highScoreManager == null) {
// Show basic congratulations dialog if high score system is unavailable
String message =
String.format(
"You guessed the number in %d %s!",
numberOfGuesses, numberOfGuesses == 1 ? "guess" : "guesses");
JOptionPane.showMessageDialog(
this, message, "Congratulations!", JOptionPane.INFORMATION_MESSAGE);
return;
}

// Prompt for username
String username =
JOptionPane.showInputDialog(
this,
String.format(
"You guessed the number in %d %s!\n\nEnter your username (1-20 characters):",
numberOfGuesses, numberOfGuesses == 1 ? "guess" : "guesses"),
"Congratulations!",
JOptionPane.QUESTION_MESSAGE);

// Validate username
if (username != null && Username.isValid(username)) {
highScoreManager.addHighScore(username, numberOfGuesses);
showHighScores();
} else if (username != null) {
JOptionPane.showMessageDialog(
this, "Invalid username. Score not saved.", "Error", JOptionPane.ERROR_MESSAGE);
}
}

/** Displays the top high scores in a dialog. */
private void showHighScores() {
if (highScoreManager == null) {
return;
}

java.util.List<HighScore> topScores = highScoreManager.getTopHighScores(10);
if (topScores.isEmpty()) {
return;
}

StringBuilder sb = new StringBuilder();
sb.append("Top High Scores\n\n");
for (int i = 0; i < topScores.size(); i++) {
sb.append(String.format("%d. %s\n", i + 1, topScores.get(i)));
}

JOptionPane.showMessageDialog(
this, sb.toString(), "High Scores", JOptionPane.INFORMATION_MESSAGE);
}

/**
* Gets the version of the application.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
package io.github.project516.NumberGuessingGame;

import java.util.List;

/**
* Contains the main game logic for the Number Guessing Game. This class manages the game loop, user
* input, and guess validation.
*/
public class GameLogic {
private HighScoreManager highScoreManager;

/** Constructs a new GameLogic instance and initializes the high score manager. */
public GameLogic() {
try {
highScoreManager = new HighScoreManager();
} catch (Exception e) {
System.err.println(
"Warning: Could not initialize high score system: " + e.getMessage());
highScoreManager = null;
}
}

/**
* Runs the main game loop. Generates a random number and prompts the user to guess it. Provides
* feedback on each guess and tracks the number of attempts.
Expand All @@ -30,9 +45,36 @@ void game(ScannerHelper scan) {
} else {
numOfGuesses++;
System.out.println("Took you " + numOfGuesses + " guesses!");

// Handle high score
if (highScoreManager != null) {
String username = Username.promptUsername(scan);
if (highScoreManager.addHighScore(username, numOfGuesses)) {
System.out.println("Score saved!");
}
displayHighScores();
}
break;
}
numOfGuesses++;
}
}

/** Displays the top high scores to the console. */
private void displayHighScores() {
if (highScoreManager == null) {
return;
}

List<HighScore> topScores = highScoreManager.getTopHighScores(5);
if (topScores.isEmpty()) {
return;
}

System.out.println("\n=== Top High Scores ===");
for (int i = 0; i < topScores.size(); i++) {
System.out.println((i + 1) + ". " + topScores.get(i));
}
System.out.println("=======================\n");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.github.project516.NumberGuessingGame;

/**
* Represents a single high score entry in the Number Guessing Game. Each entry contains a username
* and the number of guesses it took to win the game.
*/
public class HighScore implements Comparable<HighScore> {
private final String username;
private final int numberOfGuesses;

/**
* Constructs a new HighScore entry.
*
* @param username the username of the player
* @param numberOfGuesses the number of guesses it took to win
* @throws IllegalArgumentException if username is null or empty, or if numberOfGuesses is less
* than 1
*/
public HighScore(String username, int numberOfGuesses) {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("Username cannot be null or empty");
}
if (numberOfGuesses < 1) {
throw new IllegalArgumentException("Number of guesses must be at least 1");
}
this.username = username.trim();
this.numberOfGuesses = numberOfGuesses;
}

/**
* Gets the username for this high score entry.
*
* @return the username
*/
public String getUsername() {
return username;
}

/**
* Gets the number of guesses for this high score entry.
*
* @return the number of guesses
*/
public int getNumberOfGuesses() {
return numberOfGuesses;
}

/**
* Compares this high score to another. High scores are ordered by number of guesses (ascending)
* so that lower scores (fewer guesses) come first.
*
* @param other the other high score to compare to
* @return a negative integer, zero, or a positive integer as this high score is less than,
* equal to, or greater than the specified high score
*/
@Override
public int compareTo(HighScore other) {
return Integer.compare(this.numberOfGuesses, other.numberOfGuesses);
}

@Override
public String toString() {
return username + ": " + numberOfGuesses + " guess" + (numberOfGuesses == 1 ? "" : "es");
}
}
Loading
Loading