In the game that I am making I have made it so that the zombies can't go through each other. I did this with this code.
for(int i = 0; i < zombies.size(); i++){
Zombie z = (Zombie) zombies.get(i);
zombieMovement();
for(int j = i + 1; j < zombies.size(); j++){
Zombie z2 = (Zombie) zombies.get(j);
Rectangle r1 = z.getBounds();
Rectangle r2 = z2.getBounds();
if(r1.intersects(r2)){
z.setX(z.getX() - 1);
z.setY(z.getY() - 1);
z2.setX(z2.getX() + 1);
z2.setY(z2.getY() + 1);
}
}
}
the zombieMovement method looks like this.
public void zombieMovement(){
for(int i = 0; i < zombies.size(); i++){
Zombie z = (Zombie) zombies.get(i);
if(z.getY() > player.getY()){
z.setY(z.getY() - .01);
}
if(z.getY() < player.getY()){
z.setY(z.getY() + .01);
}
if(z.getX() > player.getX()){
z.setX(z.getX() - .01);
}
if(z.getX() < player.getX()){
z.setX(z.getX() + .01);
}
if(z.isAlive()){
z.move();
}else{
zombies.remove(i);
}
}
}
Because the zombies move 10 times as fast when they are in contact with each other the first couple of zombies move alot faster than the ones that come later because they are in contact with more zombies (I can't set how fast they move when they are in contact with each other to any less because then they will still be able to be inside each other and I don't want the zombies to move any faster than what they are already doing).
Are there any simple ways to fix this error in?
Rather than trying to fix zombies that overlap you should prevent the problem in the first place in your zombieMovement() method. When working out where a zombie moves to you should test to see if its new position (the +/- .01 bit) is already occupied, if so then don't move the zombie to that location.
/**
* Returns true if the point x,y is already occupied by a zombie
*/
boolean isOccupied(double x, double y) {
for (Zombie z : zombies) {
if (z.getBounds().contains(new Point(x,y))) {
return true;
}
}
return false;
}
You can then test in your movement method like this;
if (z.getY() > player.getY()) {
double newY = z.getY() - .01;
if (!isOccupied(z.getX(), newY) {
z.setY(newY);
{
}
What happens when a zombie is surrounded on all sides and you have to move it?
For a really robust solution, I'd do a simultaneous constraint solver. Essentially, you can think of it like putting a spring in between each colliding zombie and iterating until everyone is no longer intersecting. You'd probably want to put caps on displacement to make sure you don't accidentally have zombies shooting off into space as well.
Unfortunately, this approach requires complicated math. I think Box2d has some slides online explaining the basis of its algorithms. Or you could just use an existing physics engine, like Box2d.
Related
I have to do a Battleship game. The game should have some kind of AI. The AI CAN place ships with a hard coded pattern, but i want to take things a step further and make the ship placement random.
I have implemented a "trial and error" method, where ships get randomly placed on the field with a random rotation, until the algorithm runs out of either ships to be placed or fields to place the ships in. In the second case the recursive implementation allows to try out other ship/field/rotation combos, until the first condition is reached. So to speak: try all possible ship/rotation/field combo's (picked randomly) until you find one valid one, where all ships are placed.
As you can imagine, this is a terrible effort, when it comes to runtime.
The size of the "board" can be 5x5 up to 30x30 with exactly 30% of the fields beeing occupied by a ship.
Now, Runtime isn't my concern for sizes up to 14x14, but then runtime increases so badly, that i have to think of a way to reduce runtime.
Any suggestions? (I would like to get general thinking advice/idea's, not code)
In case, my explenation wasn't enough: Here's the class that tries to place the ships on the field:
public class RandomShipFactory {
private int size;
private Game game;
private ArrayList<Ship> toBeCheckedShips = new ArrayList<Ship>();
private ArrayList<Position> toBeCheckedPositions = new ArrayList<Position>();
public RandomShipFactory(int size, Game game) {
this.size = size;
this.game = game;
this.toBeCheckedShips.addAll(game.ownBoard.getShips());
for(int i = 0; i < this.size; i++) {
for(int j = 0; j < this.size; j++) {
this.toBeCheckedPositions.add(new Position(i,j));
}
}
}
public void makeRandomShipPlacements() {
this.makeRandomShipPlacements(this.toBeCheckedPositions, this.toBeCheckedShips);
game.ownBoard.printBoard();
}
private boolean makeRandomShipPlacements(ArrayList<Position> currentPositions, ArrayList<Ship> currentShips) {
if(currentPositions.isEmpty()) {
return false;
}
ArrayList<Position> checkedPositionsInThisRun = new ArrayList<Position>();
Random r = new Random();
boolean success = false;
while(!success && !currentShips.isEmpty() && !currentPositions.isEmpty()) {
int randomPositionIndex = r.nextInt(currentPositions.size());
Position randomPosition = currentPositions.remove(randomPositionIndex);
checkedPositionsInThisRun.add(randomPosition);
ArrayList<Ship> currentShipQueue = new ArrayList<Ship>();
currentShipQueue.addAll(currentShips);
while(!success && !currentShipQueue.isEmpty()) {
Ship currentShip = currentShipQueue.remove(0);
boolean shouldRotate = r.nextBoolean();
if(shouldRotate) {
currentShip.rotate();
}
boolean canPlaceShip = this.game.placeShipOnOwnBoard(currentShip, randomPosition.x, randomPosition.y);
if(canPlaceShip) {
currentShips.remove(currentShip);
ArrayList<Position> boxPositions = removeBoxSquarePositions(currentPositions, currentShip, randomPosition);
if(currentShips.isEmpty()) {
success = true;
}else {
boolean recursiveSuccess = this.makeRandomShipPlacements(currentPositions, currentShips);
if(!recursiveSuccess) {
this.game.removeShipFromOwnBoard(currentShip);
currentPositions.addAll(boxPositions);
currentShips.add(currentShip);
}else {
success = true;
}
}
}else {
currentShip.rotate();
canPlaceShip = this.game.placeShipOnOwnBoard(currentShip, randomPosition.x, randomPosition.y);
if(canPlaceShip) {
currentShips.remove(currentShip);
ArrayList<Position> boxPositions = removeBoxSquarePositions(currentPositions, currentShip, randomPosition);
if(currentShips.isEmpty()) {
success = true;
}else {
boolean recursiveSuccess = this.makeRandomShipPlacements(currentPositions, currentShips);
if(!recursiveSuccess) {
this.game.removeShipFromOwnBoard(currentShip);
currentPositions.addAll(boxPositions);
currentShips.add(currentShip);
}else {
success = true;
}
}
}
}
}
}
currentPositions.addAll(checkedPositionsInThisRun);
return success;
}
private ArrayList<Position> removeBoxSquarePositions(ArrayList<Position> positionList, Ship ship, Position pos) {
ArrayList<Position> boxPositions = new ArrayList<Position>();
for(int i = 0; i < ship.length + 2; i++) {
for(int j = 0; j < 3; j++) {
int nextX, nextY;
switch(ship.getRotation()) {
case HORIZONTAL:
nextX = pos.x - 1 + i;
nextY = pos.y - 1 + j;
break;
case VERTICAL:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
default:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
}
if(nextX >= 0 && nextX < this.size && nextY >= 0 && nextY < this.size) {
Position currentPosition = Position.findPosInList(positionList, nextX, nextY);
if(currentPosition != null) {
positionList.remove(currentPosition);
boxPositions.add(currentPosition);
}
}
}
}
return boxPositions;
}
}
Multiple things to consider here, where improvement can take place:
This point is INVALID, because the OP has actually accounted for that. But this is still a basic thing to keep in mind: NEVER try to hit positions with random addresses. As soon as the field has a few ships, this will decrease speed drastically. In addition, 'random' functions can be really really slow, depending on their implementation
a) Your code is also slow because you search items in linear lists, so in average your additional effort will be (list size) / 2. Use HashMaps or TreeMaps or HeapMaps, or their Set version.
b) Your code uses remove(anyIndex). In ArrayLists, this will start to copy (on average) half of the array data to another position on every call.
And it does not play a role, whether you 'pop' from the very top (index 0) or not.
Worst of all, you use this remove() in loops, exploding your runtime.
Use an unstable removal (order will not be maintained), or LinkedLists, or some Map.
a) instead of targeting random positions, build a map of ALL available spaces (on a 30x30 board, this will add 900 entries to the hashmap).
The advantage of this approach is that build-up is slow, but all other operations are linear or minimized.
have a parameter for the size of a ship
create the HashMap (should be its own method, because we might need this at different locations in the code):
iterate over all positions, add the free ones (where you could validly place a ship of required length and rotation) to a hashmap, use coordinates as key
alternatively, if you expect coverage to be less than 85%, add ALL positions, then iterate over already placed ships and remove their positions from the HashMap
placement collisions for future placements would be possible at this point, but get removed once we hit (y)
place the ships:
x) selecting a random 'index' from a hashmap is a little challenge here, but you'll figure that out
y) once you start placing ships, remove all affected positions from the hashmap (include the ship's length and orientation in your calculations)
resume at (x)
b) extension of that algorithm - improvement for differing sizes
if you run this algorithm for ships with differing sizes, calculate the available positions for the longest ships first, then you can also re-use the created hashmap for shorter ones. if the hashmap is empty but there are still ships remaining: when you run the 'create hashmap of valid positions' algorithm again, adjust/reduce the searched ship length to the currently required one. (This then will not be 'truly random' anymore, but random enough that no player will ever realize it. But in cryptography for example, this here would break the algorithm)
this will greatly increase speed while coverage is below ~80%
A little hint on switches: use fall-through for combined code blocks:
Your code is
switch(ship.getRotation()) {
case HORIZONTAL:
nextX = pos.x - 1 + i;
nextY = pos.y - 1 + j;
break;
case VERTICAL:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
default:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
}
it can easily be reduced to
switch(ship.getRotation()) {
case HORIZONTAL:
nextX = pos.x - 1 + i;
nextY = pos.y - 1 + j;
break;
case VERTICAL: // fall-through to default, as VERTICAL seems to be the default^^
default:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
}
I have a tic tac game setup and require some help on code implementation on having it work for computer vs human, probably with the minmax algorithm. I have it currently set up for player vs player but how can I include code for player vs computer to work as I'm at a lost with this.
#Override
public void onClick(View v) {
if (!((Button) v).getText().toString().equals("")) {
return;
}
if (playerOneMove) {
((Button) v).setText("X");
((Button) v).setTextColor(playerX);
} else {
((Button) v).setText("O");
((Button) v).setTextColor(playerO);
}
turnsCount++;
if (checkGameIsWon()) {
if (playerOneMove) {
player1Wins();
} else {
player2Wins();
}
} else if (turnsCount == 9) {
draw();
} else {
playerOneMove = !playerOneMove;
switchPlayerTurn();
}
}
private boolean checkGameIsWon() {
String[][] field = new String[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
field[i][j] = buttons[i][j].getText().toString();
}
}
for (int i = 0; i < 3; i++) {
if (field[i][0].equals(field[i][1])
&& field[i][0].equals(field[i][2])
&& !field[i][0].equals("")) {
return true;
}
}
for (int i = 0; i < 3; i++) {
if (field[0][i].equals(field[1][i])
&& field[0][i].equals(field[2][i])
&& !field[0][i].equals("")) {
return true;
}
}
if (field[0][0].equals(field[1][1])
&& field[0][0].equals(field[2][2])
&& !field[0][0].equals("")) {
return true;
}
if (field[0][2].equals(field[1][1])
&& field[0][2].equals(field[2][0])
&& !field[0][2].equals("")) {
return true;
}
return false;
}
At the end of your onClick method it'll either be:
(1) the opponent's move (this is where you swap the X's and O's)
(2) a victory for the last player to move
(3) a draw (all boxes are already filled and nobody can move)
So you just write some code to handle (1) - as in make a decision for the AI.
What I'd suggest is making a method which checks for winning state independently of the board state. So you need to pass it a matrix or 3x3 array.
You run it to see if the moving player won.
Then for each empty spot, you make a copy of the current board state and for that copy only, fill in one of the blanks with the moving player's colour/shape/icon/character. Then pass each of those arrays to the checking algorithm.
That tells you (the AI) if you have a win on your move. (If more than one move wins, just pick one at random)
If none of your moves win immediately, then you again make a copy of the current board state for each empty spot - but now for the enemy (i.e. the human). Then fill in one of those spots for each copy, and pass that to your 'does this win' algorithm. That tells you if the human has an immediate win on their turn (e.g. if you did nothing and they got another turn, or if you played somewhere useless).
If they have a win ... spoil it! Muahahaha! Take that fleshbags! :D
If there are no wins or loses, you can play randomly.
With 386 era computers this would have been blindingly fast, so even though it's a lot of comparisons it's still super fast on modern hardware (even a watch/phone).
Even so it may be worth thinking about speeding it up.
E.g. having an internal representation based on numbers, not strings.
Let's say X = 1 and O = -1 and [empty] = 0
So now we add [0][0] + [0][1] + [0][2] if the total is 3 (or -3) then we have a win.
Of course that's using the mirror nature of the number line (above and below 0), so it doesn't generalise to more than 2 players. (Whereas strings or characters would generalise to more than 2 players, but you'd need a bigger board and so on and so forth, and tic tac toe might not be interesting with that many players anyway (too many spoiling moves between each of your moves???))
I have a program that checks distance and whether or not the player has collided with a barrier. I now am trying to calculate which barrier in the array of barriers is the closest to the moving player, then returning the index of that barrier.
Here is what I have so far:
public static int closestBarrier(GameObject object, GameObject[] barriers)
// TODO stub
{
int closest = 0;
for (int i = 0; i < barriers.length - 1; i++) {
if (Math.sqrt((object.getX() - barriers[i].getX())
* (object.getX() - barriers[i].getX()))
+ ((object.getY() - barriers[i].getY()) * (object.getY() - barriers[i]
.getY())) <= Math
.sqrt((object.getX() - barriers[i + 1].getX())
* (object.getX() - barriers[i + 1].getX()))
+ ((object.getY() - barriers[i + 1].getY()) * (object
.getY() - barriers[i + 1].getY()))) {
closest = i;
} else
closest = i + 1;
}
return closest;
}
I am still new to java so I understand what I already have probably isn't very efficient or the best method of doing it (or even right at all!?).
I'd refactor it a wee bit simpler like so:
public static int closestBarrier(GameObject object, GameObject[] barriers)
{
int closest = -1;
float minDistSq = Float.MAX_VALUE;//ridiculously large value to start
for (int i = 0; i < barriers.length - 1; i++) {
GameObject curr = barriers[i];//current
float dx = (object.getX()-curr.getX());
float dy = (object.getY()-curr.getY());
float distSq = dx*dx+dy*dy;//use the squared distance
if(distSq < minDistSq) {//find the smallest and remember the id
minDistSq = distSq;
closest = i;
}
}
return closest;
}
This way you're doing less distance checks (your version does two distance checks per iteration) and also you only need the id, not the actual distance, so you can gain a bit of speed by not using Math.sqrt() and simply using the squared distance instead.
Another idea I can think of depends on the layout. Say you have a top down vertical scroller, you would start by checking the y property of your obstacle. If you have a hash of them or a sorted list, for an object at the bottom of the screen you would start loop from the largest y barrier to the smallest. Once you found the closest barriers on the Y axis, if there are more than 1 you can check for the closest on the x axis. You wouldn't need to use square or square root as you're basically splitting the checks from 1 in 2D per barrier to 2 checks in 1D, narrowing down your barrier and discarding far away barriers instead of checking against every single object all the time.
An even more advanced version would be using space partitioning but hopefully you won't need it for a simple game while learning.
I have a program which creates multiple randomly placed objects(balls) at the beginning, now im trying to move the player ball towards the closest object automatically until their centres are the same. I have determined the distance of the closest object stored in an array called distance with index of 'closest', and set the speed as variable delta initialised somewhere else, how do i get the direction right? Right now
It moves just diagonally but not towards the closest ball
coordinatex[closest] and coordinatey[closest] are the x and y coordinates of the closest object.
playerObject.getX and playerObjectgetY gives me the coordinates of the player onject
public void move(int delta) {
for(int i=0; i<distance[closest]; i++) {
if (x<coordinatex[closest] && y<coordinatey[closest]) {
playerObject.setX(playerObject.getX() + 0.1*delta);
playerObject.setY(playerObject.getY() + 0.1*delta);
} else if(x>coordinatex[closest] && y>coordinatey[closest]) {
playerObject.setX(playerObject.getX() - 0.1*delta);
playerObject.setY(playerObject.getY() - 0.1*delta);
} else if(x>coordinatex[closest] && y<coordinatey[closest]) {
playerObject.setX(playerObject.getX() - 0.1*delta);
playerObject.setY(playerObject.getY() + 0.1*delta);
} else if (x<coordinatex[closest] && y>coordinatey[closest]) {
playerObject.setX(playerObject.getX() + 0.1*delta);
playerObject.setY(playerObject.getY() - 0.1*delta);
}
}
}
Something very similar to this:
angle=atan2(ClosestBally-player_y,closestBallx-player_x);
dxperframe = cos(angle);
dyperframe = sin(-angle);
I see your handlers for the four semi-cardinal (diagonal) directions, but not the four cardinal directions. In this game, for example, enemies approach the player in a step-wise manner using the move(int row, int col) method, shown here, from any of eight directions.
I'm making a game for a class and one element of the game is displaying a number of cabbages, which are stored in an ArrayList. This ArrayList must be a fixed number of 20, 10 of Good Cabbage and 10 of Bad Cabbage.
As the cabbages are created, I want to make sure they don't overlap when they are displayed. Where I'm running into trouble with this is that when I find a cabbage that overlaps, I'm not sure how to go back and create a new cabbage in its place. So far when the code finds an overlap, it just stops the loop. I guess I'm having trouble properly breaking out of a loop and restarting at the index that goes unfilled.
Here's what I have so far for this. Any suggestions would be much appreciated.
// Initialize the elements of the ArrayList = cabbages
// (they should not overlap and be in the garden) ....
int minX = 170 ;
int maxX = 480;
int minY = 15;
int maxY = 480;
boolean r = false;
Cabbage cabbage;
for (int i = 0; i < N_GOOD_CABBAGES + N_BAD_CABBAGES; i++){
if (i % 2 == 0){
cabbage = new GoodCabbage((int)(Math.random()* (maxX-minX + 1))+ minX,
(int)(Math.random()*(maxY-minY + 1))+ minY,window);
}
else {
cabbage = new BadCabbage((int)(Math.random()* (maxX-minX + 1))+ minX,
(int)(Math.random()*(maxY-minY + 1))+ minY,window);
}
if (i >= cabbages.size()){
// compares the distance between two cabbages
for (int j = 0; j < cabbages.size(); j++){
Point c1 = cabbage.getLocation();
Cabbage y = (Cabbage) cabbages.get(j);
Point c2 = y.getLocation();
int distance = (int) Math.sqrt((Math.pow((c1.x - c2.x), 2) + Math.pow((c1.y - c2.y),2)));
if (distance <= (CABBAGE_RADIUS*2) && !(i == j)){
r = true;
}
}
if (r){
break;
}
cabbage.draw();
cabbages.add(i, cabbage);
}
}
The easiest way to do this is probably to add another loop.
A do...while loop is suited to cases where you always need at least one iteration. Something like:
boolean overlapped;
do {
// create your new cabbage here
overlapped = /* check whether it overlaps another cabbage here */;
} while (overlapped);
cabbage.draw();
cabbages.add(i, cabbage);
It looks like you are making cabbage objects and then throwing them away, which is a (trivial) waste.
Why not pick the random X and Y, check if there is room at that spot, then make the cabbage when you have a good spot? You'll just churn through numbers, rather than making and discarding entire Objects. Plus you won't have to repeat the random location code for good and bad cabbages.
int x, y
do {
// pick x and y
} while (cabbageOverlaps(x,y,list)
// create a cabbage at that x,y, and add it to list
boolean cabbageOverlaps(int x, int y, ArrayList existingCabbages)