If you have questions … 11
What to turn in … 12
Introduction
The purpose of this assignment is to give you practice writing loops, using arrays and lists, and,
most importantly, to get some experience putting together a working application involving
several interacting Java classes.
There are three classes for you to implement: LizardGame, Lizard, and GameFileUtil. As
always, your primary responsibility is to implement these classes according to the specification
and test them carefully. The three classes can be used, along with some other components, to
create an implementation of a game we call Lizards, which is a mix between the classic snake
game1 and a sliding blocks puzzle game.
The game is played on a 2-dimensional grid of “cells”. Each cell may contain a wall, an exit, or
the body segment of a lizard. Cells are located by column and row.
Figure 1
The user presses down the left mouse button and drags lizards to move them around with the
goal of getting all the lizards to the exit. A lizard’s body is multi-segmented and the user can
press and drag any segment. The lizard moves in a snake-like fashion, that is, each segment must
follow in the path of the segments in front or behind of them when the lizard moves forward or
backward respectively. The only exception is when the user clicks and drags the head or tail
1 https://en.wikipedia.org/wiki/Snake_(video_game_genre)
segments, which can move in any direction. There are only four possible directions of
movement: up, down, right, left (never diagonally)
package hw3;

import java.util.ArrayList;

import api.BodySegment;
import api.Cell;
import api.Direction;

/
* Represents a Lizard as a collection of body segments.
*/
public class Lizard {
private ArrayList<BodySegment> segments;
/
* Constructs a Lizard object.
*/
public Lizard() {
this.segments = new ArrayList<>();
}


/
* Sets the segments of the lizard. Segments should be ordered from tail to
* head.
*
* @param segments list of segments ordered from tail to head
*/
public void setSegments(ArrayList<BodySegment> segments) {
this.segments = segments;
}

/
* Gets the segments of the lizard. Segments are ordered from tail to head.
*
* @return a list of segments ordered from tail to head
*/
public ArrayList<BodySegment> getSegments() {
if (segments == null) {
return new ArrayList<>();
}
return segments;
}



/
* Gets the head segment of the lizard. Returns null if the segments have not
* been initialized or there are no segments.
*
* @return the head segment
*/
public BodySegment getHeadSegment() {
if (segments.isEmpty()) {
return null;
}
return segments.get(segments.size() - 1);
}

/
* Gets the tail segment of the lizard. Returns null if the segments have not
* been initialized or there are no segments.
*
* @return the tail segment
*/
public BodySegment getTailSegment() {
if (segments.isEmpty()) {
return null;
}
return segments.get(0);
}

/
* Gets the segment that is located at a given cell or null if there is no
* segment at that cell.
*
* @param cell to look for lizard
* @return the segment that is on the cell or null if there is none
*/
public BodySegment getSegmentAt(Cell cell) {
for (BodySegment segment : segments) {
if (segment.getCell() == cell) {
return segment;
}
}
return null;
}

/
* Get the segment that is in front of (closer to the head segment than) the
* given segment. Returns null if there is no segment ahead.
*
* @param segment the starting segment
* @return the segment in front of the given segment or null
*/
public BodySegment getSegmentAhead(BodySegment segment) {
int index = segments.indexOf(segment);
if (index == -1 || index == segments.size() - 1) {
return null; // No segment ahead or given segment not found
}
return segments.get(index + 1);
}

/
* Get the segment that is behind (closer to the tail segment than) the given
* segment. Returns null if there is not segment behind.
*
* @param segment the starting segment
* @return the segment behind of the given segment or null
*/
public BodySegment getSegmentBehind(BodySegment segment) {
int index = segments.indexOf(segment);
if (index <= 0) {
return null; // No segment behind or given segment not found
}
return segments.get(index - 1);
}

/
* Gets the direction from the perspective of the given segment point to the
* segment ahead (in front of) of it. Returns null if there is no segment ahead
* of the given segment.
*
* @param segment the starting segment
* @return the direction to the segment ahead of the given segment or null
*/
public Direction getDirectionToSegmentAhead(BodySegment segment) {
BodySegment aheadSegment = getSegmentAhead(segment);
if (aheadSegment == null) {
return null;
}
Cell currentCell = segment.getCell();
Cell aheadCell = aheadSegment.getCell();
return calculateDirection(currentCell, aheadCell);
}

/
* Gets the direction from the perspective of the given segment point to the
* segment behind it. Returns null if there is no segment behind of the given
* segment.
*
* @param segment the starting segment
* @return the direction to the segment behind of the given segment or null
*/
public Direction getDirectionToSegmentBehind(BodySegment segment) {
BodySegment behindSegment = getSegmentBehind(segment);
if (behindSegment == null) {
return null;
}
Cell currentCell = segment.getCell();
Cell behindCell = behindSegment.getCell();
return calculateDirection(currentCell, behindCell);
}

/
* Gets the direction in which the head segment is pointing. This is the
* direction formed by going from the segment behind the head segment to the
* head segment. A lizard that does not have more than one segment has no
* defined head direction and returns null.
*
* @return the direction in which the head segment is pointing or null
*/
public Direction getHeadDirection() {
BodySegment head = getHeadSegment();
if (head == null || segments.size() <= 1) {
return null;
}
BodySegment behindHead = getSegmentBehind(head);
return calculateDirection(behindHead.getCell(), head.getCell());
}

/
* Gets the direction in which the tail segment is pointing. This is the
* direction formed by going from the segment ahead of the tail segment to the
* tail segment. A lizard that does not have more than one segment has no
* defined tail direction and returns null.
*
* @return the direction in which the tail segment is pointing or null
/
public Direction getTailDirection() {
BodySegment tail = getTailSegment();
if (tail == null || segments.size() <= 1) {
return null;
}
BodySegment aheadTail = getSegmentAhead(tail);
return calculateDirection(aheadTail.getCell(), tail.getCell());
}

private Direction calculateDirection(Cell currentCell, Cell targetCell) {
int rowDiff = targetCell.getRow() - currentCell.getRow();
int colDiff = targetCell.getCol() - currentCell.getCol();
if (rowDiff == -1 && colDiff == 0) {
return Direction.UP;
} else if (rowDiff == 1 && colDiff == 0) {
return Direction.DOWN;
} else if (rowDiff == 0 && colDiff == 1) {
return Direction.RIGHT;
} else if (rowDiff == 0 && colDiff == -1) {
return Direction.LEFT;
} else {
// Invalid direction
return null;
}
}

@Override
public String toString() {
String result = “”;
for (BodySegment seg : getSegments()) {
result += seg + " ";
}
return result;
}
}
package hw3;


import api.BodySegment;
import static api.Direction.;

import java.util.ArrayList;


import api.Cell;
import api.Direction;
import api.Exit;
import api.ScoreUpdateListener;
import api.ShowDialogListener;
import api.Wall;

/
* Class that models a game.
*/
public class LizardGame {
private int width;
private int height;
private Cell[][] grid;
private ArrayList<Lizard> lizards;
private ShowDialogListener dialogListener;
private ScoreUpdateListener scoreListener;

/
* Constructs a new LizardGame object with given grid dimensions.
*
* @param width number of columns
* @param height number of rows
*/
public LizardGame(int width, int height) {
this.width = width;
this.height = height;
grid = new Cell[width][height];
for (int col = 0; col < width; col++) {
for (int row = 0; row < height; row++) {
grid[col][row] = new Cell(col, row);
}
}
lizards = new ArrayList<>();
}

/
* Get the grid’s width.
*
* @return width of the grid
*/
public int getWidth() {
// TODO: method stub
return width;
}

/
* Get the grid’s height.
*
* @return height of the grid
*/
public int getHeight() {
// TODO: method stub
return height;
}

/
* Adds a wall to the grid.
* <p>
* Specifically, this method calls placeWall on the Cell object associated with
* the wall (see the Wall class for how to get the cell associated with the
* wall). This class assumes a cell has already been set on the wall before
* being called.
*
* @param wall to add
*/
public void addWall(Wall wall) {
Cell cell = wall.getCell();
cell.placeWall(wall);
}

/
* Adds an exit to the grid.
* <p>
* Specifically, this method calls placeExit on the Cell object associated with
* the exit (see the Exit class for how to get the cell associated with the
* exit). This class assumes a cell has already been set on the exit before
* being called.
*
* @param exit to add
*/
public void addExit(Exit exit) {
exit.getCell().placeExit(exit);
}

/
* Gets a list of all lizards on the grid. Does not include lizards that have
* exited.
*
* @return lizards list of lizards
*/
public ArrayList<Lizard> getLizards() {
// TODO: method stub
return lizards;
}

/
* Adds the given lizard to the grid.
* <p>
* The scoreListener to should be updated with the number of lizards.
*
* @param lizard to add
*/
public void addLizard(Lizard lizard) {
lizards.add(lizard);
if (scoreListener != null) {
scoreListener.updateScore(lizards.size());
}
}

/
* Removes the given lizard from the grid. Be aware that each cell object knows
* about a lizard that is placed on top of it. It is expected that this method
* updates all cells that the lizard used to be on, so that they now have no
* lizard placed on them.
* <p>
* The scoreListener to should be updated with the number of lizards using
* updateScore().
*
* @param lizard to remove
*/
public void removeLizard(Lizard lizard) {
lizards.remove(lizard);
for (BodySegment segment : lizard.getSegments()) {
segment.getCell().removeLizard();
}
if (scoreListener != null) {
scoreListener.updateScore(lizards.size());
}
}

/
* Gets the cell for the given column and row.
* <p>
* If the column or row are outside of the boundaries of the grid the method
* returns null.
*
* @param col column of the cell
* @param row of the cell
* @return the cell or null
*/
public Cell getCell(int col, int row) {
if (col < 0 || col >= width || row < 0 || row >= height) {
return null;
}
return grid[col][row];
}

/
* Gets the cell that is adjacent to (one over from) the given column and row,
* when moving in the given direction. For example (1, 4, UP) returns the cell
* at (1, 3).
* <p>
* If the adjacent cell is outside of the boundaries of the grid, the method
* returns null.
*
* @param col the given column
* @param row the given row
* @param dir the direction from the given column and row to the adjacent cell
* @return the adjacent cell or null
*/
public Cell getAdjacentCell(int col, int row, Direction dir) {
switch (dir) {
case UP:
return getCell(col, row - 1);
case DOWN:
return getCell(col, row + 1);
case LEFT:
return getCell(col - 1, row);
case RIGHT:
return getCell(col + 1, row);
default:
return null;
}
}

/
* Resets the grid. After calling this method the game should have a grid of
* size width x height containing all empty cells. Empty means cells with no
* walls, exits, etc.
* <p>
* All lizards should also be removed from the grid.
*
* @param width number of columns of the resized grid
* @param height number of rows of the resized grid
*/
public void resetGrid(int width, int height) {
this.width = width;
this.height = height;
grid = new Cell[width][height];
for (int col = 0; col < width; col++) {
for (int row = 0; row < height; row++) {
grid[col][row] = new Cell(col, row);
}
}
lizards.clear();
}

/
* Returns true if a given cell location (col, row) is available for a lizard to
* move into. Specifically the cell cannot contain a wall or a lizard. Any other
* type of cell, including an exit is available.
*
* @param row of the cell being tested
* @param col of the cell being tested
* @return true if the cell is available, false otherwise
*/
public boolean isAvailable(int col, int row) {
Cell cell = getCell(col, row);
return cell != null && cell.getWall() == null && cell.getLizard() == null;
}

/
* Move the lizard specified by its body segment at the given position (col,
* row) one cell in the given direction. The entire body of the lizard must move
* in a snake like fashion, in other words, each body segment pushes and pulls
* the segments it is connected to forward or backward in the path of the
* lizard’s body. The given direction may result in the lizard moving its body
* either forward or backward by one cell.
* <p>
* The segments of a lizard’s body are linked together and movement must always
* be “in-line” with the body. It is allowed to implement movement by either
* shifting every body segment one cell over or by creating a new head or tail
* segment and removing an existing head or tail segment to achieve the same
* effect of movement in the forward or backward direction.
* <p>
* If any segment of the lizard moves over an exit cell, the lizard should be
* removed from the grid.
* <p>
* If there are no lizards left on the grid the player has won the puzzle the
* the dialog listener should be used to display (see showDialog) the message
* “You win!”.
* <p>
* It is possible that the given direction is not in-line with the body of the
* lizard (as described above), in that case this method should do nothing.
* <p>
* It is possible that the given column and row are outside the bounds of the
* grid, in that case this method should do nothing.
* <p>
* It is possible that there is no lizard at the given column and row, in that
* case this method should do nothing.
* <p>
* It is possible that the lizard is blocked and cannot move in the requested
* direction, in that case this method should do nothing.
* <p>
* <b>Developer’s note: You may have noticed that there are a lot of details
* that need to be considered when implement this method method. It is highly
* recommend to explore how you can use the public API methods of this class,
* Grid and Lizard (hint: there are many helpful methods in those classes that
* will simplify your logic here) and also create your own private helper
* methods. Break the problem into smaller parts are work on each part
* individually.</b>
*
* @param col the given column of a selected segment
* @param row the given row of a selected segment
* @param dir the given direction to move the selected segment
*/
public void move(int col, int row, Direction dir) {
// Get the cell at the specified position
Cell currentCell = getCell(col, row);
if (currentCell == null || currentCell.getLizard() == null) {
// If there is no lizard at the given position or the position is out of bounds, do nothing
return;
}

// Get the lizard at the specified position
Lizard lizard = currentCell.getLizard();

// Determine the target cell based on the given direction
Cell targetCell = getAdjacentCell(col, row, dir);
if (targetCell != null && isAvailable(targetCell.getCol(), targetCell.getRow())) {
// Move the lizard to the target cell
targetCell.placeLizard(lizard);
currentCell.removeLizard();

// Check if any segment of the lizard moves over an exit cell
if (targetCell.getExit() != null) {
// If so, remove the lizard from the grid
removeLizard(lizard);
}

// Check if there are no lizards left on the grid
if (lizards.isEmpty()) {
// If so, notify the player that they have won the puzzle
if (dialogListener != null) {
dialogListener.showDialog(“You win!”);
}
}
}
}


/
* Sets callback listeners for game events.
*
* @param dialogListener listener for creating a user dialog
* @param scoreListener listener for updating the player’s score
*/
public void setListeners(ShowDialogListener dialogListener, ScoreUpdateListener scoreListener) {
this.dialogListener = dialogListener;
this.scoreListener = scoreListener;
}

/*
* Load the game from the given file path
*
* @param filePath location of file to load
/
public void load(String filePath) {
GameFileUtil.load(filePath, this);
}

@Override
public String toString() {
String str = “---------- GRID ----------\n”;
str += “Dimensions:\n”;
str += getWidth() + " " + getHeight() + “\n”;
str += “Layout:\n”;
for (int y = 0; y < getHeight(); y++) {
if (y > 0) {
str += “\n”;
}
for (int x = 0; x < getWidth(); x++) {
str += getCell(x, y);
}
}
str += “\nLizards:\n”;
for (Lizard l : getLizards()) {
str += l;
}
str += “\n--------------------------\n”;
return str;
}
}
/Library/Java/JavaVirtualMachines/liberica-jdk-21-full.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=65169:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Users/ajglas/Desktop/college/COMS227/hw3/out/production/hw3:/Users/ajglas/.m2/repository/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar:/Users/ajglas/.m2/repository/com/icegreen/greenmail-junit5/2.0.0/greenmail-junit5-2.0.0.jar:/Users/ajglas/.m2/repository/com/icegreen/greenmail/2.0.0/greenmail-2.0.0.jar:/Users/ajglas/.m2/repository/com/sun/mail/jakarta.mail/2.0.1/jakarta.mail-2.0.1.jar:/Users/ajglas/.m2/repository/com/sun/activation/jakarta.activation/2.0.1/jakarta.activation-2.0.1.jar:/Users/ajglas/.m2/repository/jakarta/activation/jakarta.activation-api/2.0.1/jakarta.activation-api-2.0.1.jar:/Users/ajglas/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar:/Users/ajglas/.m2/repository/junit/junit/4.13.2/junit-4.13.2.jar:/Users/ajglas/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.9.2/junit-jupiter-api-5.9.2.jar:/Users/ajglas/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/Users/ajglas/.m2/repository/org/junit/platform/junit-platform-commons/1.9.2/junit-platform-commons-1.9.2.jar:/Users/ajglas/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/ajglas/Downloads/speccheck_hw3_v2.jar hw3.SpecChecker
2024-04-07 03:33:53.966 java[8633:2302130] WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES.
1. RUNNING TESTS for hw3
You received 32/38 points. 38 out of 44 tests pass.

PROBLEM:
23. LizardGame move head in a forward direction.
Message: 23. LizardGame move head in a forward direction. liz1:
expected:< (6,2,Ground,Lizard)> but was:< (5,2,Ground,Empty)>

PROBLEM:
25. LizardGame move tail in a backward direction.
Message: 25. LizardGame move tail in a backward direction. liz1:
expected:< (4,2,Ground,Lizard)> but was:< (5,2,Ground,Lizard)>

PROBLEM:
27. LizardGame move head backward.
Message: 27. LizardGame move head backward. liz1: expected:<
(4,2,Ground,Lizard)> but was:< (5,2,Ground,Lizard)>

PROBLEM:
29. LizardGame move tail forward.
Message: 29. LizardGame move tail forward. liz1: expected:<
(6,2,Ground,Empty)> but was:< (5,2,Ground,Lizard)>

PROBLEM:
31. LizardGame move inner segment forward.
Message: 31. LizardGame move inner segment forward. liz1: expected:<
(6,2,Ground,Empty)> but was:< (5,2,Ground,Lizard)>

PROBLEM:
33. LizardGame move inner segment backward.
Message: 33. LizardGame move inner segment backward. liz1:
expected:< (4,2,Ground,Lizard)> but was:< (5,2,Ground,Lizard)>

If you do not fix these problems, you are deviating
from the homework specification and may lose points.
Read error messages starting at the top. **
Zip file not created!

Process finished with exit code 0


please fix my move method