Snake game, How to make a snake move? - java

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.

Related

incrementing an object in a 2d array

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.

Using ASCII for board co-ordinates in Java

I'm pretty new to Java and learning it as a hobby mainly after school to kill my free time productively. I find it really interesting and picking it up relatively pain free however I'm a bit stuck trying to implement a basic chess program that can be played via the command line. Ideally, I'd like to print out the board initially with just Kings and Queens on both sides and get them moving forwards, backwards and diagonally. (Once I get the hang of this I would try adding all other pieces, but at first I would like to just start as simply as possible). For simplicity I will just be using the standard 8x8 playing board.
I've already created a main game loop which uses a command line input of 1 to switch players and 2 to exit the game but I am stuck when it comes to printing out positions and having them change during the game. Firstly I would like to print out the starting positions of both kings and queens as a string (e.g ["D1","E1","D8","E8"]) for the player to see. I imagine the best way to do this is by using the ASCII index but I'm not really sure where to go from this, I tried the code below I know it's not correct and I don't know what to change...
int num[] = {65, 66, 67, 68, 69, 70, 71, 72};
String start_Pos =null;
for(int i = 4; i < 6; i++){
start_Pos[i] = (Character.toString((char)i) + "1");
}
int num[] = {65, 66, 67, 68, 69, 70, 71, 72};
String start_Pos =null;
for(int i = 4; i < 6; i++){
start_Pos1[i] = (Character.toString((char)i) + "8");
}
System.out.println(start_Pos + start_Pos1);
I have also tried coding the board set-up but this is literally just printing out the starting positions of the pieces and therefore will not change when a player makes a move - ideally it should. An example would be the starting positions are shown on the board like so:
Photo (PS I know QK should be swapped on one side so they're not opposite each other, my bad!
But after an input of "D1 D3" (first coordinates indicating what piece and the second indicating final position)from player 1 the board changes to reflect this. Is this possible without having to recompile the entire code after each turn? (Maybe a stupid question...).
Any help would be greatly appreciated. I find learning through making small games like these a lot more interesting and rewarding so if anyone is able to help me implement this I would be very thankful.
Java is an Object Oriented language, so it is best to use classes and object instances.
Of course, with a chessboard, you'll still want to perform calculations on the x, y coordinates. Computers are just better with numbers, they have problems interpreting things. The chess notation is mainly of use to us humans.
So here is a class that can be used to parse and interpret chess positions. Note that you should keep this class immutable and simply use a new object instance rather than changing the x or y field.
public final class ChessPosition {
// use constants so you understand what the 8 is in your code
private static final int BOARD_SIZE = 8;
// zero based indices, to be used in a 2D array
private final int x;
private final int y;
public ChessPosition(int x, int y) {
// guards checks, so that the object doesn't enter an invalid state
if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE) {
throw new IllegalArgumentException("Invalid position");
}
this.x = x;
this.y = y;
}
public ChessPosition(String chessNotation) {
// accept upper and lowercase, but output only uppercase
String chessNotationUpper = chessNotation.toUpperCase();
// use a regular expression for this guard
if (!chessNotationUpper.matches("[A-H][0-7]")) {
throw new IllegalArgumentException("Invalid position");
}
// can be done in one statement, but we're not in a hurry
char xc = chessNotationUpper.charAt(0);
// chars are also numbers, so we can just use subtraction with another char
x = xc - 'A';
char yc = chessNotation.charAt(1);
// note that before calculation, they are converted to int by Java
y = yc - '1';
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public String getChessNotation() {
// perform the reverse and create a string out of it
// using StringBuilder#append(char c) is another option, but this is easier
return new String(new char[] {
// Java converts 'A' to int first (value 0x00000041)
(char) (x + 'A'),
(char) (y + '1')
});
}
// this will be helpfully displayed in your debugger, so implement toString()!
#Override
public String toString() {
return getChessNotation();
}
}
Now probably you want to also create a Board class with a backing 2D array of ChessPiece[][] and perform board.set(WHITE_KING, new ChessPosition("E1")) or something similar.

Battleship stuck on battle board implementation

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.

Spawn random objects without overlapping (Java)?

I'm developing a game in Java, and part of it requires that objects spawn at the top of the screen and proceed to fall down. I have three objects that can possibly spawn, and three possible x coordinates for them to spawn at, all stored in an array called xCoordinate[].
One of the objects is of a class called Enemy, which inherits a class I have called FallingThings. In the FallingThings class, I have methods to generate new objects, my enemy method is below:
public static void generateNewEnemy() {
xIndexEnemyOld = xIndexEnemy;
xIndexEnemy = new Random().nextInt(3);
if (delayTimer == 0) {
while (xIndexEnemy == xIndexEnemyOld) {
xIndexEnemy = new Random().nextInt(3);
}
}
if (xIndexEnemy != xIndexMoney && xIndexEnemy != xIndexFriend) {
Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
enemies.add((Enemy) enemy);
} else {
generateNewEnemy();
}
}
xIndexEnemy represents the index of the xCoordinates array.
xIndexMoney and xIndexFriend are the indexes of the xCoordinates array for the two other objects (the comparisons with these values ensures that one object does not spawn directly on top of another).
The delayTimer variable represents the random delay between when new objects spawn, which was set earlier in my main class.
I store each instance of an Enemy object in an ArrayList.
Everything works except for the fact that sometimes, an object will spawn over itself (for example, the delay is 0, so two enemy objects spawn directly on top of each other, and proceed to fall down at the same speed at the same time).
I've been trying to crack this for the past two days, but I understand exactly why my code right now isn't working properly. I even tried implementing collision detection to check if another object already exists in the space, but that didn't work either.
I would be extremely grateful for any suggestions and ideas.
EDIT2
It seems that you still don't understand the problem with your function. It was addressed in the other answer but I'll try to make it more clear.
public static void generateNewEnemy() {
xIndexEnemyOld = xIndexEnemy;
This is just wrong. You can't set the Old index without having actually used a new index yet.
xIndexEnemy = new Random().nextInt(3);
if (delayTimer == 0) {
while (xIndexEnemy == xIndexEnemyOld) {
xIndexEnemy = new Random().nextInt(3);
}
}
This is actually ok. You're generating an index until you get one that is different. It may not be the most elegant of solutions but it does the job.
if (xIndexEnemy != xIndexMoney && xIndexEnemy != xIndexFriend) {
Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
enemies.add((Enemy) enemy);
} else {
generateNewEnemy();
}
}
This is your problem (along with setting the Old index back there). Not only do you have to generate an index thats different from the Old index, it must also be different from IndexMoney and IndexFriend.
Now, what happens if, for example, IndexOld = 0, IndexMoney = 1 and IndexFriend = 2? You have to generate an index that's different from 0, so you get (again, for instance) 1. IndexMoney is 1 too, so the condition will fail and you do a recursive call. (Why do you even have a recursive call?)
OldIndex was 0, and now in the next call you're setting it to 1. So IndexOld = 1, IndexMoney = 1 and IndexFriend = 2. Do you see the problem now? The overlapped index is now wrong. And the new index can only be 0 no matter how many recursive calls it takes.
You're shooting yourself in the foot more than once. The recursive call does not result in an infinite loop (stack overflow actually) because you're changing the Old index. (Which, again is in the wrong place)
That if condition is making it so the newly generated index cannot overlap ANY of the previous indexes. From what you said before it's not what you want.
You can simplify your function like this,
public static void generateNewEnemy() {
xIndexEnemy = new Random().nextInt(3);
if (delayTimer == 0) {
while (xIndexEnemy == xIndexEnemyOld) {
xIndexEnemy = new Random().nextInt(3);
}
}
Enemy enemy = new Enemy(xCoordinates[xIndexEnemy]);
enemies.add((Enemy) enemy);
xIndexEnemyOld = xIndexEnemy;
// Now that you used the new index you can store it as the Old one
}
Will it work? It will certainly avoid overlapping when the delayTimer is 0 but I don't know the rest of your code (nor do I want to) and what do you do. It's you who should know.
About my suggestions, they were alternatives for how to generate the index you wanted. I was assuming you would know how to fit them in your code, but you're still free to try them after you've fixed the actual problem.
Original Answer
Here's one suggestion.
One thing you could do is to have these enemies "borrow" elements from the array. Say you have an array,
ArrayList< Float > coordinates = new ArrayList< Float >();
// Add the coordinates you want ...
You can select one of the indexes as you're doing, but use the maximum size of the array instead and then remove the element that you choose. By doing that you are removing one of the index options.
int nextIndex = new Random().nextInt( coordinates.size() );
float xCoordinate = coordinates.get( nextIndex );
coordinates.remove( nextIndex ); // Remove the coordinate
Later, when you're done with the value (say, when enough time has passed, or the enemy dies) you can put it back into the array.
coordinates.add( xCoordinate );
Now the value is available again and you don't have to bother with checking indexes.
Well, this is the general idea for my suggestion. You will have to adapt it to make it work the way you need, specifically when you place the value back into the array as I don't know where in your code you can do that.
EDIT:
Another alternative is, you keep the array that you previously had. No need to remove values from it or anything.
When you want to get a new coordinate create an extra array with only the values that are available, that is the values that won't overlap other objects.
...
if (delayTimer == 0) {
ArrayList< Integer > availableIndexes = new ArrayList< Integer >();
for ( int i = 0; i < 3; ++i ) {
if ( i != xIndexEnemyOld ) {
availableIndexes.add( i );
}
}
int selectedIndex = new Random().nextInt( availableIndexes.size() );
xIndexEnemy = availableIndexes.get( selectedIndex );
}
// Else no need to use the array
else {
xIndexEnemy = new Random().nextInt( 3 );
}
...
And now you're sure that the index you're getting should be different, so no need to check if it overlaps.
The downside is that you have to create this extra array, but it makes your conditions simpler.
(I'm keeping the "new Random()" from your code but other answers/comments refer that you should use a single instance, remember that)
As I see, if delay == 0 all is good, but if not, you have a chance to generate new enemy with the same index. Maybe you want to call return; if delayTimer != 0?
UPDATED
Look what you have in such case:
OldEnemyIndex = 1
NewEnemyIndex = random(3) -> 1
DelayTimer = 2
Then you do not pass to your if statement, then in the next if all is ok, if your enemy has no the same index with money or something else, so you create new enemy with the same index as previous

A collision check method works on a mac, but not on PC

So I am stumped. Here is my collision check method`
public void checkCollision ()
{
for (int i = 0; i < bullets.size()-1; i ++)
{
for (int j = 0; j < enemiesLaunched.size()-1; j++)
{
Rectangle temp = enemiesLaunched.get(j).getRectangle();
Rectangle temp2 = bullets.get(i).getRectangle();
`
if (temp2.intersects (temp))
{
String str = bullets.get(i).getPath();
// since the bullets are selective, the following code is to check
// if the right bullets hit the right germs
if (str.equals("oil gland.png")) // bullet is from oil gland
{
if (enemiesLaunched.get(j).getInfo().equals("highAcid"))
{
enemiesLaunched.get(j).setVisible(false);
bullets.remove(i);
}
}
else if (str.equals ("sweat gland.png"))
{
if (enemiesLaunched.get(j).getInfo().equals("lysozome"))
{
enemiesLaunched.get(j).setVisible(false);
bullets.remove(i);
}
}
else
{
if (enemiesLaunched.get(j).getInfo().equals("mucus"))
{
enemiesLaunched.get(j).setVisible(false);
bullets.remove(i);
}
}
`
On my mac, it works exactly how I intended. However, on my PC, it does not. To make matters more baffling, I have implemented the same logic on games further along in the game, and it works just fine on both the mac and pc, any help would be greatly appreciated!
How are you doing your time delta, and what is the velocity on the two objects? If your time delta is sufficiently large enough, you might not detect the collision as the two objects could have pass right through each other between checks. Have a look here for an explaination.
What tears attention is size()-1 - sure? But bullets.remove(i); certainly should be followed by --i; as otherwise the for-incrementing would skip the next bullet.
Optimized it would be by keeping get(i) and get(j) in their own variables.
I'd rather use for-loops like this if possible to ensure I don't have some wrong indexes due to typos or something:
List<Enemy> enemies = new ArrayList<Enemy>;
for (Enemy enemy : enemies) {
...
}
For example with this loop:
for (int i = 0; i < enemies.size()-1; ++i)
you will always leave the last "enemy" untouched.
And then, to be sure I'm not screwing up my Lists and iterations I would keep references to objects that need to be removed and would remove them afterwards, because I'm not sure what happens when removeing items from a collection while iterating over the same collection. The behaviour might be collectiontype and implementation (of the collection) specific.

Categories

Resources