Welcome to the Mario JavaFX Game! This is a simple Mario-style platformer built with Java 11 and JavaFX. Enjoy classic gameplay, collect coins, avoid enemies, and reach the flag!
- Classic Mario platformer mechanics
- Smooth character movement and jumping
- Enemies: Champignons (mushrooms) and Turtles
- Collectible coins with animated sprites
- Score and timer display
- Level restart and game over transitions
- Responsive controls (keyboard)
- Custom pixel-art graphics
![]() |
![]() |
![]() |
|---|
record-RL-model.mp4
The video is the result of RL model for training during one day.
- Java 11+
- Maven
- Perl (required to use the
run.plscript for simplified execution) - Python 3.x (required for the Reinforcement Learning model)
- Python virtual environment (recommended for managing RL model dependencies)
lsof(for Linux/macOS users, if usingrun.plto automatically kill processes on port 50051)
The project can be built and run in two main ways:
To run only the JavaFX game without the Reinforcement Learning model:
cd mario
mvn clean install
mvn javafx:runThe game window will open. Use your keyboard to play!
For a more integrated experience, especially if you plan to interact with or train the RL model, use the provided Perl script run.pl. This script handles starting the Python gRPC server and then launching the Java game.
First, ensure you have the Python dependencies installed, preferably in a virtual environment. Navigate to the model directory and install them:
cd model
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
deactivate
cd ..Then, from the project root directory, run the game and the RL server using run.pl:
# To run with the Python RL server using a virtual environment
perl run.pl --run-server --venv model/venv
# To run with the Python RL server using system Python (not recommended)
perl run.pl --run-serverTo enable the AI RL model, use the --enable-ai flag with run.pl or pass -Dmario.enableAI=true directly to the Maven command if running without run.pl.
Example:
# Using run.pl with AI enabled
perl run.pl --run-server --venv model/venv --enable-ai
# Running directly with Maven and AI enabled
cd mario
mvn clean install
mvn javafx:run -Djavafx.args="ai"The run.pl script will:
- Kill any existing process on port 50051 (where the gRPC server runs).
- Start the Python gRPC server in the background.
- Build the Java project (
mvn clean install). - Launch the JavaFX game.
Output from the Python server will be redirected to model/proto_server/logs/python-server.log. You can stop the Python server manually by finding its process ID (e.g., ps aux | grep server.py) and killing it.
- Numpad 6: Move Right
- Numpad 4: Move Left
- Space: Jump
The project is organized into the following key directories and files:
.
โโโ mario/
โ โโโ pom.xml
โ โโโ src/
โ โโโ main/
โ โโโ java/
โ โ โโโ com/
โ โ โโโ game/
โ โ โโโ mario/
โ โ โโโ App.java
โ โ โโโ character/
โ โ โโโ game/
โ โ โโโ item/
โ โ โโโ util/
โ โโโ resources/
โ โโโ com/
โ โโโ game/
โ โโโ mario/
โ โโโ audio/
โ โโโ character/
โ โโโ game/
โ โโโ item/
โ โโโ police/
โโโ model/
โ โโโ dqn.py
โ โโโ requirements.txt
โ โโโ proto_server/
โ โ โโโ server.py
โ โ โโโ data_pb2.py
โ โ โโโ data_pb2_grpc.py
โ โโโ util/
โ โโโ config.py
โ โโโ mario_state.py
โโโ proto/
โ โโโ data.proto
โโโ config/
โ โโโ context.json
โ โโโ server.json
โโโ screenshot/
โโโ .gitignore
โโโ readMe.md
โโโ run.pl
- Java 11
- JavaFX 13
- Maven
- Python 3.x (for RL model)
- gRPC (for communication between Java game and Python model)
This project includes a reinforcement learning (RL) model that can be trained to play the game automatically. The training workflow involves a continuous interaction between the Java game and a Python gRPC server.
- Python Server (
server.py): Initializes aDQN(Deep Q-Network) agent with a neural network, experience memory, and hyperparameters. - Server State:
server.pyalso initializeslast_state,last_action, andlast_mariotoNone.
- Java Game (
GamerAI.java): Continuously collects the current game state, packages it into aGameDataprotobuf message, and sends it via gRPC to the Python server'sGetActionmethod.
- Python Server (
server.py:_get_state_from_request): Transforms theGameDatamessage into a flat numericalstatevector (NumPy array) for the neural network. This includes Mario's coordinates,MarioState, and relative positions/statuses of antagonists, items, and coins.
- Python Server (
server.py:GetActionand_calculate_reward): Calculates arewardfor the previous action based on game events (e.g., moving forward, dying, jumping). The experience tuple(last_state, last_action, reward, current_state, done)is stored in theDQNagent's replay buffer.
- Python Server (
server.py:GetAction) callsdqn.py:act: TheDQNagent uses anepsilon-greedystrategy to select an action:- With probability
epsilon, a random action is chosen (exploration). - With probability
1 - epsilon, the neural network predicts Q-values, and the action with the highest Q-value is selected (exploitation).
- With probability
- Python Server (
server.py:GetAction): Sends the selectedactionback toGamerAI.java. - Java Game (
GamerAI.java,SceneUpdater.java): Receives the action, stores it, and theSceneUpdatertranslates it into game commands (e.g.,mario.setWalke(true),mario.setJump(true)). Keyboard input is ignored if an AI action is pending.
- Python Server (
server.py:GetAction) callsdqn.py:replay: If enough samples are in memory, aminibatchof experiences is randomly sampled. Target Q-values are calculated using the Bellman equation, and the neural network is trained to match these targets.epsilonis gradually decayed.
- Python Server (
server.py:GetAction): Updatesself.last_state,self.last_action, andself.last_mariofor the next iteration. These are reset toNoneif an episode terminates.
To build your own custom reinforcement learning model for this game, you will primarily interact with the following files:
proto/data.proto: This Protocol Buffer definition file is critical as it defines the structure of the data exchanged between the Java game and your Python-based RL model.
syntax = "proto3";
package proto;
message Position {
int32 x = 1;
int32 y = 2;
}
message Dimensions {
int32 height = 1;
int32 width = 2;
}
// Represents a game object with position and dimensions.
message Mario {
Position position = 1;
Dimensions dimensions = 2;
int32 numberOfLive = 3;
map<string, bool> state = 4;
}
// Represents an antagonist character.
message Antagonist {
Position position = 1;
Dimensions dimensions = 2;
int32 speed = 3;
string name = 4;
bool isdead = 5;
bool isZombie = 6;
}
// Represents an item in the game.
message Item {
Position position = 1;
Dimensions dimensions = 2;
string name = 3;
}
message Coin{
Position position = 1;
Dimensions dimensions = 2;
}
// Request data containing the state of the game.
message GameData {
Mario mario = 1;
int32 floor_level = 2;
int32 antagonist_context_width = 3;
int32 item_context_width = 4;
repeated Antagonist antagonists = 5;
repeated Item items = 6;
repeated Coin coins = 7;
}
// Response data containing the action to be taken.
message Action {
int32 action = 1;
}
// The game service definition.
service GameService {
// Sends game data and receives an action.
rpc GetAction(GameData) returns (Action) {}
}-
GameDatamessage: This is the input your model will receive from the game, containing information about Mario, antagonists, items, coins, and game context. Your model's observation space will be derived from this data. -
Actionmessage: This is the output your model must produce, indicating the action to be taken in the game. Understanding these message structures is fundamental to correctly interpret game states and generate valid actions.0: do nothing, 1: forward, 2: backward, 3: jump -
config/context.json: This configuration file provides essential context parameters that influence the game state and, consequently, the observations your model receives.
{
"contextItemWidth": 5,
"contextAntogonistWidth": 6,
"contextCoinWidth": 5,
"windowFilter": {
"min": 0,
"max": 800
}
}-
windowFilter: This represents the window within which information is collected. By default, humans perceive information within the game window, but it's possible to change this by modifying theminandmaxvalues. By default, these values correspond to the dimensions of the game window; you can enlarge or reduce this window. -
contextItemWidth,contextAntogonistWidth, andcontextCoinWidth: These respectively represent the number of items, antagonists, and coins that are extracted within the window defined bywindowFilter. -
config/server.json: This file specifies the network configuration for the gRPC server that facilitates communication between the Java game and your Python model.
{
"host": "localhost",
"port": 50051
}hostandport: These define where your gRPC server (e.g.,model/proto_server/server.py) will listen for incoming game data requests. Ensure your custom model's server implementation uses these same host and port settings to establish a connection with the game.
By understanding and utilizing these files, you can design a custom RL agent that processes game states, makes decisions, and interacts seamlessly with the Mario game environment.
The game uses java.util.concurrent.locks.ReentrantLock objects for thread synchronization, particularly for managing concurrent access to shared resources and character states among antagonists.
ChampignonandTurtleinstances run in their own threads, executing theirmove()method concurrently.
- Each antagonist has its own
protected ReentrantLock positionLocker. - The
move()method acquires this lock before updating an antagonist's position, ensuring exclusive access.
- Before moving, antagonists check the
positionLockerstatus of theirfrontCharacterandbehindCharacter. Movement proceeds only if adjacent characters are not currently holding their locks, preventing race conditions in close proximity.
GameManagermanages global read and write locks for all antagonist positions (getAllAntagonistPositionReaderLocker()andgetAllAntagonistPositionWriterLocker()).- Individual antagonist movements acquire a read lock and pause if a global write operation is in progress.
- The
Collision.antagonist()method acquires a global write lock when updatingfrontCharacterandbehindCharacterreferences, ensuring atomic and consistent updates of antagonist relationships.
This combined approach of per-object and global locks ensures consistency and prevents race conditions in the game's concurrent environment.
This project is for educational purposes.
This project has several exciting future prospects:
- Reinforcement Learning Model Training (Mario): Continue to train and improve the reinforcement learning model to play Mario automatically, as detailed in the "Reinforcement Learning Model Training Workflow" section. This will enable us to explore artificial intelligence applied to video games and improve the performance of agents in interactive environments.
- Reinforcement Learning Model Training (Antagonists): Develop and train separate reinforcement learning models for antagonists, allowing them to exhibit more intelligent and adaptive behaviors, thereby increasing the challenge and dynamic nature of the game.
- Enhanced Sound Effects: Implement a more comprehensive sound effects system to enrich the gaming experience, including distinct sounds for actions, item collection, enemy interactions, and level events.
- Custom Level Creation: Introduce the ability for users to design and load custom game levels. This would involve defining the environment, precise positions of items, antagonists, and coins within a structured JSON file format, allowing for endless replayability and creative freedom.
- AI-Powered Level Generation: Explore the development of an AI model, potentially leveraging Large Language Models (LLMs), to automatically generate new and challenging game levels in the specified JSON format. This could lead to dynamic and infinitely varied gameplay experiences.
Enjoy playing! Feel free to contribute or suggest improvements.



