Question: Programming Assignment java Terriers and Squrrels Objectives Learn how to read and understand code Write code that fits into an existing game Keep the squirrels
Programming Assignment java
Terriers and Squrrels
Objectives
Learn how to read and understand code
Write code that fits into an existing game
Keep the squirrels alive as long as possible
Help the terriers hunt down the squirrels
Getting Started
Create a new Java project called P4 and import P4-starter.jar.
Download here: http://www.cs.colostate.edu/~cs165/.Spring18/assignments/P4/archive/P4-starter.jar
Your directory should look like this:
P4/ resources images iconFence.png iconGrass.png iconMunch.png iconSquirrel.png iconTerrier.png iconTree.png mazes SimpleGame.txt SquirrelEscape.txt SquirrelFence.txt SquirrelNightmare.txt TwoSquirrels.txt TwoTerriers.txt src Animal.java GameEngine.java Squirrel.java Terrier.java UserInterface.java
Run the GameEngine class with resources/mazes/SimpleGame.txt. The playing field will appear but not be updated correctly. If GameEngine is not running or has compile errors, get some help in the lab.
Description
The purpose of the assignment is to finish three Java classes that implement the behavior of:
Squirrels that are trying to evade terriers
Terriers that feel compelled to chase squirrels.
You will code the behavior of both animals. The game is played on a field, with some number of terriers and squirrels whose placement is determined by a file. The field is a two-dimensional array, with 'S' for Squirrel, 'D' for Terrier, 'T' for Tree, 'F' for Fence, and '-' for empty squares with Grass. Here is a description of the classes involved in this assignment:
The GameEngine class has the main method. It reads the file specified on the command line, and instantiates the UserInterface object to put up the playing field. Approximately every couple of seconds it asks each squirrel and terrier which way they want to move, then updates the playing field accordingly. You should NOT change this class.
The UserInterface class provides the graphics for the game. When instantiated by the GameEngine, it displays a window with the playing field and a counter. After each set of moves, the GameEngine asks the UserInterface object to redraw itself. The playing field array is passed to the UserInterface object in its constructor. You should NOT change this class.
The Animal class is an abstract class that has code that is shared between the Terrier and Squirrel class. You must complete several methods in this class, as described below. The GameEngine continually asks the terriers and squirrels to move, and this class helps with that, but defers parts of the task that are specific to a particular animal to the Terrier and Squirrel classes. The Animal class has code to figure out the closest terrier to a squirrel, and the closest squirrel to a terrier.
Squirrel behavior is defined in Squirrel.java. Squirrels look for the closest terrier and always move in the opposite direction. Squirrel must avoid running into other squirrels or terriers, but they can pass through fences or climb trees. If they make it to a tree, they are safe and disappear from the field.
Terrier behavior is defined in Terrier.java. Terriers always try to chase after the nearest squirrel and eat it. Terriers must avoid running into other terriers or going off the field, and they cannot climb trees or pass through fences.
The game continues until all squirrels are safe or eaten, or for 30 iterations, whichever comes first. None of the supplied fields require that many iterations. The GameEngine reports all movements and significant events. A UML diagram of the classes in the Terriers and Squirrels is shown below:
Testing
You must setup a run configuration for the project with the name of the text file containing the game. Start by testing the individual methods you have written, using "SimpleGame.txt", field, and make sure the Squirrel locates the closest Terrier, and moves in the opposite direction, and avoids going off the field. Then make sure the Terrier locates the closet Squirrel and chases it, and avoids going off the field. The simplest game has only one of each animal. Then proceed to the more difficult fields.
Instructions
Use the javadocs to read implementation details about each method
Javadocs Here: http://www.cs.colostate.edu/~cs165/.Spring18/assignments/P4/doc/javadoc
Here are the complete list of methods you must implement to complete the game, in the optimal order:
makeMove() in Animal.java
computeDistance() in Animal.java
findClosest() in Animal.java
findMove() in Terrier.java
findMove() in Squirrel.java
isValid() in Terrier.java
isValid() in Squirrel.java
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
public class GameEngine{
// Playing field
private char[][] field;
private int width;
private int height;
// Game variables
private static int counter = 0;
private boolean done = false;
// Animal arrays
private ArrayList
private ArrayList
// Entry point
public static void main(String[] args) {
// Create game, based on specified file
GameEngine game = new GameEngine();
game.readFile(args[0]);
game.height = game.field.length;
game.width = game.field[0].length;
// Initialize user interface
UserInterface ui = new UserInterface(game.height, game.width, game.field);
// Main loop
while(!game.done){
// Wait for awhile
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
// Advance game and redraw
game.done = game.next();
ui.redrawField(counter);
}
}
// Advance game
private boolean next(){
// Move squirrels
moveSquirrels();
// Process terriers
moveTerriers();
// Limit iterations
if (++counter > 30){
System.err.println("Game stopped after 30 rounds!");
return true;
}
// Any squirrels left?
if (squirrels.size() == 0) {
System.err.println("Oh my, all the squirrels are gone!");
return true;
}
return false;
}
// Move squirrels
private void moveSquirrels() {
// Use iterator to allow dynamic removal
for (Iterator
// Get next squirrel
Squirrel s = iterator.next();
// Call squirrel to find closest terrier
s.findClosest();
// Call squirrel to move and get position
s.moveAnimal();
int currentRow = s.getCurrentRow();
int currentCol = s.getCurrentCol();
int previousRow = s.getPreviousRow();
int previousCol = s.getPreviousCol();
// Erase previous squirrel
if ((previousRow != -1) &&
(field[previousRow][previousCol] != 'F') &&
(field[previousRow][previousCol] != 'D'))
field[previousRow][previousCol] = '-';
// Check for invalid move
if (currentRow < 0 || currentRow >= field.length ||
currentCol < 0 || currentCol >= field[0].length) {
System.err.print("Squirrel[" + squirrels.indexOf(s) + "] exited the board at ");
System.err.println("(" + currentRow + "," + currentCol + ")");
System.exit(0);
}
if (field[currentRow][currentCol] == 'D')
System.err.print("Squirrel[" + squirrels.indexOf(s) + "] moved into Terrier at ");
else if (field[currentRow][currentCol] == 'S')
System.err.print("Squirrel[" + squirrels.indexOf(s) + "] moved into Squirrel at ");
else if (field[currentRow][currentCol] == 'T')
System.err.print("Squirrel[" + squirrels.indexOf(s) + "] climbed tree at ");
else if (field[currentRow][currentCol] == 'F')
System.err.print("Squirrel[" + squirrels.indexOf(s) + "] under fence at ");
else
System.err.print("Squirrel[" + squirrels.indexOf(s) + "] moved to ");
System.err.println("(" + currentRow + "," + currentCol + ")");
// Special handling
if (field[currentRow][currentCol] == 'T') {
// Squirrel found tree
iterator.remove();
} else if (field[currentRow][currentCol] == 'F') {
// Squirrel under fence
} else {
// Valid move
field[currentRow][currentCol] = 'S';
}
}
}
// Move terriers
private void moveTerriers() {
// Without squirrels, don't bother
if (squirrels.size() == 0)
return;
// Iterate terriers
for (Terrier d : dogs) {
// Call terrier to find closest squirrel
d.findClosest();
// Call terrier to move and get position
d.moveAnimal();
int currentRow = d.getCurrentRow();
int currentCol = d.getCurrentCol();
int previousRow = d.getPreviousRow();
int previousCol = d.getPreviousCol();
// Erase previous terrier
if (previousRow != -1) field[previousRow][previousCol] = '-';
// Check for invalid move
if (currentRow < 0 || currentRow >= field.length ||
currentCol < 0 || currentCol >= field[0].length) {
System.err.print("Terrier[" + dogs.indexOf(d) + "] exited the board at ");
System.err.println("(" + currentRow + "," + currentCol + ")");
System.exit(0);
}
if (field[currentRow][currentCol] == 'D')
System.err.print("Terrier[" + dogs.indexOf(d) + "] moved into Terrier at ");
else if (field[currentRow][currentCol] == 'F')
System.err.print("Terrier[" + dogs.indexOf(d) + "] moved into Fence at ");
else if (field[currentRow][currentCol] == 'T')
System.err.print("Terrier[" + dogs.indexOf(d) + "] moved into Tree at");
else if (currentRow < 0)
System.err.print("Terrier[" + dogs.indexOf(d) + "] exited the top at ");
else if (currentRow >= field.length)
System.err.print("Terrier[" + dogs.indexOf(d) + "] exited the bottom at ");
else if (currentCol < 0)
System.err.print("Terrier[" + dogs.indexOf(d) + "] exited the left at ");
else if (currentCol >= field[0].length)
System.err.print("Terrier[" + dogs.indexOf(d) + "] exited the right at ");
else
System.err.print("Terrier[" + dogs.indexOf(d) + "] moved to ");
// Print location
System.err.println("(" + currentRow + "," + currentCol + ")");
// Ate a squirrel?
if (field[currentRow][currentCol] == 'S') {
// Munched squirrel
System.err.print("Terrier[" + dogs.indexOf(d) + "] ate Squirrel at ");
System.err.println("(" + currentRow + "," + currentCol + ")");
field[currentRow][currentCol] = 'M';
removeSquirrel(currentRow, currentCol);
} else {
// Valid move
field[currentRow][currentCol] = 'D';
}
}
}
// Remove a squirrel
private void removeSquirrel(int row, int col) {
Squirrel r = null;
for (Squirrel s : squirrels){
if (s.getCurrentRow() == row && s.getCurrentCol() == col)
r = s;
}
squirrels.remove(r);
}
// Read maze
private void readFile(String filename) {
try {
// Open file
Scanner scan = new Scanner(new File(filename));
// Read numbers
height = scan.nextInt();
width = scan.nextInt();
// Allocate maze
field = new char[height][width];
// Read maze
for (int row = 0; row < height; row++) {
// Read line
String line = scan.next();
for (int col = 0; col < width; col++) {
// Fill in character
field[row][col] = line.charAt(col);
// Make objects
if (field[row][col] == 'D') {
Terrier dog = new Terrier(row, col, field);
dogs.add(dog);
} else if (field[row][col] == 'S') {
Squirrel squirrel = new Squirrel(row, col, field);
squirrels.add(squirrel);
}
}
}
scan.close();
} catch (IOException e) {
System.err.println("Cannot read maze: " + filename);
System.exit(0);
}
}
}
// UserInterface.java - user interface for "Terriers and Squirrels"
// Author: Chris Wilcox
// With contributions from Ethan Lambert
// Date: 10/27/2016
// Course: CS163/CS164
// Email: wilcox@cs.colostate.edu
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
public class UserInterface extends JFrame {
private static final long serialVersionUID = 1L;
// User interface
private JPanel topPanel; // Points
private JPanel bottomPanel; // Board
private Image squirrel, dog, fence, tree, munch, grass; // Images
private JLabel message; // Label
private JButton buttons[][]; // Buttons
private Font font; // Font
// Game related
private char field[][];
public UserInterface(int height, int width, char[][] field) {
this.field = field;
// Look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Setup message
font = new Font("Arial", Font.PLAIN, 24);
message = new JLabel("");
message.setFont(font);
message.setForeground(new Color(0xFFFFFF));
// Top panel for message
topPanel = new JPanel();
topPanel.add(message);
topPanel.setBackground(new Color(0x0076A3));
// Bottom panel for board
bottomPanel = new JPanel();
bottomPanel.setLayout(new GridLayout(height, width, 0, 0));
add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH);
add(bottomPanel, BorderLayout.CENTER);
// Split panel
JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
sp.setEnabled(false);
sp.setDividerSize(0);
sp.add(topPanel);
sp.add(bottomPanel);
add(sp, BorderLayout.CENTER);
// Load images
ImageIcon icon0 = new ImageIcon("resources/images/iconTerrier.png");
Image image0 = icon0.getImage();
dog = image0.getScaledInstance(480 / width, 480 / width, Image.SCALE_DEFAULT);
ImageIcon icon1 = new ImageIcon("resources/images/iconSquirrel.png");
Image image1 = icon1.getImage();
squirrel = image1.getScaledInstance(480 / width, 480 / width, Image.SCALE_DEFAULT);
ImageIcon icon2 = new ImageIcon("resources/images/iconMunch.png");
Image image2 = icon2.getImage();
munch = image2.getScaledInstance(480 / width, 480 / width, Image.SCALE_DEFAULT);
ImageIcon icon3 = new ImageIcon("resources/images/iconFence.png");
Image image3 = icon3.getImage();
fence = image3.getScaledInstance(480 / width, 480 / width, Image.SCALE_DEFAULT);
ImageIcon icon4 = new ImageIcon("resources/images/iconTree.png");
Image image4 = icon4.getImage();
tree = image4.getScaledInstance(480 / width, 480 / width, Image.SCALE_DEFAULT);
ImageIcon icon5 = new ImageIcon("resources/images/iconGrass.png");
Image image5 = icon5.getImage();
grass = image5.getScaledInstance(480 / width, 480 / width, Image.SCALE_DEFAULT);
// Build panel of buttons
buttons = new JButton[height][width];
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
// Initialize and add button
JButton button = new JButton();
buttons[row][col] = button;
Border border = new LineBorder(Color.black, 1);
button.setOpaque(true);
button.setBackground(new Color(0x006600));
button.setBorder(border);
button.setName(row + "," + col);
bottomPanel.add(button);
}
}
redrawField(0);
// Window setup
setSize(540, 540); // fixed size
setTitle("Terriers and Squirrels");
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setAlwaysOnTop(true);
setVisible(true);
}
// Redraw the field
void redrawField(int counter) {
// Update Generation
message.setText("Terriers and Squirrels, Move: " + counter);
// Redraw board
for (int i = 0; i < field.length; i++) {
for (int j = 0; j < field[0].length; j++) {
switch (field[i][j]) {
case 'D':
buttons[i][j].setIcon(new ImageIcon(dog));
break;
case 'S':
buttons[i][j].setIcon(new ImageIcon(squirrel));
break;
case 'F':
buttons[i][j].setIcon(new ImageIcon(fence));
break;
case 'T':
buttons[i][j].setIcon(new ImageIcon(tree));
break;
case 'M':
buttons[i][j].setIcon(new ImageIcon(munch));
break;
default:
buttons[i][j].setIcon(new ImageIcon(grass));
}
buttons[i][j].repaint();
}
}
}
}
public abstract class Animal {
// Enumeration for animal moves
public enum eMove {
NO_MOVE,
LEFT,
UP_LEFT,
UP,
UP_RIGHT,
RIGHT,
DOWN_RIGHT,
DOWN,
DOWN_LEFT
}
// Instance data for animal classes
public int currentRow;
public int currentCol;
public int previousRow;
public int previousCol;
public int closestRow;
public int closestCol;
public char[][] field;
// Constructor for animal classes
public Animal(int initialRow, int initialCol, char[][] field){
this.currentRow = initialRow;
this.currentCol = initialCol;
this.previousRow = -1;
this.previousCol = -1;
this.closestRow = -1;
this.closestCol = -1;
this.field = field;
}
// Getters for animal classes
public int getCurrentRow(){ return currentRow; }
public int getCurrentCol(){ return currentCol; }
public int getPreviousRow(){ return previousRow; }
public int getPreviousCol(){ return previousCol; }
public int getClosestRow(){ return closestRow; }
public int getClosestCol(){ return closestCol; }
// ABSTRACT METHODS, must be implemented by terrier and squirrel
/**
* Figure out the next move for an animal
* @return best next move depending on if the animal is squirrel or terrier
*/
public abstract eMove findMove();
// Is the move valid for the animal?
/**
* Is the best move a valid move.
* @param row possible new move for row
* @param col possible new move for col
* @return true if the move is valid
*/
public abstract boolean isValid(int row, int col);
// IMPLEMENTED METHODS, shared behavior between terrier and squirrel
/**
* Find the closest other animal to current position.
* The remainder of the method is a nested loop traversing
* the field of play to find the closest animal of the
* opposite type. Call computeDistance each
* time from the inner loop. When you find the closest animal,
* store its position in closestRow and closestCol.
*/
void findClosest(){
double minimum = Double.MAX_VALUE;
closestRow = -1;
closestCol = -1;
// Terriers look for closest squirrels, and vice versa
char lookFor = ' ';
if (this instanceof Terrier) lookFor = 'S';
if (this instanceof Squirrel) lookFor = 'D';
// STUDENT CODE HERE
}
/**
* Find, adjust, make the move for an animal
*/
public void moveAnimal() {
makeMove(adjustMove(findMove()));
}
/**
* Adjust move to avoid obstacles
* @param move figure out which move is best and valid
* @return the best valid move
*/
private eMove adjustMove(eMove move) {
if (move == eMove.DOWN_LEFT)
if (!isValid(currentRow+1,currentCol-1))
move = eMove.LEFT;
if (move == eMove.LEFT)
if (!isValid(currentRow,currentCol-1))
move = eMove.UP_LEFT;
if (move == eMove.UP_LEFT)
if (!isValid(currentRow-1,currentCol-1))
move = eMove.UP;
if (move == eMove.UP)
if (!isValid(currentRow-1,currentCol))
move = eMove.UP_RIGHT;
if (move == eMove.UP_RIGHT)
if (!isValid(currentRow-1,currentCol+1))
move = eMove.RIGHT;
if (move == eMove.RIGHT)
if (!isValid(currentRow,currentCol+1))
move = eMove.DOWN_RIGHT;
if (move == eMove.DOWN_RIGHT)
if (!isValid(currentRow+1,currentCol+1))
move = eMove.DOWN;
if (move == eMove.DOWN)
if (!isValid(currentRow+1,currentCol))
move = eMove.DOWN_LEFT;
if (move == eMove.DOWN_LEFT)
if (!isValid(currentRow+1,currentCol-1))
move = eMove.NO_MOVE;
return move;
}
/**
* Move the animal.
* Finish the makeMove method that moves the animal based on the move
* returned from findMove and adjustMove. This is done by
* incrementing, decrementing the currentRow and currentCol, based on
* the parameter of type eMove passed in. Before changing the current
* position, you must set the previousRow and previousCol to the current
* position, to allow the UserInterface to erase the icon before moving
* the animal.
* @param move the direction the animal should move.
*/
private void makeMove(eMove move) {
// STUDENT CODE HERE
}
// UTILITY METHODS, just code to do stuff
/**
* Compute the Euclidean distance between two animals. Find the formula online.
* @param row0 row of the first animal
* @param col0 col of the first animal
* @param row1 row of the second animal
* @param col1 col of the second animal
* @return the distance between the two animals.
*/
double computeDistance(int row0, int col0, int row1, int col1) {
// STUDENT CODE HERE
return 0.0;
}
/**
* Figure out if move will stay on board
* @param row
* @param col
* @return
*/
public boolean stayOnBoard(int row, int col){
// Stay on board?
if (row < 0 || row >= field.length)
return false;
if (col < 0 || col >= field[0].length)
return false;
return true;
}
}
public class Squirrel extends Animal {
// Constructor
public Squirrel(int initialRow, int initialCol, char[][] field) {
super(initialRow, initialCol, field);
}
/**
* Make squirrel flee from closest terrier.
* use the location of the closest terrier (closestRow, closestCol),
* and return a variable of type eMove that represents a move away
* from the terrier. For example, if the closest terrier is left of
* your location (currentRow, currentCol), you should return eMove.RIGHT,
* and if the closest terrier is above and to the right of you, you
* should return eMove.DOWN_LEFT, etc. The reasoning here is that squirrels
* do not like terriers, for a perfectly good reason.
* @return the move away from the closest terrier
*/
public eMove findMove() {
// STUDENT CODE HERE
return eMove.RIGHT;
}
/**
* Figure out if move is valid for squirrel
* You should call the stayOnBoard method in Animal to figure out if
* the move will stay on the playing field. If not, check if the move
* will cause the squirrel to run into a terrier or another squirrel,
* and if so return false. Otherwise return true. You must examine the
* playing field to make this decision.
* @param row possible new move for row
* @param col possible new move for col
* @return returns a boolean that is true if the squirrel can move, and false otherwise.
*/
public boolean isValid(int row, int col){
// STUDENT CODE HERE
return true;
}
}
public class Terrier extends Animal {
// Constructor
public Terrier(int initialRow, int initialCol, char[][] field) {
super(initialRow, initialCol, field);
}
//
/**
* Make terrier chase closest squirrel
* Use the location of the closest squirrel (closestRow, closestCol),
* and return a variable of type eMove that represents a move towards
* the squirrel. For example, if the closest squirrel is left of your
* location (currentRow, currentCol), you should return eMove.LEFT,
* and if the closest squirrel is above and to the right, you should
* return eMove.UP_RIGHT, etc. The reasoning here is that terriers like
* squirrels, for all the wrong reasons! You should be able to leverage
* this method from Squirrel.java by reversing all the moves.
* @return the next move the Terrier should make
*/
public eMove findMove() {
// STUDENT CODE HERE
return eMove.LEFT;
}
/**
* Figure out if move is valid for terrier
* You should call the stayOnBoard method in Animal to figure
* out if the move will stay on the playing field. If not, check
* if the move will cause the terrier to run into another terrier,
* fence, or tree, and if so return false. Otherwise return true.
* You must examine the playing field to make this decision.
* You should be able to leverage this method from Squirrel.java,
* then change what you're looking for on the playing field.
* @param row possible new move for row
* @param col possible new move for col
* @return true if the terrier can move, and false otherwise
*/
public boolean isValid(int row, int col){
// STUDENT CODE HERE
return true;
}
}
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
