we recently somewhat learned Object Oriented Programming and already have a project on it due soon, so I am not too familiar with OOP. However, we were assigned a project of creating a Battleship game.
I have created a ship, square, and battle board class. I have tested all three, and all tests have also passed except for one method on Battleboard classs. To test each class, I used a toString method. This is in my battle board's toString method:
for (int i = 0; i < squares.length; i++) {
Just a couple of problems that I spotted, there can be more as the code is still incomplete:
A. In the very beginning of
public boolean addShip(int length, boolean isHorizontal, int startRow, int startCol) {
there is
square = new Square();
It seems logical to check whether a ship exists at the square to which you add it, but instead you check whether it exists in a brand new square. We don't have Square code, but I assume that it does not have a ship initially.
B. In the same method there is the following code:
if (isHorizontal == true) {
ship = new Ship(length, isHorizontal, startRow, startCol);
for (int i = 0; i < ship.getLength(); i++) {
if (startCol > numberOfColumns) {
return false;
} else {
square.addShip(ship);
startCol++;
}
}
}
So the ship is being repeatedly added to the same brand new square which is not related to the board. Later, that square is not added anywhere and now used anyhow.
I'm not sure how to fix this, but probably all the squares should have been inialized before this method is called, no squares should been created in this method; instead, you need to find a square corresponding to the current iteration`.
C. The following code
} else if (!square.hasBeenHit() && square.hasShip()) {
//Returns length of ship if there is a ship that hasn't been hit
if (ship.getLength() == 1) {
toString += "1 ";
} else if (ship.getLength() == 2) {
toString += "2 ";
} else if (ship.getLength() == 3) {
toString += "3 ";
} else if (ship.getLength() == 4) {
toString += "4 ";
}
}
uses the same ship on all iterations, so it behaves in the same way on all the iterations (appends 1). The correct thing would be to find a ship belonging to the current square (i, j), if it exists, and use it.
D. A brand new square is created on each loop, again, although you are iterating over squares! It would be more logical to write square = squares[i][j] instead of square = new Square().
I'd recommend you to use a debugger to see what happens in your code.
Related
public static int score(int[][] array, int win, int turn) {
int score = 0;
if (GamePrinciples.gameEnd(array, win)) {
if (GamePrinciples.draw(array)) {
score = 0;
} else if (GamePrinciples.winningBoard(array, win)[0] == 1) {
score = 1;
} else {
score = -1;
}
} else {
for (int[][] i : children(array, win, turn)) {
score += score(i, win, GamePrinciples.nextPlayer(turn));
}
}
return score;
}
briefly this program is part of my minimax algorithm. So the problem is that I get a stack over flow. Where am I going wrong?
if an array is in ending mode then if it is a draw it gives a score of zero if player one wins then a score of one and if player two wins it gives a score of two.
if the array is however not in the ending state we get the children of the array (immediate children that is the boards that result from the current board with only one move). The score of the board will be the sum of the score of each of its children. The logic seems okay and the other methods such as children, nextPlayer, winningBoard, draw all work fine with testing. So I am guessing there is problem with this kind of recursive implementation. Can anyone help? Thanks in advance
Your code seems wrong in the loop:
for (int[][] i : children(array, win, turn)) {
I haven’t tested, but you should call the method children() outside the for.
By calling the method within the for clause, you are always returning the initial array instead of iterating through it.
So try putting the children() method return to a variable and iterate through this variable.
Something like:
… c = children(…)
for(int[][] i : c) {
…
Good day, so I intend for my code to loop through my array and increment the row index of object by 1 position. I used timer task because I want the object to move forward after certain amount of time. This is the code I have tried. I have looked but I have struggled to find solution relevant to my problem. Would appreciate the help.
class cat_function extends TimerTask {
public void run() {
synchronized (game.board) {
for (int i = 0; i < game.board.length; i++) {
for (int k = 0; k < game.board[0].length; k++) {
if (game.board[i][k] instanceof cat) {
cat garfield = new cat(0, 0);
game.board[i][k] = garfield;
game.board[i][k + 1] = garfield;
}
}
}
}
}
}
Assuming:
game.board is defined as a Cat[][]
an empty cell's value is null
Then all you have to do is
if (game.board[i][k] instanceof cat) {
game.board[i][k + 1] = game.board[i][k]; // Put cat in new location
game.board[i][k] = null; // Remove cat from previous location
}
However, this code still has two problems
What do you do when you reach the edge of the board. You'll have to add logic to make it do something different so you don't fall of the edge.
There's no need to scan the entire game board every time just to find the Cat. Keep the cat's location (indexes) separately so you always know where it is and don't have to look for it.
If there can be more than one cat on the board you will also need logic to decide what happens if two cats "collide" when moving (i.e. you try to move a cat into a cell that already contains a cat).
Solving those problems is left as an exercise for you.
My flood fill method:
public void fillNeighbours(int x, int y) {
for(int i = -1; i < 2; i++) {
for(int j = -1; j < 2; j++) {
try {
visible[x+i][y+j] = true;
if(num[x+i][y+j] == 0) {
fillNeighbours(x+i, y+j);
}
} catch (ArrayIndexOutOfBoundsException ignored) {}
}
}
}
That catch (ArrayIndexOutOfBoundsException ignored) {} is there for avoiding x and/or y position from going outside of the array. Array size is 30 by 30. I'm making minesweeper like game. So you may know why I need this method and how it should work. If u don't know what minesweeper is then here is quick video about that game: Introduction to minesweeper
The code revisits fields which are set to visible already.
Try something like
if(!visible[x+i][y+j]){
visible[x+i][y+j] = true;
if(num[x+i][y+j] == 0) {
fillNeighbours(x+i, y+j);
}
}
It looks like you're recursively calling fillNeighbours without any break out clause (base case) so the calls fill the stack.
From
Wikistack
The tree laws of recursion are
A recursive algorithm must have a base case.
A recursive algorithm
must change its state and move toward the base case.
A recursive
algorithm must call itself, recursively.
Once fillNeighbours finds a cell and calls itself, the next loop will always call another when i and j are equal to zero. So it will never exit, and crash once the stack is full.
Aside from that, it will produce a very deep tree as it is not keeping track of which cells have been recursed, and will call fillNeighbours on the same cell multiple times.
Before asking my question I want to make some things clear. First, I am new to Java and programming in general. Second, This is my first post so please go easy on me if I did something wrong. Finally, I DO NOT want any specific solutions to my assignment in any responses to this post. Those issues are for me to figure out. What I do want is an explanation as to why my test code wont compile/run. To better understand the issue I will paste the assignment information, then the Driver class that is given, then my class code that is accessed by the Driver class. The compiler error I have is shown in the title, but since it is fairly vague, here is a screenshot of the exact errors I'm getting.
The following is the assignment:
You are to design a class “LostPuppy.java” which represents a puppy lost in a multi-floor building that
contains the same number of rooms on each floor. During instantiation (or creation) of an object of this class,
each room on each floor will be initialized as empty (you will actually use the space ‘ ‘ character for this
purpose) and a random room will be chosen where the puppy is lost. For this purpose, the character “P” will
be placed in this random location. Further details on the constructor is listed below.
An object of this class is used as a game for two players to take turns searching for the puppy, one room at a
time until the unfortunate little canine is found. The instantiation of this object and the search will be performed
by a “driver” program which has been provided to you allowing you to only have to concentrate on developing
the class (the driver program is in the file “PuppyPlay.java”)
Fields (of course, all fields are private):
A character (char) array named myHidingPlaces. This represents the building where the rows are floors
and the columns are rooms on each floor (this building has an unusual numbering system; the floors and
rooms both start at zero).
Two integers that will hold the floor and room where the puppy is lost, named myFloorLocation and
myRoomLocation.
A char named myWinner which will be assigned the player’s character when a player finds the puppy
(the driver program uses digits ‘1’ and ‘2’ to more clearly distinguish the players from the puppy).
A boolean named myFound which is set to true when the puppy is found.
Constructor:
Receives two integer parameters as the user’s input for the number of floors and rooms of the building
in which the puppy is lost.
The constructor instantiates the 2D array “myHidingPlaces” as a character array with the first parameter
for the rows (theFloors) and the second parameter as the columns (theRooms).
Initialize myHidingPlaces’ cells, each to contain a space ‘ ‘ (done with single quotes)
Set myFloorLocation (floor puppy is on) randomly based using the first parameter
Set myRoomLocation (room puppy is in) randomly based using the second parameter
Set myHidingPlaces[myFloorLocation][myRoomLocation] to the char ‘P’
Set myWinner to a single space
Set myFound to false
Methods:
roomSearchedAlready receives the floor and room to be searched and returns true if the room has
already been searched, false otherwise.
puppyLocation receives the floor and room to be searched and returns true if the floor and room are
where the puppy is lost, false otherwise. This method should NOT change any of the fields.
indicesOK receives the floor and room to be searched and returns true if the floor and room values are
within the array indices range, false otherwise (used to check that these indices will not cause an error
when applied to the array).
numberOfFloors returns how many floors are in the building (the first floor starts at zero).
numberOfRooms returns how many rooms are on each floor of the building (the first room starts at
zero and all floors have the same number of rooms).
searchRoom receives the floor and room to be searched and also the current player (as a char type)
and returns true if the puppy is found, false otherwise. If the puppy is NOT found searchRoom also
sets the myHidingPlaces array at the received floor and room location to the received player value (a ‘1’
or a ‘2’) OR, when found, sets the myWinner field to the current player AND sets myFound to true.
toString displays the current hidingPlaces array and it’s contents EXCEPT the location of the puppy
which remains hidden until he/she is found at which point toString will be called (by the driver) and both
the player who found the puppy and a ‘P’ will be displayed in the same cell….
NOW, and perhaps the awkward portion of the toString output. Normally, when displaying a 2D array,
the [0][0] cell is displayed in the upper left corner as with matrices. However, because the puppy
decided to get lost in a building and not a matrix, it would make more visual sense to have the first floor
(row 0) displayed on the bottom, second floor above it… and finally the top floor, well… on top! To
save words, look closely at the sample run provided on the next page. Your output should look the
same as what is seen on the next page in the sample run.
Here is the Driver program:
import java.util.Random;
import java.util.Scanner;
/**
* This program is used as a driver program to play the game from the
* class LostPuppy. Not to be used for grading!
*
* A puppy is lost in a multi-floor building represented in the class
* LostPuppy.class. Two players will take turns searching the building
* by selecting a floor and a room where the puppy might be.
*
* #author David Schuessler
* #version Spring 2015
*/
public class PuppyPlay
{
/**
* Driver program to play LostPuppy.
*
* #param theArgs may contain file names in an array of type String
*/
public static void main(String[] theArgs)
{
Scanner s = new Scanner(System.in);
LostPuppy game;
int totalFloors;
int totalRooms;
int floor;
int room;
char[] players = {'1', '2'};
int playerIndex;
boolean found = false;
Random rand = new Random();
do
{
System.out.print("To find the puppy, we need to know:\n"
+ "\tHow many floors are in the building\n"
+ "\tHow many rooms are on the floors\n\n"
+ " Please enter the number of floors: ");
totalFloors = s.nextInt();
System.out.print("Please enter the number of rooms on the floors: ");
totalRooms = s.nextInt();
s.nextLine(); // Consume previous newline character
// Start the game: Create a LostPuppy object:
game = new LostPuppy(totalFloors, totalRooms);
// Pick starting player
playerIndex = rand.nextInt(2);
System.out.println("\nFloor and room numbers start at zero '0'");
do
{
do
{
System.out.println("\nPlayer " + players[playerIndex]
+ ", enter floor and room to search separated by a space: ");
floor = s.nextInt();
room = s.nextInt();
//for testing, use random generation of floor and room
//floor = rand.nextInt(totalFloors);
//room = rand.nextInt(totalRooms);
} while (!game.indicesOK(floor, room)
|| game.roomSearchedAlready(floor, room));
found = game.searchRoom(floor, room, players[playerIndex]);
playerIndex = (playerIndex + 1) % 2;
System.out.println("\n[" + floor + "], [" + room + "]");
System.out.println(game.toString());
s.nextLine();
} while (!found);
playerIndex = (playerIndex + 1) % 2;
System.out.println("Great job player " + players[playerIndex] +"!");
System.out.println("Would you like to find another puppy [Y/N]? ");
}
while (s.nextLine().equalsIgnoreCase("Y"));
}
}
Finally, here is my test code:
import java.util.Random;
import java.util.Scanner;
public class LostPuppy
{
char[][] myHidingPlaces;
int myFloorLocation;
int myRoomLocation;
char myWinner;
boolean myFound;
Random random = new Random();
public void LostPuppy(int theFloors, int theRooms)
{// this ^ void is the issue and is now removed from my code(answered 7/14/2015 on stack overflow)
char[][] myHidingPlaces = new char[theFloors][theRooms];
for (int i = 0; i < theFloors; i++)
{
for (int j = 0; j < theRooms; j++)
{
myHidingPlaces[i][j] = ' ';
}
}
myFloorLocation = random.nextInt(theFloors);
myRoomLocation = random.nextInt(theRooms);
myHidingPlaces[myFloorLocation][myRoomLocation] = 'P';
myWinner = ' ';
myFound = false;
}
public boolean roomSearchedAlready(int floor, int room)
{
if (myHidingPlaces[floor][room] == '1' ||
myHidingPlaces[floor][room] == '2')
{
return true;
}
else
{
return false;
}
}
public boolean puppyLocation(int floor, int room)
{
if (myHidingPlaces[floor][room] == 'P')
{
return true;
}
else
{
return false;
}
}
public boolean indicesOK(int floor, int room)
{
if (floor <= myHidingPlaces.length || room <= myHidingPlaces[0].length)
{
return true;
}
else
{
return false;
}
}
public int numberOfFloors()
{
return myHidingPlaces.length - 1;
}
public int numberOfRooms()
{
return myHidingPlaces[0].length - 1;
}
public boolean searchRoom(int floor, int room, char player)
{
if (myFound = true)
{
myWinner = player;
myFound = true;
return true;
}
else
{
myHidingPlaces[floor][room] = player;
return false;
}
}
public String toString()
{
return "this is a test";
}
}
I DO NOT want any specific solutions to my assignment in any responses
to this post. Those issues are for me to figure out. What I do want is
an explanation as to why my test code wont compile/run.
This line
game = new LostPuppy(totalFloors, totalRooms);
Won't compile because you haven't defined any constructor that expect two int as arguments (totalFloors and totalRooms).
In order to fix this, declare in your class LostPuppy a constructor such as
public LostPuppy(int totalFloors, int totalRooms)
{
//Do something here with paremeters..
}
This line
while (!game.indicesOK(floor, room)
Won't compile because you haven't defined any indicesOk method in your LostPuppy class.
Create a method such as
public boolean indicesOK(int floot, int room)
{
//return some conditions
}
I think your building method should be constructor:
public LostPuppy(int theFloors, int theRooms)
{
myHidingPlaces = new char[theFloors][theRooms]; //this line
for (int i = 0; i < theFloors; i++)
{
for (int j = 0; j < theRooms; j++)
{
myHidingPlaces[i][j] = ' ';
}
}
myFloorLocation = random.nextInt(theFloors);
myRoomLocation = random.nextInt(theRooms);
myHidingPlaces[myFloorLocation][myRoomLocation] = 'P';
myWinner = ' ';
myFound = false;
}
Note: look at the marked line, do not define type, if you define type, it becomes local variable instead of instance variable.
I am writing a snake game, specifically, is a centipede game. It needs me to draw a snake and that snake will automatically move one line by one line.
I did draw a snake, and it can move from left side to right side. However, the problem is:
I can't make the snake changes line, if it finish the first line, I need it changes to the second line and which starts from the right side.
My code is like this:
private void move()
{
myCentipedes[0] =
new Centipede(Settings.centipedeStartSize, Settings.RIGHT,
Settings.DOWN);
myCentipedes[0].segments = new Point[Settings.centipedeStartSize];
myCentipedes[0].segments[0] = new Point(0, 0);
boolean dr = true;
if (dr == true) {
if (myCentipedes[0].segments[0].x < 30) {
System.out.println(myCentipedes[0].segments[0].x +
" " +
myCentipedes[0].segments[0].y);
myCentipedes[0].segments[0] = new Point(x, 0);
for (int i = 1; i < 10; i++) {
myCentipedes[0].segments[i] =
new Point(myCentipedes[0].segments[i - 1].x - 1,
myCentipedes[0].segments[i - 1].y);
}
x++;
}
}
if (myCentipedes[0].segments[0].x == 29) {
x = 29;
dr = false;
}
if (dr == false) {
if (myCentipedes[0].segments[0].x > 0) {
myCentipedes[0].segments[0] = new Point(x, 1);
for (int i = 1; i < 10; i++) {
myCentipedes[0].segments[i] =
new Point(myCentipedes[0].segments[i - 1].x + 1, 1);
}
x--;
}
}
}
It appears to me that you re-create your entire centipede on every single move:
private void move()
{
myCentipedes[0] =
new Centipede(Settings.centipedeStartSize, Settings.RIGHT,
Settings.DOWN);
Is re-creating the centipede every move() intentional? Or should move() run the centipede entirely down the board, from start to finish? (If so, you'll need to add some looping to this method.)
I assume the myCentipedes[0] is simply a placeholder for future extensions, involving two or more centipedes on the board simultaneously. This sort of over-generic programming can sometimes make the code more difficult to read and write while initially programming, and almost certainly doesn't help matters. You can always re-factor a move() method that works on one centipede to a move(int centipede) method that works on a specific centipede and a move() method that calls move(int) for every centipede on the board. Or maybe you'll find it easier to place the movement code into the Centipede class, and need to remove the array indexes then and use class member storage instead.
boolean dr = true;
if (dr == true) {
dr will always equal true at this point. You might as well remove the variable and the test.
for (int i = 1; i < 10; i++) {
myCentipedes[0].segments[i] =
new Point(myCentipedes[0].segments[i - 1].x - 1,
myCentipedes[0].segments[i - 1].y);
}
Since you're counting up, you'll actually copy the value from segment[0] through to all elements in the array, one element at a time. Can't you just assign the Point objects new array indexes? Starting from i=centipede.segments.length and counting down, it'll look more like this:
for (int i=myCentipede[0].segments.length; i > 0; i--) {
myCentipede[0].segments[i] = myCentipede[0].segments[i-1];
}
myCentipede[0].segments[0] = new Point(...,...);
Some of your tests can be simplified:
if (myCentipedes[0].segments[0].x == 29) {
x = 29;
dr = false;
}
if (dr == false) {
if (myCentipedes[0].segments[0].x > 0) {
If dr == false at this point, you might as well have written it like this instead:
if (myCentipedes[0].segments[0].x == 29) {
x = 29;
if (myCentipedes[0].segments[0].x > 0) {
But then the second if is obviously not needed -- after all, 29 > 0.
While you're here, clean up all those hard-coded 10 with either a constant (Settings.centipedeStartSize) or find the actual length of the centipede (myCentipedes[0].segments.length).
Now that I've critiqued your current approach, I'd like to suggest a different tack:
Take a step back and break your problem down into smaller methods.
You've embedded two for loops that move the centipede one segment at a time by assigning to segment[i] the values from segment[i-1]. Instead of duplicating the code, write a new method with the body of the for loop to move the centipede forward. Make it take a Point object for the new first element each trip through the function. (Don't forget to make it count down rather than up.)
Once you've broken apart the for loops, I think it will be easier to make whatever changes are necessary for traveling left-to-right and right-to-left. You will probably want to write it with nested for loops -- one to control the vertical dimension, and within it, perhaps one or two new for loops to control the horizontal dimension. Make these loops work with a simple Centipede c, rather than the complicated expression you've currently got.
Breaking apart the larger function into smaller function will give you a better opportunity to test your functions in isolation -- test movement manually, with simple test methods like this:
move_forward(Centipede c, Point p) {
/* code to move forward one space to occupy `p` */
}
test_right() {
Centipede c = new Centipede(/* ... */);
move_forward(c, new Point(0,0));
move_forward(c, new Point(1,0));
move_forward(c, new Point(2,0));
move_forward(c, new Point(3,0));
move_forward(c, new Point(4,0));
move_forward(c, new Point(5,0));
/* ... */
}
Take it slow, test every method as you write them, and I think you'll find this is an easier problem than it currently looks.