I have written a script to be set off whenever a player is within a distance of the monster. The script checks if the x position is greater than or less than the players x, and same for the z. (y is automatically set to terrain)
public int checkWalkX(Vector3f position) {
if (Math.floor(this.getX()) != Math.floor(position.x)) {
if(this.getX() > position.x) return 1; //Greater
if(this.getX() < position.x) return 2; //Less
}
return 0;
}
public int checkWalkZ(Vector3f position) {
if (Math.floor(this.getZ()) != Math.floor(position.z)) {
if(this.getZ() > position.z) return 1; //Greater
if(this.getZ() < position.z) return 2; //Less
}
return 0;
}
public void follow(Player player) {
walking = false;
following = true;
if(checkWalkX(player.getPosition()) == 1) this.setX(this.getX() - mobSpeed);
else if(checkWalkX(player.getPosition()) == 2) this.setX(this.getX() + mobSpeed);
if(checkWalkZ(player.getPosition()) == 1) this.setZ(this.getZ() - mobSpeed);
else if(checkWalkZ(player.getPosition()) == 2) this.setZ(this.getZ() + mobSpeed);
if(Math.floor(checkWalkX(walkToPosition)) == 0 && Math.floor(checkWalkZ(walkToPosition)) == 0) following = false;
}
For some reason when I run this script, the monster will only move within a distance of 2ish. He moves the right ways kinda, but he doesnt follow me. Would anyone know why this is?
You should leave out the Math.floor() condition.What you could get right now is that the player for example is at x=1 and the monster at x=1.99. The Floor condition would shorten this to 1 == 1 and declare that both are at the same place. In the worst case this would leave the monster at a distance of sqrt(2).
You can leave out the condition to get:
public int checkWalkX(Vector3f position) {
if(this.getX() > position.x) return 1; //Greater
if(this.getX() < position.x) return 2; //Less
return 0; // equal
}
The drawback of this is that you almost never get a zero back. The monster will always move. And here we come to the next problem. It always moves a fixed distance. This can lead to jumping around the target if the mobspeed is greater than the distance.
An approach to solve this could be to calculate the distance of the monster to the player (dist = sqrt(deltaX^2 + deltaZ^2)) and it this is greater than the desired distance move the monster along the monster-player-vector for a distance of mobspeed or monster-player-distance whichever is shorter.
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 am new to programing, so bare with me lol. Our teacher has given us a game in which we need to add some elements to it. It's a game where we have a player, some enemies and some food made out of rectangles. If the player hits the food i want the food to spawn somewhere else in the window. Heres is the code where im trying to make it happen:
private void updateFood(){
for(int i = 0; i < food.length; ++i)
{
//Should we follow or move randomly?
//2 out of 3 we will follow..
if(rnd.nextInt(3) < 2)
{
//We follow
int dx = player.getX() - food[i].getX();
int dy = player.getY() - food[i].getY();
if(abs(dx) > abs(dy))
{
if(dx > 0)
{
//Player is to the right - opiste
food[i].moveLeft();
}
else
{
//Player is to the left - opiste
food[i].moveRight();
}
}
else if(dy > 0)
{
//Player is down; - opiste
food[i].moveUp();
}
else
{//Player is up; - opiste
food[i].moveDown();
}
}
else
{
//We move randomly
int move = rnd.nextInt(4);
if(move == 0)
{
//Move right
food[i].moveRight();
}
else if(move == 1)
{
//Move left
food[i].moveLeft();
}
else if(move == 2)
{
//Move up
food[i].moveUp();
}
else if(move == 3)
{
//Move down
food[i].moveDown();
}
}
if(food[i].getX() == player.getX() && food[i].getY() == player.getY()){
food[i].getX() = random(0,width);
food[i].getY() = random(0,height);
}
}
}
the last 10 lines (or so) is where im having the problem.
if(food[i].getX() == player.getX() && food[i].getY() == player.getY()){
food[i].getX() = random(0,width);
food[i].getY() = random(0,height);
With limited info on what methods your food[index] objects have, I'm going to guess .getX() is read-only, meaning you can't set a value to it. Either find a .setX(number) method, or use the existing .moveLeft/Right/Up/Down(number) with a bit of maths.
currently my program is only always giving me 4, how can I determine how many steps the ant took to cover the whole board? The ant can walk up down left right, but can't walk off the board, and then do this simulation 4 times.
public static void main(String args[]) {
int[][] grid = new int[8][8];
int count = 0;
int x = 0;
int y = 0; // arrays are 0 based
while(true)
{
int random = (int)Math.random()*4+1;
if (random == 1)
{
x--; // move left
}
else if (random == 2)
{
x++; // move right
}
else if (random == 3)
{
y--; // move down
}
else if (random == 4)
{
y++; // move up
}
if(x < 0 || y < 0 || x >= grid.length || y >= grid[x].length) break;
count++;
grid[x][y]++;
}
System.out.println("Number of Steps in this simulation: " + count); // number of moves before it fell
}
}
The problem is this expression:
int random = (int)Math.random()*4+1;
Through the explicit cast, only Math.random() ist casted to int. But since Math.random() returns a dobule < 1, it is casted to 0 and thus random is always 1 and the method always returns 0.
The problem can be fixed by casting Math.random() * 4:
int random = (int) (Math.random() * 4) + 1;
The parenthesis enforce that the value of Math.random() * 4 (which will be a value in the interval [0, 3)) will be casted to int.
Two remarks on your code:
I would recommend introducing an enum Direction with four values (one for each direction) and choose a random Direction by calling Direction.values()[(int) (Math.random() * 4)];
I would recommend to use a switch instead of the if-else-if cascade.
Ideone demo
The program will exit the while(true) loop once one of the 4 conditions is true. My suggestion is to move these conditions in your if(random == value) checks like this:
if( random == 1 )
{
x--;
if (x < 0 )
{
x++;
}
}
Now to exit your while(true) loop you need to have an extra condition. I would suggest to think about your board in terms of 0's and 1's. Everytime the ant cross a cell, you set the grid[x][y] = 1.
int stepsTaken = 0;
int cellsToCover = grid.length * grid[0].length ;
int coveredCells = 0;
while(true)
{
//your code here
if( random == 1 )
{
stepsTaken++;
x--;
if (x < 0 )
{
x++;
}
}
// the other if's with "stepsTaken" incremented too.
if ( grid[x][y] == 0 )
{
grid[x][y] = 1;
coveredCells++;
}
if (coveredCells == cellsToCover )
break;
}
But please notice the many ifs statements inside a while(true) loop. If you have to fill a board of 10 rows x 10 columns it would take too much until the board is filled. Instead I would suggest you to use some more efficient algorithms like backtracking, dynamic programming etc.
Edit : Added step counter.
I'm programming a little game and I'm having some problems with the intersections. I need an efficient algorithm to check if two objects (which have x and y coords. and also a width and an height) are intersecting.
I tried with the following, but it doesn't always work, sometimes it doesn't recnognize an intersection.
public boolean contains(int x, int y) {
if ((x < this.x + this.width) && (x >= this.x) && (y < this.y + this.height) && (y >= this.y))
return true;
else
return false;
}
I have an ArrayList containing the objects, and I do the following:
private boolean checkIntersection(String pDirection) {
for (int i = 0; i < walls.size(); i++) {
if (pDirection.equalsIgnoreCase("right") && car.contains(walls.get(i).getX() - 1, walls.get(i).getY()))
return true;
if (pDirection.equalsIgnoreCase("left") && car.contains(walls.get(i).getX() + 30, walls.get(i).getY()))
return true;
if (pDirection.equalsIgnoreCase("top") && car.contains(walls.get(i).getX(), walls.get(i).getY() + 30))
return true;
if (pDirection.equalsIgnoreCase("down") && car.contains(walls.get(i).getX(), walls.get(i).getY() - 1))
return true;
}
return false;
}
Note that "-1" and "+30" is to avoid the car enter the "walls", there the walls have a width of 30 and an height of 30. The car also has the same dimensions.
Also note that the x and y are the top-left cords of the rectangles. The car and the walls are rectangles.
I would be thankful for your help.
INFO: It doesn't recnognize an intersection at the beginn of a row of walls if I am above the wall and I change the direction to "down" or viceversa.
See picture
EDIT 1 (I tried inverting the objects, but it also doesn't always work):
private boolean checkIntersection(String pDirection) {
for (int i = 0; i < walls.size(); i++) {
if (pDirection.equals("right") && walls.get(i).contains(car.getX() + 30, car.getY()))
return false;
if (pDirection.equals("left") && walls.get(i).contains(car.getX() - 1, car.getY()))
return false;
if (pDirection.equals("top") && walls.get(i).contains(car.getX(), car.getY() - 1))
return false;
if (pDirection.equals("down") && walls.get(i).contains(car.getX(), car.getY() + 30))
return false;
}
return true;
}
The flaw in your algorithm is, you are always checking the left-top point of the wall whether it is in the car. However, this is not equivalent to having intersection.
Instead, you should check whether any one of the objects contains (at least) one corner (not necessarily the top left one) of the other object.
Note that you should perform this check for both sides, i.e. either the car contains any corner of the wall or the wall contains any corner of the car.
I solved modifying the contains method in the following way, and it now works perfectly:
public boolean contains(int x, int y) {
if ((x < this.x + this.width) && (x > this.x-this.width) && (y < this.y + this.height) && (y > this.y-this.height))
return true;
else
return false;
}
I think that I did it involuntarily (checking for non-intersection instead of intersection), but I can optimize it using the answer/suggestion of #samgak and #Gene. So thanks, problem solved.
So I am trying to write a simple method which takes in set of four coordinates and decide whether they form a square or not.My approach is start with a point and calculate the distance between the other three points and the base point.From this we can get the two sides which have same value and the one which is a diagonal.Then I use Pythagoras theorem to find if the sides square is equal to the diagonal.If it is the isSquare method return true else false.The thing I want to find out is there some cases I might be missing out on or if something is wrong with the approach.Thanks for the all the help.
public class CoordinatesSquare {
public static boolean isSquare(List<Point> listPoints) {
if (listPoints != null && listPoints.size() == 4) {
int distance1 = distance(listPoints.get(0), listPoints.get(1));
int distance2 = distance(listPoints.get(0), listPoints.get(2));
int distance3 = distance(listPoints.get(0), listPoints.get(3));
if (distance1 == distance2) {
// checking if the sides are equal to the diagonal
if (distance3 == distance1 + distance2) {
return true;
}
} else if (distance1 == distance3) {
// checking if the sides are equal to the diagonal
if (distance2 == distance1 + distance3) {
return true;
}
}
}
return false;
}
private static int distance(Point point, Point point2) {
//(x2-x1)^2+(y2-y1)^2
return (int) (Math.pow(point2.x - point.x, 2) + (Math.pow(point2.y
- point.y, 2)));
}
public static void main(String args[]) {
List<Point> pointz = new ArrayList<Point>();
pointz.add(new Point(2, 2));
pointz.add(new Point(2, 4));
pointz.add(new Point(4, 2));
pointz.add(new Point(4, 4));
System.out.println(CoordinatesSquare.isSquare(pointz));
}
}
//Point Class
public class Point {
Integer x;
Integer y;
boolean isVisited;
public Point(Integer x, Integer y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object obj) {
if(obj!=null && obj.getClass().equals(this.getClass())){
return ((Point) obj).x.equals(this.x)&&((Point) obj).y.equals(this.y);
}
return false;
}
}
You know, you can do the same check much easier. You just have to check two things:
"four points make a parallelogram" and "one of its angles is right".
First is true when P3 = P1 + (P2-P1) + (P4-P1)
And the second when (P2-P1)*(P4-P1) = 0
Where A*B is a dot product (A.x * B.x + A.y * B.y)
The only catch here is computational error. You can't expect floats to be exactly equal, so instead of A=B you should consider using something like abs(A-B) < E where E is small enough for your case.
Here's a corner case:
What if dist1 is the diagonal distance of the square? (I'm assuming the 4 points are in arbitrary order.)
You probably need to do another check for the distances:
if(dist1 == dist2){
//do stuff
}
else if(dist1 == dist3){
//do stuff
}
else if(dist2 == dist3){
//do stuff
}
else return false;
Your function doesn't take everything into account. You're only checking one point against the others. jwpat7 mentions this, so here's an example:
Assume the points are in this order: (red, yellow, green, blue), and each block on the grid is one.
Your distance1 and distance2 will both be equal to 4, so you're essentially saying that the last point can be any point where distance3 = 8. This is the blue line. If the last point is anywhere on that line, you just approved it as square.
You can fix this easily by doing the same check , but using the next coordinate as the 'base', instead of 0. If your check passes for two points, it's definitely a square.
Alternative:
You can check if it's not a square. In a valid square, there are only two valid distances, side length(s), and diagonal length(d).
Since you're using squared distance, d = s * 2
If any distance(there are only six) does not equal either d or s, it cannot be a square. If all six do, it must be a square.
The advantage is that if you check to prove it is a square, you have to do all six distance checks. If you want to prove it's not a square, you can just stop after you find a bad one.
So, it depends on your data. If you're expecting more squares than non-squares, you might want to check for squareness. If you expect more non-squares, you should check for non-squareness. That way you get a better average case, even though the worst case is slower.
public static boolean isSquare(List<Point> points){
if(points == null || points.size() != 4)
return false;
int dist1 = sqDistance(points.get(0), points.get(1));
int dist2 = sqDistance(points.get(0), points.get(2));
if(dist1 == dist2){ //if neither are the diagonal
dist2 = sqDistance(points.get(0), points.get(3));
}
int s = Math.min(dist1, dist2);
int d = s * 2;
for(int i=0;i<points.size;i++){
for(int j=i+1;j<points.size();j++){
int dist = sqDistance(points.get(i), points.get(j));
if(dist != s && dist != d))
return false;
}
}
return true;
}
If you add an else if(dist2 == dist3){...} alternative (as suggested in another answer also) then it is true that your isSquare method will recognize a square when the four points form a square. However, your code will also report some non-squares as being squares. For example, consider the set of points {(0,0), (1,1), (0,-1), (-1,0)}. Then your distance1,2,3 values are 2, 1, 1, respectively, which will satisfy the tests in the dist2 == dist3 case.
Any non-degenerate quadrilateral has a total of six inter-corner distances. Knowing five of those distances constrains the remaining distance to either of two values; that is, it doesn't uniquely constrain it. So I imagine that a square-testing method based on inter-corner distances will have to compute and test all six of them.
You are not using the Pythagorean Theorem correctly. The Pythagorean Theorem states that the sum of the squares of two legs is the square of the diagonal, and you are interpreting it to mean that the sum of the two legs is equal to the diagonal. You should use this for the Pythagorean Theorem testing:
if (distance3 == Math.sqrt(distance1*distance1 + distance2*distance2)) {
return true;
}
Does this make sense?
<script>
function isSquare(p1,p2,p3,p4){
if ((areACorner(p1,p2,p3) && areACorner(p4,p2,p3))
|| (areACorner(p1,p2,p4) && areACorner(p3,p2,p4))
|| (areACorner(p1,p3,p4) && areACorner(p2,p3,p4))) return true
return false
}
function areACorner(p1,p2,p3){
//pivot point is p1
return Math.abs(p2.y - p1.y) == Math.abs(p3.x - p1.x)
&& Math.abs(p2.x - p1.x) == Math.abs(p3.y - p1.y)
}
</script>
Output:
console.log(isSquare({x:0,y:0},{x:1,y:1},{x:0,y:1},{x:1,y:0}))
true
console.log(isSquare({x:0,y:0},{x:1,y:1},{x:-1,y:-1},{x:1,y:0}))
false
If you use something like (my C code), where I use squared distance (to avoid sqrt):
int sqDist(Point p1, Point p2) {
int x = p1.x - p2.x;
int y = p1.y - p2.y;
return(x*x + y*y);
}
where Point is simply:
typedef struct {
int x, y;
} Point;
`
In your code, calculate the permutations of each corner to one another, find the smallest / largest edges (in squared values), then you can check that you have 4 sides and 2 diagonals:
int squares[6];
squares[0] = sqDist(p[0], p[1]);
squares[1] = sqDist(p[0], p[2]);
squares[2] = sqDist(p[0], p[3]);
squares[3] = sqDist(p[1], p[2]);
squares[4] = sqDist(p[1], p[3]);
squares[5] = sqDist(p[2], p[3]);
int side = squares[0];
int diagonal = squares[0];
int i = 0;
while((++i <= 4) && (side >= diagonal)) {
if(squares[i] < side) side = squares[i];
if(squares[i] > diagonal) diagonal = squares[i];
}
int diagonal_cnt = 0;
int side_cnt = 0;
int error = 0;
for(int i = 0; i < 6; i++) {
if(abs(side - squares[i]) <= error) side_cnt++;
if(abs(diagonal - squares[i]) <= error) diagonal_cnt++;
}
printf("Square = %s\n", ((side_cnt == 4) && (diagonal_cnt == 2)) ? "true" : "false");
You could change the error value to handle floating points errors -- if you'd like to convert this routine to handle floating point values.
Note: If all points are at the same location, I consider this a point (not a square).