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
4 changes: 2 additions & 2 deletions .github/workflows/javadoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ jobs:
- name: Generate JavaDoc
run: ./gradlew javadoc

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

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ NumberGuessingGame-windows.zip
NumberGuessingGame-macos.zip
NumberGuessingGame-linux.tar.gz

# TeaVM generated JavaScript (build artifact)
teavm/classes.js
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ A simple number guessing game where you try to guess a randomly generated number
**Features:**
- 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
Expand Down Expand Up @@ -61,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 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'
}
214 changes: 214 additions & 0 deletions app/src/main/java/io/github/project516/NumberGuessingGame/WebGUI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package io.github.project516.NumberGuessingGame;

import org.teavm.jso.browser.Window;
import org.teavm.jso.dom.html.*;

/**
* Web-based GUI for the Number Guessing Game using TeaVM. This class provides a browser-native
* interface that reuses the core game logic from the existing classes. Created specifically for
* TeaVM compilation to JavaScript for GitHub Pages deployment.
*/
public class WebGUI {
// Game state - reusing existing game logic classes
private int targetNumber;
private int numberOfGuesses;
private RandomNumber randomGenerator;
private CheckGuess guessChecker;

// DOM elements
private HTMLDocument document;
private HTMLInputElement guessInput;
private HTMLButtonElement submitButton;
private HTMLButtonElement newGameButton;
private HTMLElement feedbackElement;
private HTMLElement guessCountElement;

/**
* Main entry point for the web application. TeaVM will call this method when the page loads.
*
* @param args command line arguments (not used in web context)
*/
public static void main(String[] args) {
new WebGUI().initialize();
}

/** Initializes the web GUI and starts a new game. */
public void initialize() {
// Initialize game objects - reusing existing classes
randomGenerator = new RandomNumber();
guessChecker = new CheckGuess();

// Get the HTML document
document = Window.current().getDocument();

// Create the game UI
createGameUI();

// Start a new game
startNewGame();
}

/** Creates the game UI elements in the HTML document. */
private void createGameUI() {
// Get or create the game container
HTMLElement gameContainer = (HTMLElement) document.getElementById("game-container");
if (gameContainer == null) {
gameContainer = (HTMLElement) document.createElement("div");
gameContainer.setId("game-container");
document.getBody().appendChild(gameContainer);
}

// Clear existing content
gameContainer.setInnerHTML("");

// Create title
HTMLElement title = (HTMLElement) document.createElement("h1");
title.setInnerHTML("Number Guessing Game");
gameContainer.appendChild(title);

// Create instructions
HTMLElement instructions = (HTMLElement) document.createElement("p");
instructions.setInnerHTML("Guess a number between 1 and 100!");
gameContainer.appendChild(instructions);

// Create guess count display
guessCountElement = (HTMLElement) document.createElement("p");
guessCountElement.setId("guess-count");
guessCountElement.setInnerHTML("Number of guesses: 0");
gameContainer.appendChild(guessCountElement);

// Create input field
guessInput = (HTMLInputElement) document.createElement("input");
guessInput.setType("number");
guessInput.setAttribute("min", "1");
guessInput.setAttribute("max", "100");
guessInput.setAttribute("placeholder", "Enter your guess");
guessInput.setId("guess-input");
gameContainer.appendChild(guessInput);

// Create submit button
submitButton = (HTMLButtonElement) document.createElement("button");
submitButton.setInnerHTML("Submit Guess");
submitButton.setId("submit-button");
submitButton.addEventListener("click", evt -> submitGuess());
gameContainer.appendChild(submitButton);

// Allow Enter key to submit
guessInput.addEventListener(
"keypress",
evt -> {
if ("Enter".equals(((HTMLKeyboardEvent) evt).getKey())) {
submitGuess();
}
});

// Create feedback area
feedbackElement = (HTMLElement) document.createElement("p");
feedbackElement.setId("feedback");
feedbackElement.setInnerHTML(" ");
gameContainer.appendChild(feedbackElement);

// Create new game button (initially hidden)
newGameButton = (HTMLButtonElement) document.createElement("button");
newGameButton.setInnerHTML("New Game");
newGameButton.setId("new-game-button");
newGameButton.getStyle().setProperty("display", "none");
newGameButton.addEventListener("click", evt -> startNewGame());
gameContainer.appendChild(newGameButton);
}

/** Starts a new game by resetting the game state. */
private void startNewGame() {
// Generate new target number using existing RandomNumber class
targetNumber = randomGenerator.number(100);
numberOfGuesses = 0;

// Reset UI
guessInput.setValue("");
guessInput.setDisabled(false);
submitButton.setDisabled(false);
newGameButton.getStyle().setProperty("display", "none");
feedbackElement.setInnerHTML(" ");
feedbackElement.setClassName("");
updateGuessCount();

// Focus on input
guessInput.focus();
}

/** Processes the user's guess and updates the UI with feedback. */
private void submitGuess() {
String input = guessInput.getValue().trim();

if (input.isEmpty()) {
showFeedback("Please enter a number!", "error");
return;
}

int guess;
try {
guess = Integer.parseInt(input);
} catch (NumberFormatException e) {
showFeedback("Invalid input! Please enter a valid number.", "error");
guessInput.setValue("");
return;
}

// Validate guess using existing CheckGuess class
try {
guessChecker.check(guess);
} catch (IllegalArgumentException e) {
showFeedback(e.getMessage(), "error");
guessInput.setValue("");
return;
}

numberOfGuesses++;
updateGuessCount();

// Check the guess
if (guess > targetNumber) {
showFeedback("Too high! Try a lower number.", "high");
} else if (guess < targetNumber) {
showFeedback("Too low! Try a higher number.", "low");
} else {
// Correct guess!
showFeedback(
String.format(
"🎉 Congratulations! You guessed it in %d %s!",
numberOfGuesses, numberOfGuesses == 1 ? "guess" : "guesses"),
"success");
guessInput.setDisabled(true);
submitButton.setDisabled(true);
newGameButton.getStyle().setProperty("display", "block");
newGameButton.focus();
}

guessInput.setValue("");
guessInput.focus();
}

/**
* Updates the feedback message and applies the appropriate CSS class.
*
* @param message the feedback message to display
* @param cssClass the CSS class to apply (error, high, low, success)
*/
private void showFeedback(String message, String cssClass) {
feedbackElement.setInnerHTML(message);
feedbackElement.setClassName("feedback-" + cssClass);
}

/** Updates the guess count display. */
private void updateGuessCount() {
guessCountElement.setInnerHTML("Number of guesses: " + numberOfGuesses);
}

/** Keyboard event interface for handling key presses. */
private interface HTMLKeyboardEvent extends HTMLEvent {
String getKey();
}

/** Base HTML event interface. */
private interface HTMLEvent extends org.teavm.jso.JSObject {}
}
18 changes: 18 additions & 0 deletions teavm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh
# Script to build TeaVM JavaScript output for GitHub Pages deployment
# This replaces the cheerpj.sh script

echo "Building project..."
./gradlew build

echo "Compiling Java to JavaScript with TeaVM..."
# Disable configuration cache due to TeaVM plugin compatibility
./gradlew teavmc --no-configuration-cache

echo "Copying TeaVM output to javadoc directory..."
mkdir -p app/build/docs/javadoc/teavm
cp -f app/build/teavm/classes.js app/build/docs/javadoc/teavm/
cp -f teavm/index.html app/build/docs/javadoc/teavm/

echo "TeaVM build completed successfully!"
echo "Output location: app/build/docs/javadoc/teavm/"
45 changes: 45 additions & 0 deletions teavm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# TeaVM Web Version

This directory contains the web browser version of the Number Guessing Game, compiled from Java to JavaScript using TeaVM.

## What is TeaVM?

TeaVM is an ahead-of-time compiler for Java bytecode that emits JavaScript. It allows running Java applications in web browsers without requiring a Java plugin or JVM.

## Files

- `index.html` - The main HTML page that loads and runs the game
- `classes.js` - The compiled JavaScript (generated during build, not committed to git)

## Building

To build the TeaVM version:

```bash
./teavm.sh
```

This script will:
1. Build the project
2. Compile the WebGUI class to JavaScript using TeaVM
3. Copy the output to `app/build/docs/javadoc/teavm/` for deployment

## Implementation

The web version uses `WebGUI.java` which:
- Implements a browser-native UI using TeaVM's JSO (JavaScript Objects) API
- Reuses the core game logic classes (`RandomNumber`, `CheckGuess`, etc.)
- Creates DOM elements dynamically for the game interface
- Provides the same gameplay experience as the Swing GUI

## Deployment

The TeaVM version is automatically deployed to GitHub Pages via the `javadoc.yml` workflow whenever changes are pushed to the master branch.

## Technical Details

- **TeaVM Version**: 0.10.2
- **Plugin**: io.github.zebalu.teavm-gradle-plugin v1.0.0
- **Main Class**: `io.github.project516.NumberGuessingGame.WebGUI`
- **Target**: JavaScript
- **Java Version Required**: 11+ (for compilation)
Loading
Loading