Feature: initial commit

This commit is contained in:
2018-01-04 12:16:14 +00:00
commit 9065ef6af5
27 changed files with 2243 additions and 0 deletions

23
README.md Normal file
View File

@@ -0,0 +1,23 @@
# Rabbits vs Hunter
## Goal
This projects manages a rabbit hunt.
It's possible to change some criteria such as trees, burrows and rabbits counts.
## Building
Using maven, just execute the following command :
```
mvn clean package
```
In the new _target_ folder, you will find the _rabbits-vs-hunter-[version]-jar-with-dependencies.jar_ jar file. This jar contains all dependencies.
## Running
Execute the built jar :
```
java -jar rabbits-vs-hunter-[version]-jar-with-dependencies.jar
```

63
pom.xml Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fr.pavnay</groupId>
<artifactId>rabbits-vs-hunter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- Tools -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>fr.pavnay.rabbits.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!-- this is used for inheritance merges -->
<phase>package</phase>
<!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,17 @@
package fr.pavnay.rabbits;
import fr.pavnay.rabbits.ui.FormFrame;
/**
*
* The main class to run
*
*/
public class Main {
public static void main(String[] args) {
new FormFrame();
}
}

View File

@@ -0,0 +1,202 @@
package fr.pavnay.rabbits.engine;
import java.awt.Point;
import java.util.List;
import java.util.Random;
import org.apache.log4j.Logger;
import fr.pavnay.rabbits.model.Burrow;
import fr.pavnay.rabbits.model.Character;
import fr.pavnay.rabbits.model.Forest;
import fr.pavnay.rabbits.model.Hunter;
import fr.pavnay.rabbits.model.Rabbit;
import fr.pavnay.rabbits.model.enums.Speed;
import fr.pavnay.rabbits.model.enums.Status;
/**
*
* This class provides the hunt mechanisms like moving, shooting, etc...
*
*/
public class HuntEngine {
private static final Logger logger = Logger.getLogger(HuntEngine.class);
private Forest forest;
private Hunter hunter;
private int stepCount = 0;
private int lastShotStep = -4;
private Status status = Status.RUNNING;
public HuntEngine(Forest forest, Hunter hunter) {
this.forest = forest;
this.hunter = hunter;
}
public Forest getForest() {
return forest;
}
public Hunter getHunter() {
return hunter;
}
public Status getStatus() {
return status;
}
/**
* The core engine method. To have a full hunt cycle, call this method in a loop still the HuntEngine.getStatus() returns a value different than RUNNING.
*
* @return
*/
public Status step() {
logger.debug("Step " + stepCount);
boolean hunterHasShot = false; // Only one shot per step
int stepSinceLastShot = stepCount - lastShotStep;
move(hunter);
Random rand = new Random();
for (Rabbit rabbit : forest.getRabbits() ) {
if( stepSinceLastShot > 3 && !rabbit.isScared() ) {
if( rabbit.getSpeed() > Speed.MEDIUM.getSpeed() ) {
rabbit.slowDown();
} else {
rabbit.setSpeed(rand.nextInt(Speed.RUNNING.getSpeed()));
}
} else if( stepSinceLastShot > 5 && rabbit.isScared() ) {
rabbit.setScared(false);
}
move(rabbit);
boolean canShoot = !hunterHasShot && stepSinceLastShot > 5; // One shot per step and only 5 steps after the last shot.
if( canShoot && forest.canView( hunter, rabbit ) && hunter.isInRange(rabbit) ) {
hunterHasShot = true;
lastShotStep = stepCount;
if( hunter.shoot(rabbit) ) {
forest.killedRabbit(rabbit);
} else {
logger.info(String.format("Hunter missed a shot. %d ammos remaining", hunter.getAmmos()));
hunter.purchase(rabbit);
}
}
}
// A shot occurs, so rabbits must escape
if( hunterHasShot ) {
logger.debug("Escape from the hunter");
forest.escape(hunter);
}
stepCount++;
return updateStatus();
}
/**
* The rabbit must choose a destination - Random, the last one or a burrow
*
* @param rabbit The rabbit to move
*/
protected void move( Rabbit rabbit ) {
Point location = rabbit.getLocation();
Point destination = rabbit.getDestination();
if( location.distance(destination) <= rabbit.getSpeed() ) {
List<Burrow> refuges = rabbit.getRefuges();
if( refuges.size() > 0 ) { // Purchased rabbit
Burrow refuge = refuges.get( refuges.size() -1 );
if( refuge.isFree() ) { // Only one rabbit per burrow
refuge.setFree(false);
forest.savedRabbit( rabbit );
} else {
Burrow burrow = forest.getNearestBurrow(hunter, rabbit); // Find another burrow
rabbit.setRefuge(burrow); // Keep in mind this burrow is full
rabbit.setDestination(burrow.getLocation());
}
} else {
setRandomDestination(rabbit);
}
}
if( rabbit.getDestination() != null ) {
go(rabbit);
}
}
/**
* The hunter must choose a destination : the last one or a new randomized destination.
*
* @param hunter
*/
protected void move( Hunter hunter ) {
Point location = hunter.getLocation();
Point destination = hunter.getDestination();
if( location.distance(destination) < hunter.getSpeed() ) {
destination = setRandomDestination(hunter);
}
go(hunter);
}
/**
* Move the given character (Hunter or Rabbit)
*
* @param character
*/
protected void go(Character character) {
Point location = character.getLocation();
Point destination = character.getDestination();
double x = destination.getX() - location.getX();
double y = destination.getY() - location.getY();
double rate = character.getSpeed() / location.distance(destination);
double newX = Math.max(0, location.getX() + x * rate);
double newY = Math.max(0, location.getY() + y * rate);
Point newLocation = new Point((int) Math.min(newX, forest.getEdgeArea()), (int) Math.min(newY, forest.getEdgeArea()));
character.increaseDistance((int) location.distance(newLocation));
location.setLocation( newLocation );
}
/**
* "Choose" a random destination for the given character (Hunter or Rabbit)
*
* @param character The character to move
* @return The new destination
*/
protected Point setRandomDestination(Character character) {
Random rand = new Random();
Point destination = new Point(rand.nextInt(forest.getEdgeArea()), rand.nextInt(forest.getEdgeArea()));
character.setDestination(destination);
return destination;
}
/**
* Update the hunt status
*
* @return The new Status
*/
protected Status updateStatus() {
if( forest.getRabbits().size() == 0 ) { // No more running rabbits, so the hunt ends
if( (forest.getRabbits().size() + forest.getSavedRabbits().size()) < forest.getDeadRabbits().size() ) { // Too many dead rabbit, the hunter wins
status = Status.HUNTER_WINS;
} else {
status = Status.RABBITS_WIN; // else, rabbits win
}
logger.info("Hunt ended : " + status);
} else if( hunter.getAmmos() == 0 ) { // No more ammos, rabbits win
status = Status.RABBITS_WIN;
logger.info("Hunt ended : " + status);
} else { // Hunt still running
status = Status.RUNNING;
}
return status;
}
}

View File

@@ -0,0 +1,36 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
/**
*
* A rabbit burrow - Only one rabbit per burrow.
* Burrows are filled when a rabbit is purchased.
*
*/
public class Burrow {
private Point location;
private boolean free;
public Burrow( Point location ) {
this.location = location;
free = true;
}
public Point getLocation() {
return location;
}
public void setFree(boolean free) {
this.free = free;
}
public boolean isFree() {
return free;
}
@Override
public String toString() {
return "Burrow [location=" + location + ", free=" + free + "]";
}
}

View File

@@ -0,0 +1,18 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
/**
*
* An interface to manage hunter and rabbits in the same way.
*
*/
public interface Character {
public Point getLocation();
public Point getDestination();
public void setDestination(Point destination);
public int getSpeed();
public void increaseDistance(int distance);
}

View File

@@ -0,0 +1,295 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.log4j.Logger;
import fr.pavnay.rabbits.model.predicate.RangePredicate;
/**
*
* The main "entity" where rabbits live.
*
*/
public class Forest {
private static final Logger logger = Logger.getLogger(Forest.class);
private List<Tree> trees = new ArrayList<Tree>();
private List<Rabbit> rabbits = new CopyOnWriteArrayList<Rabbit>();
private List<Rabbit> savedRabbits = new CopyOnWriteArrayList<Rabbit>();
private List<Rabbit> deadRabbits = new CopyOnWriteArrayList<Rabbit>();
private List<Burrow> burrows = new ArrayList<Burrow>();
private int edgeArea;
public Forest( int area, int treesCount, int burrowsCount, int rabbitsCount ) {
if( area <= 0 || area > 10) {
throw new IllegalArgumentException("Area must be between 1 and 10 ");
}
if( treesCount <= 0 || treesCount > 1000) {
throw new IllegalArgumentException("Tree count must be between 1 and 1000 ");
}
if( rabbitsCount <= 0 ) {
throw new IllegalArgumentException("Add some rabbits");
}
if( burrowsCount <= 0 || burrowsCount < rabbitsCount) {
throw new IllegalArgumentException("Add some burrows. Greater or equals than rabbits count.");
}
edgeArea = (int) Math.sqrt(area * 1000);
populate(treesCount, burrowsCount, rabbitsCount);
}
/**
* Add trees, burrows and rabbits to the forest. These entities have random location (and rabbits random destination)
*
* @param treesCount Number of trees to create
* @param burrowsCount Number of burrows to create
* @param rabbitsCount Number of rabbits to create
*/
private void populate(int treesCount, int burrowsCount, int rabbitsCount) {
Random rand = new Random();
for( int i = 0; i < treesCount; i++ ) {
trees.add(new Tree(new Point(rand.nextInt(edgeArea), rand.nextInt(edgeArea))));
}
for( int i = 0; i < burrowsCount; i++ ) {
burrows.add(new Burrow(new Point(rand.nextInt(edgeArea), rand.nextInt(edgeArea))));
}
for( int i = 0; i < rabbitsCount; i++ ) {
Rabbit rabbit = new Rabbit(new Point(rand.nextInt(edgeArea), rand.nextInt(edgeArea)));
rabbit.setDestination(new Point(rand.nextInt(edgeArea), rand.nextInt(edgeArea)));
rabbits.add(rabbit);
}
}
public int getEdgeArea() {
return edgeArea;
}
public List<Rabbit> getRabbits() {
return rabbits;
}
/**
*
* @param hunter
* @return Rabbits in hunter range
*/
public List<Rabbit> getRabbitsInRange(Hunter hunter) {
return (List<Rabbit>) CollectionUtils.select(rabbits, new RangePredicate(hunter));
}
/**
*
* @return Dead rabbits
*/
public List<Rabbit> getDeadRabbits() {
return deadRabbits;
}
/**
*
* @return Rabbits in burrows
*/
public List<Rabbit> getSavedRabbits() {
return savedRabbits;
}
/**
* Move a rabbit from "running" to "dead"
* @param rabbit
*/
public void killedRabbit(Rabbit rabbit) {
if( rabbits.remove(rabbit) ) {
logger.info("Poor rabbit... Killed...");
deadRabbits.add(rabbit);
} else {
throw new RuntimeException("Unable to kill a poor rabbit...");
}
}
/**
* Move a rabbit from "running" to "saved"
* @param rabbit
*/
public void savedRabbit(Rabbit rabbit) {
if( rabbits.remove(rabbit) ) {
logger.info("In a burrow... Rabbit saved");
rabbit.setDestination(null);
savedRabbits.add(rabbit);
} else {
throw new RuntimeException("Unable to save a poor rabbit...");
}
}
public List<Burrow> getBurrows() {
return burrows;
}
/**
* Rabbits in hunter range must escape
* @param hunter
*/
public void escape(Hunter hunter) {
for( Rabbit rabbit : getRabbitsInRange(hunter) ) {
escape(hunter, rabbit);
}
}
/**
* Choose a rabbit destination to escape. If the given rabbit is scared (the one on which the hunter shot), it must find a burrow.
* The others must go at the opposite of hunter location.
*
* @param hunter
* @param rabbit
*/
protected void escape( Hunter hunter, Rabbit rabbit ) {
Point target = null;
if( rabbit.isScared() ) {
logger.debug("Rabbit's searching a burrow");
Burrow refuge = getNearestBurrow(hunter, rabbit);
rabbit.setRefuge(refuge);
} else {
logger.debug("Go away from the hunter");
// Hunter -> Rabbit vector
double a = rabbit.getLocation().getX() - hunter.getLocation().getX();
double b = rabbit.getLocation().getY() - hunter.getLocation().getY();
// Keep in forest
double x = Math.max(0, rabbit.getLocation().getX() + a);
double y = Math.max(0, rabbit.getLocation().getY() + b);
target = new Point((int) Math.min(x, edgeArea), (int) Math.min(y, edgeArea));
rabbit.setDestination(target);
}
}
/**
* Compute 2 nearest burrows. One with back to the hunter and another more safe. The last one is preferred
*
* @param hunter
* @param rabbit
* @return
*/
public Burrow getNearestBurrow(Hunter hunter, Rabbit rabbit) {
logger.debug("Searching the nearest burrow");
double distance = Double.MAX_VALUE, worthDistance = Double.MAX_VALUE;
Burrow nearest = null, worthNearest = null;
for( Burrow burrow : burrows ) {
if( rabbit.getRefuges().contains(burrow) ) {
continue;
}
double tmpDistance = burrow.getLocation().distance(rabbit.getLocation());
double dotProduct = dotProduct(hunter.getLocation(), rabbit.getLocation(), burrow.getLocation());
if( dotProduct <= 0 && tmpDistance < distance ) {
distance = tmpDistance;
nearest = burrow;
}
else if( dotProduct > 0 && tmpDistance < worthDistance ) { // Back to the hunter
worthDistance = tmpDistance;
worthNearest = burrow;
}
}
return distance <= worthDistance ? nearest : worthNearest;
}
public List<Tree> getTrees() {
return trees;
}
/**
* A rabbit is viewed when there are no trees between the hunter and it.
*
* @param hunter
* @param rabbit
* @return
*/
public boolean canView(Hunter hunter, Rabbit rabbit) {
for( Tree tree : trees ) {
if( isBetween(hunter.getLocation(), rabbit.getLocation(), tree.getLocation())) {
logger.debug("Rabbit is hidden");
return false;
}
};
return true;
}
private static final int EPSILON = 5;
/**
* Checks if a tree is between a rabbit and the hunter.
*
* @param hunter
* @param rabbit
* @param tree
* @return
*/
protected boolean isBetween(Point hunter, Point rabbit, Point tree) {
double crossproduct = (tree.getY() - hunter.getY()) * (rabbit.getX() - hunter.getX()) - (tree.getX() - hunter.getX()) * (rabbit.getY() - hunter.getY());
if (Math.abs(crossproduct) > EPSILON ) {
return false;
}
double dotproduct = dotProduct(hunter, rabbit, tree); // Tree is far away
if ( dotproduct < 0 ) {
return false;
}
double squaredlengthba = (rabbit.getX()- hunter.getX())*(rabbit.getX() - hunter.getX()) + (rabbit.getY() - hunter.getY())*(rabbit.getY() - hunter.getY());
if ( dotproduct > squaredlengthba) { // Tree is too far
return false;
}
return true;
}
/**
* dotProduct to know if the tree is between the hunter and the rabbit.
*
* @param hunter
* @param rabbit
* @param tree
* @return If positive, the tree is between.
*/
private double dotProduct(Point hunter, Point rabbit, Point tree) {
return (tree.getX() - hunter.getX()) * (rabbit.getX() - hunter.getX()) + (tree.getY() - hunter.getY())*(rabbit.getY() - hunter.getY());
}
public void printStats() {
logger.debug("Running rabbits");
printRabbits(rabbits);
logger.debug("Saved rabbits");
printRabbits(savedRabbits);
logger.debug("Dead rabbits");
printRabbits(deadRabbits);
}
private void printRabbits(List<Rabbit> rabbits) {
for( Rabbit rabbit : rabbits ) {
logger.debug(rabbit);
}
}
@Override
public String toString() {
return "Forest [trees=" + trees + ", rabbits=" + rabbits + ", burrows=" + burrows + ", edgeArea=" + edgeArea
+ "]";
}
}

View File

@@ -0,0 +1,129 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
import org.apache.log4j.Logger;
import fr.pavnay.rabbits.model.enums.Speed;
public class Hunter implements Character {
private static final Logger logger = Logger.getLogger(Hunter.class);
private final static int INITIAL_AMMOS = 10;
private int ammos = INITIAL_AMMOS;
private int hungryLevel = 0;
private int distance = 0;
private Point location;
private int range;
private int speed;
private Point destination;
public Hunter( Point location, int range ) {
this.location = location;
this.range = range;
this.speed = Speed.MEDIUM.getSpeed();
}
public int getAmmos() {
return ammos;
}
public void setAmmos(int ammos) {
this.ammos = ammos;
}
public int getHungryLevel() {
return hungryLevel;
}
public void setHungryLevel(int hungryLevel) {
this.hungryLevel = hungryLevel;
}
public int getDistance() {
return distance;
}
@Override
public Point getLocation() {
return location;
}
@Override
public Point getDestination() {
return destination;
}
@Override
public void setDestination(Point destination) {
this.destination = destination;
}
@Override
public int getSpeed() {
return speed;
}
/**
* Increasing the walked distance increases the hungry level
*
*/
@Override
public void increaseDistance(int distance) {
this.distance += distance;
setHungryLevel(Math.min(10, (int) (this.distance / 15)));
}
/**
* Tries to shoot a rabbit
*
* @param rabbit
* @return
*/
public boolean shoot(Rabbit rabbit) {
if( ammos == 0 ) {
logger.info("Click... No more ammos");
return false;
}
double rate = getAccuracyRate(rabbit);
ammos--;
return Math.random() < rate; // If the random value is less than the computed accuracy, the shot is good
}
/**
* The hunter finds a rabbit. He goes to its last position.
*
* @param rabbit
*/
public void purchase(Rabbit rabbit) {
rabbit.purchased();
destination = rabbit.getLocation();
}
/**
* Accuracy depends to the distance with the target, the number of leaving ammos and the hungry level.
*
* @param rabbit
* @return
*/
protected double getAccuracyRate(Rabbit rabbit) {
double rate = 1 - getLocation().distance(rabbit.getLocation()) / range;
rate -= 0.005 * (INITIAL_AMMOS - ammos);
rate -= 0.01 * hungryLevel;
return rate;
}
/**
* The given rabbit is in range if the distance between the hunter and the rabbit is less than the hunter range.
*
* @param rabbit
* @return
*/
public boolean isInRange(Rabbit rabbit) {
logger.debug(location.distance(rabbit.getLocation()) + "<=" + range);
return location.distance(rabbit.getLocation()) <= range;
}
@Override
public String toString() {
return "Hunter [ammos=" + ammos + ", hungryLevel=" + hungryLevel + ", distance=" + distance + ", location="
+ location + ", range=" + range + "]";
}
}

View File

@@ -0,0 +1,91 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import fr.pavnay.rabbits.model.enums.Color;
import fr.pavnay.rabbits.model.enums.Speed;
public class Rabbit implements Character {
private Point location;
private int speed;
private Color color;
private int distance;
private Point destination;
private List<Burrow> refuges = new ArrayList<Burrow>();
private boolean scared;
public Rabbit(Point location) {
this.color = Math.random() < 0.5 ? Color.BROWN : Color.WHITE;
this.location = location;
speed = Speed.SLOW.getSpeed();
distance = 0;
}
@Override
public Point getLocation() {
return location;
}
@Override
public int getSpeed() {
return speed;
}
@Override
public void increaseDistance(int distance) {
this.distance += distance;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public Color getColor() {
return color;
}
public int getDistance() {
return distance;
}
public boolean isScared() {
return scared;
}
public void setScared(boolean scared) {
this.scared = scared;
}
@Override
public void setDestination(Point destination) {
this.destination = destination;
}
@Override
public Point getDestination() {
return destination;
}
/**
* The rabbit finds a burrow. So it keeps in mind its location and to go it.
*
* @param refuge
*/
public void setRefuge(Burrow refuge) {
this.refuges.add( refuge );
destination = refuge.getLocation();
}
public List<Burrow> getRefuges() {
return refuges;
}
public void slowDown() {
speed = Math.max(0, speed - 1);
}
public void purchased() {
scared = true;
}
@Override
public String toString() {
return "Rabbit [location=" + location + ", speed=" + speed + ", color=" + color + ", distance=" + distance
+ "]";
}
}

View File

@@ -0,0 +1,22 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
public class Tree {
private Point location;
public Tree( Point location ) {
this.location = location;
}
public Point getLocation() {
return location;
}
@Override
public String toString() {
return "Tree [location=" + location + "]";
}
}

View File

@@ -0,0 +1,22 @@
package fr.pavnay.rabbits.model.enums;
/**
*
* An utility enum to give color to rabbits
*
*/
public enum Color {
WHITE(java.awt.Color.WHITE),
BROWN(new java.awt.Color(149, 86, 40));
private java.awt.Color color;
private Color( java.awt.Color color) {
this.color = color;
}
public java.awt.Color getColor() {
return color;
}
}

View File

@@ -0,0 +1,25 @@
package fr.pavnay.rabbits.model.enums;
/**
*
* An utility enum to give some default speeds
*
*/
public enum Speed {
STOPPED(0),
SLOW(1),
MEDIUM(5),
RUNNING(10);
private int speed;
private Speed(int speed) {
this.speed = speed;
}
public int getSpeed() {
return speed;
}
}

View File

@@ -0,0 +1,18 @@
package fr.pavnay.rabbits.model.enums;
/**
*
* The hunt status
*
*/
public enum Status {
RUNNING,
HUNTER_WINS,
RABBITS_WIN;
@Override
public String toString() {
return name().charAt(0) + name().substring(1).toLowerCase().replace('_', ' ');
}
}

View File

@@ -0,0 +1,25 @@
package fr.pavnay.rabbits.model.predicate;
import org.apache.commons.collections4.Predicate;
import fr.pavnay.rabbits.model.Hunter;
import fr.pavnay.rabbits.model.Rabbit;
/**
*
* An utility class to check if a rabbit is in the hunter range
*
*/
public class RangePredicate implements Predicate<Rabbit> {
private Hunter hunter;
public RangePredicate(Hunter hunter) {
this.hunter = hunter;
}
public boolean evaluate(Rabbit rabbit) {
return hunter.isInRange(rabbit);
}
}

View File

@@ -0,0 +1,115 @@
package fr.pavnay.rabbits.ui;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.text.NumberFormatter;
import fr.pavnay.rabbits.engine.HuntEngine;
import fr.pavnay.rabbits.model.Forest;
import fr.pavnay.rabbits.model.Hunter;
/**
*
* The entry point in which forest area, trees, burrows and rabbit numbers are set.
*
*/
public class FormFrame extends JFrame implements ActionListener, PropertyChangeListener {
private static final long serialVersionUID = 4594334659003442873L;
private JFormattedTextField areaInput; // Only number with specificities (min, max)
private JFormattedTextField treeInput;
private JFormattedTextField rabbitInput;
private JFormattedTextField burrowInput;
private JButton validateBtn;
public FormFrame() {
setTitle("Rabbits vs Hunter");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridBagLayout());
UiUtils.add(this, new JLabel("Area"), 0, 0, 1, 1);
areaInput = new JFormattedTextField(getFormatter(1, 10));
areaInput.setValue(10);
areaInput.addActionListener(this);
UiUtils.add(this, areaInput, 1, 0, 1, 1);
UiUtils.add(this, new JLabel("Trees"), 0, 1, 1, 1);
treeInput = new JFormattedTextField(getFormatter(0, 300));
treeInput.setValue(20);
treeInput.addActionListener(this);
UiUtils.add(this, treeInput, 1, 1, 1, 1);
UiUtils.add(this, new JLabel("Rabbits"), 0, 2, 1, 1);
rabbitInput = new JFormattedTextField(getFormatter(1, 20));
rabbitInput.setValue(5);
rabbitInput.addActionListener(this);
UiUtils.add(this, rabbitInput, 1, 2, 1, 1);
UiUtils.add(this, new JLabel("Burrows"), 0, 3, 1, 1);
burrowInput = new JFormattedTextField(getFormatter(1, 20));
burrowInput.setValue(10);
burrowInput.addPropertyChangeListener(this);
UiUtils.add(this, burrowInput, 1, 3, 1, 1);
validateBtn = new JButton("Validate");
validateBtn.addActionListener(this);
UiUtils.add(this, validateBtn, 0, 4, 2, 1);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private NumberFormatter getFormatter( int min, int max) {
NumberFormat format = NumberFormat.getInstance();
NumberFormatter formatter = new NumberFormatter(format);
formatter.setValueClass(Integer.class);
formatter.setMinimum(min);
formatter.setMaximum(max);
formatter.setAllowsInvalid(false);
return formatter;
}
@Override
public void actionPerformed(ActionEvent event) {
if( Integer.parseInt(burrowInput.getText()) < Integer.parseInt(rabbitInput.getText()) ) {
JOptionPane.showMessageDialog(this, "Add some burrows. Greater or equals than rabbits count.", "Error", JOptionPane.ERROR_MESSAGE);
} else {
Forest forest = new Forest(Integer.parseInt(areaInput.getText()), Integer.parseInt(treeInput.getText()),
Integer.parseInt(burrowInput.getText()), Integer.parseInt(rabbitInput.getText()));
Random rand = new Random();
Hunter hunter = new Hunter(new Point(rand.nextInt(forest.getEdgeArea()), rand.nextInt(forest.getEdgeArea())), 20);
hunter.setDestination(new Point(rand.nextInt(forest.getEdgeArea()), rand.nextInt(forest.getEdgeArea())));
HuntEngine engine = new HuntEngine(forest, hunter);
new MainFrame(engine);
dispose();
}
}
@Override
public void propertyChange(PropertyChangeEvent arg0) {
validateBtn.setEnabled(!"".equals(areaInput.getText()) && !"".equals(treeInput.getText())
&& !"".equals(rabbitInput.getText()) && !"".equals(burrowInput.getText()));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(250, 200);
}
}

View File

@@ -0,0 +1,98 @@
package fr.pavnay.rabbits.ui;
import java.util.List;
import javax.swing.SwingWorker;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import fr.pavnay.rabbits.engine.HuntEngine;
import fr.pavnay.rabbits.model.enums.Status;
/**
*
* This worker provides the loop to play HuntEngine.step() until the status is different to RUNNING
*
*/
public class HuntWorker extends SwingWorker<Status, String> {
private static final Logger logger = Logger.getLogger(HuntWorker.class);
public final static long SLEEP_TIME = 500;
private HuntEngine engine;
private MainFrame mainFrame;
public HuntWorker(HuntEngine engine, MainFrame mainFrame ) {
this.engine = engine;
this.mainFrame = mainFrame;
HuntLogAppender appender = new HuntLogAppender(this);
LogManager.getLogger("fr.pavnay.rabbits").addAppender(appender);
}
public void appendLog(String log) {
publish(log);
}
/**
* Provides some hunt event from logs to UI
*/
@Override
protected void process(List<String> logs) {
for(String log : logs) {
mainFrame.report(log);
}
}
@Override
protected Status doInBackground() throws Exception {
Status status = engine.getStatus();
try {
while( status == Status.RUNNING ) {
status = engine.step();
Thread.sleep(SLEEP_TIME);
}
}catch( Exception e) {
logger.error(e.getMessage(), e);
}
logger.debug("Hunt ended : " + engine.getStatus());
return engine.getStatus();
}
@Override
protected void done() {
super.done();
logger.debug("Done - " + engine.getStatus());
}
/**
*
* A log appender to retrieve hunt logs and give them to UI.
*
*/
public class HuntLogAppender extends AppenderSkeleton {
private final HuntWorker worker;
public HuntLogAppender( HuntWorker slurperWorker) {
this.worker = slurperWorker;
}
protected void append(LoggingEvent event)
{
if(event.getLevel().equals(Level.INFO)){
worker.appendLog(event.getMessage().toString());
}
}
public void close()
{
}
public boolean requiresLayout()
{
return false;
}
}
}

View File

@@ -0,0 +1,114 @@
package fr.pavnay.rabbits.ui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import org.apache.log4j.Logger;
import fr.pavnay.rabbits.engine.HuntEngine;
import fr.pavnay.rabbits.ui.panel.Canvas;
import fr.pavnay.rabbits.ui.panel.CanvasLegend;
import fr.pavnay.rabbits.ui.panel.CharacterPanel;
import fr.pavnay.rabbits.ui.panel.LogPanel;
/**
*
* The frame to see the hunt.
*
*/
public class MainFrame extends JFrame implements PropertyChangeListener, ActionListener {
private static final long serialVersionUID = -5070100141993068939L;
private static final Logger logger = Logger.getLogger(MainFrame.class);
private HuntEngine engine;
private Timer timer; // Timer used to refresh UI periodically
private JPanel canvas; // The hunt panel
private CharacterPanel characterPanel; // Characters stats panel
private LogPanel logPanel; // Hunt event panel
private JLabel statusLabel; // Hunt status
private HuntWorker worker;
public MainFrame( HuntEngine engine ) {
setTitle("Rabbits vs Hunter");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
this.engine = engine;
JPanel statusPanel = new JPanel();
statusPanel.add(new JLabel("Status :"));
statusLabel = new JLabel(engine.getStatus().toString());
statusPanel.add(statusLabel);
add(statusPanel, BorderLayout.NORTH);
canvas = new Canvas(engine.getForest(), engine.getHunter());
timer = new Timer((int) HuntWorker.SLEEP_TIME, this);
add(canvas, BorderLayout.CENTER);
characterPanel = new CharacterPanel(engine.getHunter(), engine.getForest());
logPanel = new LogPanel();
int VERT_GAP = 10;
int EB_GAP = 5;
JPanel sidePanel = new JPanel();
sidePanel.setBorder(BorderFactory.createEmptyBorder(EB_GAP, EB_GAP, EB_GAP, EB_GAP));
sidePanel.setLayout(new BoxLayout(sidePanel, BoxLayout.PAGE_AXIS));
sidePanel.add(characterPanel);
sidePanel.add(Box.createVerticalStrut(VERT_GAP));
sidePanel.add(Box.createVerticalStrut(VERT_GAP));
sidePanel.add(new JScrollPane(logPanel));
add(sidePanel, BorderLayout.EAST);
add( new CanvasLegend(engine.getForest()), BorderLayout.SOUTH );
worker = new HuntWorker(engine, this);
worker.addPropertyChangeListener(this);
worker.execute();
timer.start();
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) { // Worker ends
statusLabel.setText(engine.getStatus().toString());
statusLabel.repaint();
logger.debug(engine.getHunter());
engine.getForest().printStats();
}
}
public void actionPerformed(ActionEvent event) {
if(event.getSource()==timer){
canvas.repaint();
characterPanel.refresh();
}
}
public void report(String log) {
logPanel.addLog(log);
}
}

View File

@@ -0,0 +1,40 @@
package fr.pavnay.rabbits.ui;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JComponent;
/**
*
* An utility class for UI
*
*/
public class UiUtils {
/**
* Add a component in a container with a GridBagLayout
*
* @param container
* @param component
* @param x
* @param y
* @param w
* @param h
*/
public static void add(Container container, JComponent component, int x, int y,
int w, int h) {
GridBagConstraints bagConstraints = new GridBagConstraints();
bagConstraints.gridx = x;
bagConstraints.gridy = y;
bagConstraints.gridwidth = w;
bagConstraints.gridheight = h;
bagConstraints.anchor = (x == 0) ? GridBagConstraints.EAST
: GridBagConstraints.WEST;
bagConstraints.fill = (x == 0) ? GridBagConstraints.BOTH
: GridBagConstraints.HORIZONTAL;
bagConstraints.insets = new Insets(5, 5, 5, 5);
container.add(component, bagConstraints);
}
}

View File

@@ -0,0 +1,71 @@
package fr.pavnay.rabbits.ui.panel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import fr.pavnay.rabbits.model.Burrow;
import fr.pavnay.rabbits.model.Forest;
import fr.pavnay.rabbits.model.Hunter;
import fr.pavnay.rabbits.model.Rabbit;
import fr.pavnay.rabbits.model.Tree;
/**
*
* This class draws all hunt elements : trees, burrows, hunter and rabbits
*
*/
public class Canvas extends JPanel {
private static final long serialVersionUID = 4209640358977009595L;
protected static int RATE = 5;
protected static int ITEM_RATIO = 2;
private Forest forest;
private Hunter hunter;
public Canvas( Forest forest, Hunter hunter) {
this.hunter = hunter;
this.forest = forest;
}
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.setColor(Color.RED);
graphics.fillOval((int) hunter.getLocation().getX() * RATE, (int) hunter.getLocation().getY() * RATE, 3 * ITEM_RATIO, 3 * ITEM_RATIO);
graphics.setColor(Color.GREEN);
for( Tree tree : forest.getTrees() ) {
graphics.fillOval((int) tree.getLocation().getX() * RATE, (int) tree.getLocation().getY() * RATE, 5 * ITEM_RATIO, 5 * ITEM_RATIO);
}
for( Burrow burrow : forest.getBurrows() ) {
if( burrow.isFree() ) {
graphics.setColor(Color.GRAY);
} else {
graphics.setColor(Color.DARK_GRAY);
}
graphics.fillOval((int) burrow.getLocation().getX() * RATE, (int) burrow.getLocation().getY() * RATE, 4 * ITEM_RATIO, 2 * ITEM_RATIO);
}
for( Rabbit rabbit : forest.getRabbits() ) {
if( rabbit.getColor() == fr.pavnay.rabbits.model.enums.Color.WHITE ) {
graphics.setColor(Color.BLACK);
graphics.drawOval((int) rabbit.getLocation().getX() * RATE, (int) rabbit.getLocation().getY() * RATE, 3 * ITEM_RATIO, 3 * ITEM_RATIO);
} else {
graphics.setColor(rabbit.getColor().getColor());
graphics.fillOval((int) rabbit.getLocation().getX() * RATE, (int) rabbit.getLocation().getY() * RATE, 3 * ITEM_RATIO, 3 * ITEM_RATIO);
}
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(forest.getEdgeArea() * RATE, forest.getEdgeArea() * RATE);
}
}

View File

@@ -0,0 +1,54 @@
package fr.pavnay.rabbits.ui.panel;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import fr.pavnay.rabbits.model.Forest;
/**
*
* This class draws the Canvas elements legend
*
*/
public class CanvasLegend extends JPanel {
private static final long serialVersionUID = 1307357862666434838L;
private int width;
public CanvasLegend(Forest forest) {
this.width = forest.getEdgeArea() * Canvas.RATE;
}
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.setColor(Color.RED);
graphics.fillOval(5, 10, 3 * Canvas.ITEM_RATIO, 3 * Canvas.ITEM_RATIO);
graphics.drawString("Hunter", 12, 15);
graphics.setColor(Color.GREEN);
graphics.fillOval(width / 5 + 5, 10, 5 * Canvas.ITEM_RATIO, 5 * Canvas.ITEM_RATIO);
graphics.drawString("Trees", width / 5 + 12, 15);
graphics.setColor(Color.GRAY);
graphics.fillOval(2 * width / 5 + 5, 10, 4 * Canvas.ITEM_RATIO, 2 * Canvas.ITEM_RATIO);
graphics.drawString("Burrows", 2 * width / 5 + 12, 15);
graphics.setColor(Color.BLACK);
graphics.drawOval(3 * width / 5 + 5, 10, 3 * Canvas.ITEM_RATIO, 3 * Canvas.ITEM_RATIO);
graphics.drawString("Rabbits", 3 * width / 5 + 12, 15);
graphics.setColor(fr.pavnay.rabbits.model.enums.Color.BROWN.getColor());
graphics.fillOval(4 * width / 5 + 5, 10, 3 * Canvas.ITEM_RATIO, 3 * Canvas.ITEM_RATIO);
graphics.drawString("Rabbits", 4 * width / 5 + 12, 15);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(width, 20);
}
}

View File

@@ -0,0 +1,98 @@
package fr.pavnay.rabbits.ui.panel;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import fr.pavnay.rabbits.model.Forest;
import fr.pavnay.rabbits.model.Hunter;
import fr.pavnay.rabbits.ui.UiUtils;
/**
*
* This class gives some hunt statistics
*
*/
public class CharacterPanel extends JPanel {
private static final long serialVersionUID = -7823773213443395686L;
private Hunter hunter;
private Forest forest;
private JLabel ammosCountLabel;
private JLabel hungryLevelLabel;
private JLabel distanceLabel;
private JLabel runningRabbitsLabel;
private JLabel savedRabbitsLabel;
private JLabel deadRabbitsLabel;
public CharacterPanel(Hunter hunter, Forest forest) {
this.hunter = hunter;
this.forest = forest;
JPanel innerPanel = new JPanel(new GridBagLayout());
UiUtils.add(innerPanel, new JLabel("Hunter"), 0, 0, 3, 1);
JPanel tmpPanel = new JPanel(new GridBagLayout());
UiUtils.add(tmpPanel,new JLabel("Ammos"), 0, 0, 1, 1);
ammosCountLabel = new JLabel(String.valueOf(hunter.getAmmos()));
Font plainFont = ammosCountLabel.getFont().deriveFont(Font.PLAIN);
ammosCountLabel.setFont(plainFont);
UiUtils.add(tmpPanel, ammosCountLabel, 1, 0, 1, 1);
UiUtils.add(tmpPanel, new JLabel("Hungry"), 2, 0, 1, 1);
hungryLevelLabel = new JLabel(String.valueOf(hunter.getHungryLevel()));
hungryLevelLabel.setFont(plainFont);
UiUtils.add(tmpPanel, hungryLevelLabel, 3, 0, 1, 1);
UiUtils.add(tmpPanel, new JLabel("Distance"), 4, 0, 1, 1);
distanceLabel = new JLabel(String.valueOf(hunter.getDistance()));
distanceLabel.setFont(plainFont);
UiUtils.add(tmpPanel, distanceLabel, 5, 0, 1, 1);
UiUtils.add(innerPanel, tmpPanel, 0, 1, 1, 1);
UiUtils.add(innerPanel, new JLabel("Rabbits"), 0, 2, 3, 1);
tmpPanel = new JPanel(new GridBagLayout());
UiUtils.add(tmpPanel,new JLabel("Running"), 0, 0, 1, 1);
runningRabbitsLabel = new JLabel(String.valueOf(forest.getRabbits().size()));
plainFont = runningRabbitsLabel.getFont().deriveFont(Font.PLAIN);
runningRabbitsLabel.setFont(plainFont);
runningRabbitsLabel.setForeground(Color.BLACK);
UiUtils.add(tmpPanel, runningRabbitsLabel, 1, 0, 1, 1);
UiUtils.add(tmpPanel, new JLabel("Saved"), 2, 0, 1, 1);
savedRabbitsLabel = new JLabel(String.valueOf(forest.getSavedRabbits().size()));
savedRabbitsLabel.setFont(plainFont);
savedRabbitsLabel.setForeground(Color.GREEN);
UiUtils.add(tmpPanel, savedRabbitsLabel, 3, 0, 1, 1);
UiUtils.add(tmpPanel, new JLabel("Dead"), 4, 0, 1, 1);
deadRabbitsLabel = new JLabel(String.valueOf(forest.getDeadRabbits().size()));
deadRabbitsLabel.setFont(plainFont);
deadRabbitsLabel.setForeground(Color.RED);
UiUtils.add(tmpPanel, deadRabbitsLabel, 5, 0, 1, 1);
UiUtils.add(innerPanel, tmpPanel, 0, 3, 1, 1);
add(innerPanel);
}
public void refresh() {
ammosCountLabel.setText(String.valueOf(hunter.getAmmos()));
hungryLevelLabel.setText(String.valueOf(hunter.getHungryLevel()));
distanceLabel.setText(String.valueOf(hunter.getDistance()));
runningRabbitsLabel.setText(String.valueOf(forest.getRabbits().size()));
savedRabbitsLabel.setText(String.valueOf(forest.getSavedRabbits().size()));
deadRabbitsLabel.setText(String.valueOf(forest.getDeadRabbits().size()));
}
}

View File

@@ -0,0 +1,28 @@
package fr.pavnay.rabbits.ui.panel;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* This class provides some hunt events
*
*/
public class LogPanel extends JPanel {
private static final long serialVersionUID = 3786097551382633033L;
public void addLog(String log) {
JLabel label = new JLabel(log);
label.setFont(label.getFont().deriveFont(Font.PLAIN));
add(label);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
}

View File

@@ -0,0 +1,8 @@
log4j.rootLogger=INFO, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d [%-5p] %c - %m%n
log4j.logger.fr.pavnay.rabbits=INFO

View File

@@ -0,0 +1,248 @@
package fr.pavnay.rabbits.engine;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.awt.Point;
import org.junit.Test;
import fr.pavnay.rabbits.model.Burrow;
import fr.pavnay.rabbits.model.Forest;
import fr.pavnay.rabbits.model.Hunter;
import fr.pavnay.rabbits.model.Rabbit;
import fr.pavnay.rabbits.model.enums.Speed;
import fr.pavnay.rabbits.model.enums.Status;
public class HuntEngineTest {
@Test
public void testUpdateStatusRunning() {
Hunter hunter = new Hunter(new Point(3, 2), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
engine.updateStatus();
assertEquals( "Default status is RUNNING", Status.RUNNING, engine.getStatus());
}
@Test
public void testUpdateStatusRabbitsWin() {
Hunter hunter = new Hunter(new Point(3, 2), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
hunter.setAmmos(0);
engine.updateStatus();
assertEquals( "Default status is RUNNING", Status.RABBITS_WIN, engine.getStatus());
}
@Test
public void testUpdateStatusHunterWins() {
Hunter hunter = new Hunter(new Point(3, 2), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
forest.getDeadRabbits().addAll(forest.getRabbits());
forest.getRabbits().clear();
engine.updateStatus();
assertEquals( "Default status is RUNNING", Status.HUNTER_WINS, engine.getStatus());
}
@Test
public void testUpdateStatusHunterLoose() {
Hunter hunter = new Hunter(new Point(3, 2), 10);
Forest forest = new Forest(10, 1, 2, 2);
HuntEngine engine = new HuntEngine(forest, hunter);
forest.getDeadRabbits().add(forest.getRabbits().get(0));
forest.getSavedRabbits().add(forest.getRabbits().get(1));
forest.getRabbits().clear();
engine.updateStatus();
assertEquals( "Default status is RUNNING", Status.RABBITS_WIN, engine.getStatus());
}
@Test
public void testMoveHunterCloseToDestination() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
hunter.setDestination(new Point(2, 0));
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
engine.move(hunter);
assertNotEquals("Destination has changed - X", 2, hunter.getDestination().getX());
assertNotEquals("Destination has changed - Y", 0, hunter.getDestination().getY());
}
@Test
public void testMoveHunterFarFromDestination() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
hunter.setDestination(new Point(20, 0));
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
engine.move(hunter);
assertNotEquals("Destination has changed - X", 20, hunter.getDestination().getX());
assertNotEquals("Destination has changed - Y", 0, hunter.getDestination().getY());
}
@Test
public void testMoveRabbitCloseToFreeBurrow() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Burrow burrow = forest.getBurrows().get(0);
burrow.getLocation().setLocation(2, 0);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setRefuge(burrow);
engine.move(rabbit);
assertFalse("Lucky rabbit doesn't run anymore", forest.getRabbits().contains(rabbit));
assertNull("No more destination", rabbit.getDestination());
}
@Test
public void testMoveRabbitCloseToNotFreeBurrow() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Burrow burrow = forest.getBurrows().get(0);
burrow.getLocation().setLocation(2, 0);
burrow.setFree(false);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setRefuge(burrow);
engine.move(rabbit);
assertTrue("Rabbit still running", forest.getRabbits().contains(rabbit));
assertEquals("New destination : next burrow",forest.getBurrows().get(1).getLocation(), rabbit.getDestination());
}
@Test
public void testMoveRabbitToFarFromBurrow() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Burrow burrow = forest.getBurrows().get(0);
burrow.getLocation().setLocation(20, 0);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setRefuge(burrow);
engine.move(rabbit);
assertTrue("Rabbit still running", forest.getRabbits().contains(rabbit));
assertEquals("Burrow still the destination", burrow.getLocation(), rabbit.getDestination());
}
@Test
public void testMoveRabbitCloseToDestination() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setDestination(new Point(3, 0));
engine.move(rabbit);
assertNotEquals("New destination", new Point(3, 0), rabbit.getDestination());
}
@Test
public void testMoveRabbitFarFromDestination() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setDestination(new Point(13, 0));
engine.move(rabbit);
assertEquals("Same destination", new Point(13, 0), rabbit.getDestination());
}
@Test
public void testSetRandomDestinationHunter() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
hunter.setDestination(null);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
engine.setRandomDestination(hunter);
assertNotNull("Destination set", hunter.getDestination());
}
@Test
public void testSetRandomDestinationRabbit() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.setDestination(null);
engine.setRandomDestination(rabbit);
assertNotNull("Destination set", rabbit.getDestination());
}
@Test
public void testGoHunter() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
hunter.setDestination(new Point(20, 20));
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
engine.go(hunter);
assertEquals("Location has changed", new Point(3, 3), hunter.getLocation());
}
@Test
public void testGoRabbit() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setDestination(new Point(13, 13));
engine.go(rabbit);
assertEquals("Location has changed", new Point(3, 3), rabbit.getLocation());
}
@Test
public void testSavedRabbit() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Forest forest = new Forest(10, 1, 2, 1);
HuntEngine engine = new HuntEngine(forest, hunter);
Burrow burrow = forest.getBurrows().get(0);
burrow.getLocation().setLocation(2, 0);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(new Point(0, 0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.setRefuge(burrow);
engine.go(rabbit);
assertEquals("Destination has not changed", new Point(5, 0), rabbit.getLocation());
}
}

View File

@@ -0,0 +1,246 @@
package fr.pavnay.rabbits.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.awt.Point;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
import org.junit.rules.ExpectedException;
@SuppressWarnings("deprecation")
public class ForestTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testInitSuccess() {
Forest forest = new Forest(10, 1, 1, 1);
assertNotNull("Forest created", forest);
}
@Test
public void testInitFailureAreaMin() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(JUnitMatchers.containsString("Area"));
new Forest(0, 1, 1, 1);
}
@Test
public void testInitFailureAreaMax() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(JUnitMatchers.containsString("Area"));
new Forest(11, 1, 1, 1);
}
@Test
public void testInitFailureTreesMin() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(JUnitMatchers.containsString("Tree"));
new Forest(1, 0, 1, 1);
}
@Test
public void testInitFailureTreesMax() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(JUnitMatchers.containsString("Tree"));
new Forest(10, 1001, 1, 1);
}
@Test
public void testInitFailureNoRabbits() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(JUnitMatchers.containsString("rabbits"));
new Forest(10, 100, 1, 0);
}
@Test
public void testInitFailureNotEnoughBurrows() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage(JUnitMatchers.containsString("burrow"));
new Forest(10, 100, 1, 2);
}
@Test
public void testIsBetweenSuccess() {
Hunter hunter = new Hunter(new Point(0,0), 10);
Forest forest = new Forest(10, 1, 1, 1);
Rabbit rabbit = new Rabbit(new Point(4, 4));
Tree tree = new Tree(new Point(2, 2));
assertTrue("Tree is between hunter and rabbit", forest.isBetween(hunter.getLocation(), rabbit.getLocation(), tree.getLocation()));
}
@Test
public void testCanViewSuccess() {
Hunter hunter = new Hunter(new Point(0,0), 10);
Forest forest = new Forest(10, 2, 1, 1);
forest.getTrees().get(0).getLocation().setLocation(10, 0);
forest.getTrees().get(1).getLocation().setLocation(20, 0);
Rabbit rabbit = new Rabbit(new Point(4, 4));
assertTrue("No Tree between hunter and rabbit", forest.canView(hunter, rabbit));
}
@Test
public void testCanViewFailure() {
Hunter hunter = new Hunter(new Point(0,0), 10);
Forest forest = new Forest(10, 2, 1, 1);
forest.getTrees().get(0).getLocation().setLocation(2, 2);
forest.getTrees().get(1).getLocation().setLocation(2, 0);
Rabbit rabbit = new Rabbit(new Point(4, 4));
assertFalse("Tree is between hunter and rabbit", forest.canView(hunter, rabbit));
}
@Test
public void testIsBetweenFailureTreeBehind() {
Hunter hunter = new Hunter(new Point(0,0), 10);
Forest forest = new Forest(10, 1, 1, 1);
Rabbit rabbit = new Rabbit(new Point(4, 4));
Tree tree = new Tree(new Point(5, 5));
assertFalse("Tree is behind the rabbit", forest.isBetween(hunter.getLocation(), rabbit.getLocation(), tree.getLocation()));
}
@Test
public void testIsBetweenFailureTreeSomewhere() {
Hunter hunter = new Hunter(new Point(0,0), 10);
Forest forest = new Forest(10, 1, 1, 1);
Rabbit rabbit = new Rabbit(new Point(4, 4));
Tree tree = new Tree(new Point(10, 5));
assertFalse("Tree is not in the hunter-rabbit aligment", forest.isBetween(hunter.getLocation(), rabbit.getLocation(), tree.getLocation()));
}
@Test
public void testKilledRabbitSuccess() {
Forest forest = new Forest(10, 1, 2, 2);
Rabbit rabbit = forest.getRabbits().get(0);
forest.killedRabbit(rabbit);
assertFalse("Poor killed rabbit still running", forest.getRabbits().contains(rabbit));
assertEquals("Still one rabbit alive", 1, forest.getRabbits().size());
assertTrue("Poor killed rabbit can't run", forest.getDeadRabbits().contains(rabbit));
assertEquals("One dead rabbit", 1, forest.getDeadRabbits().size());
}
@Test
public void testSavedRabbitSuccess() {
Forest forest = new Forest(10, 1, 1, 1);
Rabbit rabbit = forest.getRabbits().get(0);
forest.savedRabbit(rabbit);
assertFalse("Poor saved rabbit still running", forest.getRabbits().contains(rabbit));
assertTrue("Lucky rabbit can't run", forest.getSavedRabbits().contains(rabbit));
}
@Test
public void testGetRabbitsInRangeSuccess() {
Hunter hunter = new Hunter(new Point(0,0), 10);
Forest forest = new Forest(10, 1, 2, 2);
forest.getRabbits().get(0).getLocation().setLocation(0, 5); // First rabbit in range
forest.getRabbits().get(1).getLocation().setLocation(0, 15); // Seconde one out of range
List<Rabbit> rabbitsInRange = forest.getRabbitsInRange(hunter);
assertEquals("One rabbit in range", 1, rabbitsInRange.size());
assertTrue("First rabbit in range", rabbitsInRange.contains(forest.getRabbits().get(0)));
assertFalse("Second rabbit out of range", rabbitsInRange.contains(forest.getRabbits().get(1)));
}
@Test
public void testGetNearestBurrowFirst() {
Hunter hunter = new Hunter(new Point(0, 1), 10);
Forest forest = new Forest(10, 1, 2, 1);
forest.getBurrows().get(0).getLocation().setLocation(7, 2);
forest.getBurrows().get(1).getLocation().setLocation(9, 0);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(4, 1);
assertEquals("Second burrow is nearest from rabbit", forest.getBurrows().get(0), forest.getNearestBurrow(hunter, rabbit));
}
@Test
public void testGetNearestBurrowSecond() {
Hunter hunter = new Hunter(new Point(0, 1), 10);
Forest forest = new Forest(10, 1, 2, 1);
forest.getBurrows().get(0).getLocation().setLocation(9, 0);
forest.getBurrows().get(1).getLocation().setLocation(7, 2);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(4, 1);
assertEquals("Second burrow is nearest from rabbit", forest.getBurrows().get(1), forest.getNearestBurrow(hunter, rabbit));
}
@Test
public void testGetNearestBurrowWorstIsBetter() {
Hunter hunter = new Hunter(new Point(0, 1), 10);
Forest forest = new Forest(10, 1, 2, 1);
forest.getBurrows().get(0).getLocation().setLocation(7, 2);
forest.getBurrows().get(1).getLocation().setLocation(3, 2);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(4, 1);
assertEquals("Second burrow is nearest from rabbit", forest.getBurrows().get(1), forest.getNearestBurrow(hunter, rabbit));
}
@Test
public void testGetNearestBurrowNotVisited() {
Hunter hunter = new Hunter(new Point(0, 1), 10);
Forest forest = new Forest(10, 1, 2, 1);
forest.getBurrows().get(0).getLocation().setLocation(7, 2);
forest.getBurrows().get(1).getLocation().setLocation(9, 0);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(4, 1);
rabbit.setRefuge(forest.getBurrows().get(0));
assertEquals("Second burrow was not visited by the rabbit", forest.getBurrows().get(1), forest.getNearestBurrow(hunter, rabbit));
}
@Test
public void testEscapeScared() {
Hunter hunter = new Hunter(new Point(3, 2), 10);
Forest forest = new Forest(10, 1, 1, 1);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(6, 3);
rabbit.setScared(true);
forest.escape(hunter, rabbit);
Burrow burrow = forest.getBurrows().get(0);
assertEquals("Right location X", burrow.getLocation().getX(), rabbit.getDestination().getX(), 0);
assertEquals("Right location Y", burrow.getLocation().getY(), rabbit.getDestination().getY(), 0);
}
@Test
public void testEscapeNotScared() {
Hunter hunter = new Hunter(new Point(3, 2), 10);
Forest forest = new Forest(10, 1, 2, 1);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(6, 3);
forest.escape(hunter, rabbit);
assertEquals("Right location X", 9, rabbit.getDestination().getX(), 0);
assertEquals("Right location Y", 4, rabbit.getDestination().getY(), 0);
}
@Test
public void testEscapeNotScaredOutOfForest() {
Hunter hunter = new Hunter(new Point(5, 3), 10);
Forest forest = new Forest(10, 1, 2, 1);
Rabbit rabbit = forest.getRabbits().get(0);
rabbit.getLocation().setLocation(2, 2);
forest.escape(hunter, rabbit);
assertEquals("Right location X", 0, rabbit.getDestination().getX(), 0);
assertEquals("Right location Y", 1, rabbit.getDestination().getY(), 0);
}
}

View File

@@ -0,0 +1,108 @@
package fr.pavnay.rabbits.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.awt.Point;
import org.junit.Test;
public class HunterTest {
@Test
public void testInRangeSuccess() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(10, 0));
assertTrue("Rabbit is in hunter range", hunter.isInRange(rabbit));
}
@Test
public void testInRangeFailure() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(11, 0));
assertFalse("Rabbit is out of hunter range", hunter.isInRange(rabbit));
}
@Test
public void testShootSuccess() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(0, 0));
assertTrue("Rabbit is close to the hunter. Shot successfully", hunter.shoot(rabbit));
assertEquals("Hunter's ammos decreases", 9, hunter.getAmmos());
}
@Test
public void testShootNoMoreAmmosFailure() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(0, 0));
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
hunter.shoot(rabbit);
assertFalse("No more ammos. Shot failure", hunter.shoot(rabbit));
}
@Test
public void testShootFailure() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(10, 0));
assertFalse("Rabbit is out of hunter's range. Shot failure", hunter.shoot(rabbit));
assertEquals("Hunter's ammos decreases", 9, hunter.getAmmos());
}
@Test
public void testGetAccuracyRate() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(5, 0));
assertEquals("Hunter's half rate to shot", hunter.getAccuracyRate(rabbit), 0.5, 0);
}
@Test
public void testGetAccuracyRateHalfAmmo() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(5, 0));
hunter.setAmmos(5);
assertEquals("Hunter's half rate to shot", hunter.getAccuracyRate(rabbit), 0.475, 0);
}
@Test
public void testGetAccuracyRateHalfHungryLevel() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(5, 0));
hunter.setHungryLevel(5);
assertEquals("Hunter's half hungry level", hunter.getAccuracyRate(rabbit), 0.45, 0);
}
@Test
public void testGetAccuracyRateFullHungryLevel() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(5, 0));
hunter.setHungryLevel(10);
assertEquals("Hunter's half hungry level", hunter.getAccuracyRate(rabbit), 0.4, 0.001);
}
@Test
public void testGetAccuracyRateHalfAmmosHalfHungryLevel() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(5, 0));
hunter.setAmmos(5);
hunter.setHungryLevel(5);
assertEquals("Hunter's half hungry level", hunter.getAccuracyRate(rabbit), 0.425, 0.001);
}
@Test
public void testGetAccuracyRateHalfAmmosFullHungryLevel() {
Hunter hunter = new Hunter(new Point(0, 0), 10);
Rabbit rabbit = new Rabbit(new Point(5, 0));
hunter.setAmmos(5);
hunter.setHungryLevel(10);
assertEquals("Hunter's half hungry level", hunter.getAccuracyRate(rabbit), 0.375, 0);
}
}

View File

@@ -0,0 +1,29 @@
package fr.pavnay.rabbits.model;
import java.awt.Point;
import org.junit.Test;
import fr.pavnay.rabbits.model.enums.Speed;
import static org.junit.Assert.assertEquals;
public class RabbitTest {
@Test
public void testSlowDown() {
Rabbit rabbit = new Rabbit(new Point(0,0));
rabbit.setSpeed(Speed.MEDIUM.getSpeed());
rabbit.slowDown();
assertEquals("Rabbit slow down", Speed.MEDIUM.getSpeed() - 1, rabbit.getSpeed());
}
@Test
public void testSlowDownStopped() {
Rabbit rabbit = new Rabbit(new Point(0,0));
rabbit.setSpeed(Speed.SLOW.getSpeed());
rabbit.slowDown();
rabbit.slowDown();
assertEquals("Rabbit stopped", Speed.STOPPED.getSpeed(), rabbit.getSpeed());
}
}