Skip to content

Commit

Permalink
Merge pull request #136 from IUS-CS/Vision-and-Movement
Browse files Browse the repository at this point in the history
Vision and movement
  • Loading branch information
joncrobe authored Dec 6, 2023
2 parents e1c01b4 + 3e9f5a1 commit 476bb6b
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 24 deletions.
157 changes: 156 additions & 1 deletion src/main/java/team/rocket/Map.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package team.rocket;

import team.rocket.Entities.AbstractOrganism;
import java.util.HashMap;
import java.util.Optional;
import java.util.Queue;
import java.util.LinkedList;

import team.rocket.Enums.Direction;
import team.rocket.Entities.AbstractAnimal;
import team.rocket.Entities.AbstractOrganism;
import team.rocket.Entities.Fox;
import team.rocket.Entities.Grass;
import team.rocket.Entities.Rabbit;

/**
* A Map contains the information about the arrangement of a set of simulated organisms.
Expand Down Expand Up @@ -283,4 +291,151 @@ public void moveOrganism(int currentRow, int currentCol, int newRow, int newCol)
grid[newRow][newCol] = grid[currentRow][currentCol];
grid[currentRow][currentCol] = null;
}

/**
* Finds the location of the nearest target to the given animal within the animal's range of vision
*
* @param animal the animal that is looking for a target
* @return an array containing the row and column of the nearest target to the animal or null if it can't
* find a target
*/
public Optional<int[]> locateNearestTarget(AbstractAnimal animal, int row, int column) {
Optional<int[]> location = Optional.empty();

Queue<int[]> queue = new LinkedList<>();
boolean targetFound = false;
int[] coordinates;

if (animal instanceof Rabbit) {
for (int checkingRange = 1; checkingRange <= Rabbit.getVision(); checkingRange++) { // Checks a rabbit's whole range of vision
/*
The following for loop adds locations to the queue in the following pattern (using a checking range
of 3 and a starting animal of a rabbit for example):
Before: i = 0: i = 1: i = 2:
. . . . . . . . . . X . . . . . . X . . . . . . X . . .
. . . . . . . . . . . . . . . . X . . . . . . X . X . .
. . . . . . . . . . . . . . . . . . . X . . X . . . X .
. . . R . . . X . . R . . X X . . R . . X X . . R . . X
. . . . . . . . . . . . . . . X . . . . . . X . . . X .
. . . . . . . . . . . . . . . . . . X . . . . X . X . .
. . . . . . . . . . X . . . . . . X . . . . . . X . . .
*/
int checkingRow;
int checkingColumn;
for (int i = 0; i < checkingRange; i++) {
checkingRow = row + i;
checkingColumn = column + (checkingRange - i);
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}

checkingRow = row + (checkingRange - i);
checkingColumn = column - i;
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}

checkingRow = row - i;
checkingColumn = column - (checkingRange - i);
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}

checkingRow = row - (checkingRange - i);
checkingColumn = column + i;
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}
}

while ((coordinates = queue.poll()) != null) { // Checks the coordinates currently in the queue
if (this.getOrganism(coordinates[0], coordinates[1]) instanceof Grass) {
location = Optional.of(coordinates);
targetFound = true;
break;
}
}

if (targetFound) {
break;
}
}

if (!targetFound) {
location = Optional.empty();
}
} else if (animal instanceof Fox) { // Checks a fox's whole range of vision
for (int checkingRange = 1; checkingRange <= Fox.getVision(); checkingRange++) { // Checks a rabbit's whole range of vision
/*
The following for loop adds locations to the queue in the following pattern (using a checking range
of 3 and a starting animal of a rabbit for example):
Before: i = 0: i = 1: i = 2:
. . . . . . . . . . X . . . . . . X . . . . . . X . . .
. . . . . . . . . . . . . . . . X . . . . . . X . X . .
. . . . . . . . . . . . . . . . . . . X . . X . . . X .
. . . R . . . X . . R . . X X . . R . . X X . . R . . X
. . . . . . . . . . . . . . . X . . . . . . X . . . X .
. . . . . . . . . . . . . . . . . . X . . . . X . X . .
. . . . . . . . . . X . . . . . . X . . . . . . X . . .
*/
int checkingRow;
int checkingColumn;
for (int i = 0; i < checkingRange; i++) {
checkingRow = row + i;
checkingColumn = column + (checkingRange - i);
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}

checkingRow = row + (checkingRange - i);
checkingColumn = column - i;
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}

checkingRow = row - i;
checkingColumn = column - (checkingRange - i);
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}

checkingRow = row - (checkingRange - i);
checkingColumn = column + i;
if ((checkingRow >= 0 && checkingRow < this.height) &&
(checkingColumn >= 0 && checkingColumn < this.width)) {
queue.add(new int[] {checkingRow, checkingColumn});
}
}

while ((coordinates = queue.poll()) != null) { // Checks the coordinates currently in the queue
if (this.getOrganism(coordinates[0], coordinates[1]) instanceof Rabbit) {
location = Optional.of(coordinates);
targetFound = true;
break;
}
}

if (targetFound) {
break;
}
}

if (!targetFound) {
location = Optional.empty();
}
} else {
location = Optional.empty();
}

return location;
}
}
57 changes: 41 additions & 16 deletions src/main/java/team/rocket/Simulation.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package team.rocket;

import java.lang.Runnable;
import java.util.Optional;

import team.rocket.Entities.AbstractAnimal;
import team.rocket.Entities.AbstractOrganism;
Expand Down Expand Up @@ -61,16 +62,16 @@ public Simulation(int mapWidth, int mapHeight) {
public Simulation() {
map = new Map();
//adds two rabbits to the top left corner when the simulation starts running
map.addOrganism(OrganismFactory.getInstance().createOrganism("Rabbit"), 0,0 );
map.addOrganism(OrganismFactory.getInstance().createOrganism("Rabbit"),0,1 );
map.addOrganism(OrganismFactory.getInstance().createOrganism("Rabbit"), 0, 0);
map.addOrganism(OrganismFactory.getInstance().createOrganism("Rabbit"), 0, 1);
}

/**
* Creates a simulation from a map
*
* @param m the map to create a simulation from
*/
public Simulation(Map m){
public Simulation(Map m) {
map = m;
}

Expand All @@ -79,10 +80,10 @@ public Simulation(Map m){
*/
@Override
public void run() {
if(printOutput) UI.outputGridViaMainThread(0, map);
if (printOutput) UI.outputGridViaMainThread(0, map);
for (currentDay = 1; currentDay <= daysPerRun; currentDay++) { // Iterates through each day
long startTime = TimeManager.getCurrentTime();
int millisecondsPerDay = millisecondsPerTimeStep*timeStepsPerDay;
int millisecondsPerDay = millisecondsPerTimeStep * timeStepsPerDay;

for (currentTimeStep = 1; currentTimeStep <= timeStepsPerDay; currentTimeStep++) { // Iterates through each time step in the current day
moveAnimal();
Expand All @@ -103,7 +104,7 @@ public void run() {
long currentTime = TimeManager.getCurrentTime();
if (currentTime < startTime + millisecondsPerDay) { // Only sleep if computation time didn't take long enough
try {
Thread.sleep(startTime + millisecondsPerDay-currentTime);
Thread.sleep(startTime + millisecondsPerDay - currentTime);

} catch (InterruptedException e) {
System.out.println("There was an unexpected issue with the simulation.");
Expand Down Expand Up @@ -182,10 +183,10 @@ private void breed() {
* Finds the closest empty tile to position y, x in the grid. Only searches within 1 tile orthogonally and
* diagonally.
*
* @param map the map to search
* @param y the center y position
* @param x the center x position
* @return an array with the y position then the x position
* @param map the map to search
* @param y the center y position
* @param x the center x position
* @return an array with the y position then the x position
*/
private int[] findClosestEmptyTile(Map map, int y, int x, int maxDistance) {
int[] closestEmptyTile = null;
Expand All @@ -211,15 +212,39 @@ private int[] findClosestEmptyTile(Map map, int y, int x, int maxDistance) {
private void moveAnimal() {
for (int i = 0; i < map.getHeight(); i++) { // Iterates through each row of the grid
for (int j = 0; j < map.getWidth(); j++) { // Iterates through each column of the grid
if (map.getOrganism(i, j) instanceof AbstractAnimal animal) { // Check if the object is an instance of AbstractAnimal
if (animal.isStarving()) {
map.removeOrganism(i, j);
animal.reduceCount();
break;
if (map.getOrganism(i, j) instanceof AbstractAnimal) { // Check if the object is an instance of AbstractAnimal
AbstractAnimal animal = (AbstractAnimal) map.getOrganism(i, j);
Optional<int[]> targetLocation = map.locateNearestTarget(animal, i, j);

if (targetLocation.isPresent()) {
int[] target = targetLocation.get();
int targetRow = target[0];
int targetColumn = target[1];

// Determine the direction to move
int newRow = i, newColumn = j;
if (i < targetRow && i < map.getHeight() - 1) {
newRow++; // Move down
} else if (i > targetRow && i > 0) {
newRow--; // Move up
}

if (j < targetColumn && j < map.getWidth() - 1) {
newColumn++; // Move right
} else if (j > targetColumn && j > 0) {
newColumn--; // Move left
}

// Move the animal to the new location
if (newRow != i || newColumn != j) { // Check if there is a change in position
map.moveOrganism(i, j, newRow, newColumn);
}
} else {
animal.move(map, i, j);
}
animal.move(map, i, j);
}
}
// ;)
}

for (int i = 0; i < map.getHeight(); i++) { // Iterates through each row of the grid
Expand Down
7 changes: 0 additions & 7 deletions src/test/resources/features/Simulation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ Feature: Simulation
Given There is an empty simulation
Then The getCurrentDay method returns a value equal to 0

# Testing that the program appropriately sleeps for timesteps
Scenario: Program duration should wait some ms per TimeStep at minimum on an empty simulation
Given There is an empty simulation
And The current time is recorded
When The run method is run for the simulation
Then The current time is greater than the expected offset

# Testing setDaysPerRun method
Scenario: Changing the DaysPerRun cause the number of days to have passed to change
Given There is an empty simulation
Expand Down

0 comments on commit 476bb6b

Please sign in to comment.