A poor hedgehog has gotten lost in a series of enchanted gardens… It is up to you to find it!
Step into the shoes of a brave gardener and set off on an adventure through maps full of surprises. Collect all the carrots to open the doors, avoid the wasps (they sting hard but do not survive), and beware of hornets (tougher and even more dangerous).
Each step costs energy depending on the terrain. Stay still for a short while and you will recover some energy. But be careful: if you get stung without enough energy to absorb the hit, the game is over.
So, who will dare cross these gardens and save the hedgehog?

Nests only produce wasps or hornets while the gardener is inside the garden. If the gardener comes back to a garden that has already been visited, collected items must still be gone, and existing insects must keep evolving.
Each garden is a rectangular map made of cells.
Some cells can be crossed, others cannot. The gardener, wasps, and hornets move on this grid. Doors let you move from one garden to another, provided they are open. Wasps and hornets never use doors: they are terrified of drafts and refuse to step on them, whether they are open or closed.
To open the doors, all carrots must be collected. But beware: the gardens are full of wasps and hornets… and you are allergic to stings!
Wasps die after stinging you. Hornets can sting twice before dying. Stings are costly: -20 energy for a wasp, -30 for a hornet. If you have no energy left when you are stung, the game is lost.
Fortunately, insecticide bombs are here to help. Each bomb kills a wasp; two bombs are needed to kill a hornet. Insects may also walk onto bombs, which is bad for their health.
Terrain rules:
energyRecoverDuration), energy is recovered, up to the initial maximum (gardenerEnergy).Bonuses are collected automatically when walking over them:
energyBoost, up to your maximum energy (gardenerEnergy), and cure all diseases (diseaseLevel is reset to 1).diseaseLevel +1 for diseaseDuration). If you collect several of them, their effects overlap: each core increases the fatigue level for its own duration.
diseaseLevel therefore reflects the number of rotten cores still active at a given moment.The energy level, fatigue level (diseaseLevel), and number of bombs are always displayed in the status bar.
Here are the elements you will encounter in the game:
| Image | Name | Description |
|---|---|---|
![]() |
Grass | Gardener movement cost: 1 x diseaseLevel |
![]() |
Dirt | Gardener movement cost: 2 x diseaseLevel |
![]() |
Flower bush | Passable only by wasps and hornets |
![]() |
Wasp nest | Spawns one wasp + one insecticide bomb every 5 seconds |
![]() |
Hornet nest | Spawns one hornet + two insecticide bombs every 10 seconds |
![]() |
Hedgehog | Frightened hedgehog to rescue as quickly as possible |
![]() |
Closed door | Impassable until all carrots have been collected |
![]() |
Open door | Allows travel to another garden |
![]() |
Carrots | Collect them all to open the doors |
![]() |
Poisoned apple | Temporary sickness, increased fatigue |
![]() |
Apple | Cure + energy boost |
![]() |
Insecticide bomb | Kills a wasp or injures a hornet |
![]() |
Wasp | Dies after stinging or after touching a bomb |
![]() |
Hornet | Can sting twice; deals heavy damage and requires two insecticide bombs |
![]() |
Gardener | That’s you! |
We can classify these elements as follows:
We provide you with an initial draft of the game.
At startup, a simple map (MapLevelDefaultStart) is loaded: the gardener can move freely on it, regardless of the type of cell.
Game parameters are stored in a .properties file, which is easy to modify.
Java properties files make it easy to store key/value pairs.
| Parameter | Default value | Comment |
|---|---|---|
| waspMoveFrequency | 1 / s | Wasp movement speed |
| hornetMoveFrequency | 2 / s | Hornet movement speed |
| gardenerEnergy | 100 | Gardener max energy |
| energyBoost | 50 | Energy gained from an apple |
| energyRecoverDuration | 1000 ms | Idle time needed to recover 1 energy point |
| diseaseDuration | 5000 ms | Duration of sickness after eating a rotten core |
Starter project archive: UBGarden-student-2025.zip
MapLevelDefaultStart mapdiseaseLevel)waspMoveFrequency into accounthornetMoveFrequency into accountWhen the gardener walks over a bonus, it should be picked up. Suppose Apple and PoisonedApple are subclasses of Bonus. One might want to write something like this in the Gardener class:
class Gardener {
void pickUp(Apple apple) { ... }
void pickUp(PoisonedApple poisonedApple) { ... }
void doMove(Direction direction) {
Position nextPos = direction.nextPosition(getPosition());
Decor next = game.world().getGrid().get(nextPos);
setPosition(nextPos);
if (next != null)
pickUp(next);
}
}
But in Java, it is not possible to perform dynamic dispatch based on the runtime type of a method parameter (here, the bonus type). Java only performs dynamic dispatch on the receiver object, not on arguments.
To work around this, we invert the dependency: instead of the gardener “picking up” the bonus, the bonus “gets picked up” by the gardener.
With this mechanism, we benefit from dynamic dispatch on the actual bonus type, followed by static dispatch on the gardener’s pickUp(...) method.
class Gardener {
void pickUp(Apple apple) { ... }
void pickUp(PoisonedApple poisonedApple) { ... }
void doMove(Direction direction) {
// ...
if (next != null)
next.pickUpBy(this);
}
}
public interface Pickupable {
default void pickUpBy(Gardener gardener) {}
}
public abstract class Bonus implements Pickupable {}
public class Apple extends Bonus {
@Override
public void pickUpBy(Gardener gardener) {
gardener.pickUp(this);
}
}
public class PoisonedApple extends Bonus {
@Override
public void pickUpBy(Gardener gardener) {
gardener.pickUp(this);
}
}
In this case, the correct pickUp(Apple) or pickUp(PoisonedApple) method of the gardener is called thanks to double dispatch:
pickUpBy)pickUp(...) method