Calculating direction based on point offsets - java

For my tile-based game, I need to calculate direction based on a given point offset (difference between two points). For example, let's say I'm standing at point (10, 4) and I want to move to point (8, 6). The direction I move at is north-west. What would be the best way to calculate this?
Here's me basic implementation in Java.
public int direction(int x, int y) {
if (x > 0) {
if (y > 0) {
return 0; // NE
} else if (y < 0) {
return 1; // SE
} else {
return 2; // E
}
} else if (x < 0) {
if (y > 0) {
return 3; // NW
} else if (y < 0) {
return 4; // SW
} else {
return 5; // W
}
} else {
if (y > 0) {
return 6; // N
} else if (y < 0) {
return 7; // S
} else {
return -1;
}
}
}
Surely it can be optimised or shortened. Any help? Thanks.

I think the easiest to understand way would be making a static array that contains the values for all cases.
// Won't say anything about how much these values make sense
static final int[][] directions = {
{3, 6, 0},
{5, -1, 2}, // -1 for "no direction", feel free to replace
{4, 7, 1}
};
public int direction(int x, int y) {
x = (x < 0) ? 0 : ((x > 0) ? 2 : 1);
y = (y < 0) ? 0 : ((y > 0) ? 2 : 1);
return directions[y][x];
}
Edit: Now it's correct (why are so many languages missing a proper sgn function?)

My answers with if conditions :).
public int direction(int x, int y) {
//0 NE, 1 SE, 2 E, 3 NW, 4 SW, 5 W, 6 N, 7 S, 8 (Same place / Not a direction)
int direction = 0;
if(x < 0){
direction = 3;
}else if(x == 0){
direction = 6;
}
if(y < 0){
direction = direction + 1;
}else if(y == 0){
direction = direction + 2;
}
return direction;
}

define a 2D array to hold all states.
convert x and y to 0, 1 or 2 based on their value (x>0 or x<0 or x ==0)
return the specific index of array.

This is about as short and clean as you can get, if you represent the eight cardinal directions this way, as separate enumerated values. You're choosing between eight distinct return values, so a decision tree with eight leaves is the best you can do.
You might get something a little tidier if you split direction into two components (N-S and E-W), but without knowing more about what you do with direction, we can't know whether that's worth the trouble.

You can receive and return your direction as a Point or something similar (anyway, an (x,y) tuple). So if you're standing in p0 = (10, 4) and want to move to p1 = (8, 6), the result would be (in pseudocode):
norm(p1 - p0) = norm((-2,2)) = (-1,1)
You can calculate the norm of an integer if you divide it by its absolute value. So for a point you calculate the norm of both members. Just bear in mind that (-1,1) is more expressive than 3 and you can operate in an easier fashion with it.
If you need specific operations, you can create your own Java Point class or extend the existing ones in the library.

Related

What is wrong with my Java recursive function?

I'm trying to write a relatively straightforward recursive program in Java to compute all the possible ways to traverse a 4x4 matrix (not necessarily traveling through every spot), starting at the top left and ending in the bottom right spaces. I use a 2-D array to do this, marking off visited spaces with "1"s as I go.
It's been a while since I've worked recursively and I can't seem to get the output I expect. The output from the code below is "2" - obviously, the result should be much higher. I know there's something tiny I'm overlooking. Can someone tell me what it is?
public static void main(String[] args) {
int[][] matrix = new int[4][4];
int result = moveRobot(matrix, 0, 0);
System.out.print(result + "");
}
public static int moveRobot(int[][] matrix, int x, int y) {
if (x == 3 && y == 3) {
return 1;
} else if (x < 0 || y < 0 || x > 3 || y > 3) {
return 0;
} else if (matrix[x][y] == 1) {
return 0;
} else {
matrix[x][y] = 1;
return moveRobot(matrix, x, y+1) + moveRobot(matrix, x+1, y) + moveRobot(matrix, x, y-1) +
moveRobot(matrix, x-1, y);
}
}
The problem is that the matrix is not copied but passed by value of the reference to it. Every time you modify it such in matrix[x][y] = 1 other successive code paths will see the modification instead that working on an unmodified state.
For example here:
moveRobot(matrix, x, y+1) + moveRobot(matrix, x+1, y)
Entering the first call will modify matrix, so in second moveRobot call you'd end up with 1 in matrix[x][y+1] while that's not what you want.

How can I find an overlap between two given ranges? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
Is there an effective way to find the overlap between two ranges?
Practically, the two ranges marked as (a - c), and (b - d), and I assume (c > a) && (d > b).
(b <= a <= d) which means if ((a >= b) && (d > a))
(b <= c <= d) which means if ((c >= b) && (d >= c))
(a <= b <= c) which means if ((b > a) && (c > b))
(a <= d <= c) which means if ((d > a) && (c > d))
But it never ends, because in this way I can find only one range at the time, and in each if I have to check the other cases as well.
For exemple, if the first condition (1) correct, I know what happening with the start of the range (a) I still need to check the others for the end of the range (c).
Not to mention that all this works in the case that (c > a) && (d > b), and not one of them is equal to another.
Two ranges overlap in one of two basic cases:
one range contains the other completely (i.e. both the start and end of one range are between the start and end of the other), or
the start or end of one range is contained within the other range
Conversely, they do not overlap only if neither endpoint of each range is contained within the other range (cases 11 and 12 in your diagram). We can check whether the low end of either range is past the high end of the other, to detect both those cases:
if (a > d || c < b) {
// no overlap
}
else {
// overlap
}
We can invert the condition and then use DeMorgan's laws to swap the order, if that's preferable:
if (a <= d && c >= b) {
// overlap
}
else {
// no overlap
}
To find the actual overlap range, you take the maximum of the two low ends, and the minimum of the two high ends:
int e = Math.max(a,b);
int f = Math.min(c,d);
// overlapping range is [e,f], and overlap exists if e <= f.
All above assumes that the ranges are inclusive, that is, the range defined by a and c includes both the value of a and the value of c. It is fairly trivial to adjust for exclusive ranges, however.
Use apache commons Range and its subclasses, especially the overlap method.
The check for an overlap (just true/false) is actually quite easy:
Assume the ranges [a,b] and [c,d].
You have an overlap if: a <= d and b => c. This also works for a = b and/or c = d.
If you have an overlap then the overlapping range is [max(a,c),min(b,d)].
You can detect the collision of two ranges by using a modified circular collision detection algorithm.
import java.util.Arrays;
public class RangeUtils {
public static void main(String[] args) {
int[] rangeA = { 10, 110 };
int[] rangeB = { 60, 160 };
int[] rangeC = intersectingRange(rangeA, rangeB);
System.out.println("Range: " + Arrays.toString(rangeC)); // Range: [60, 110]
}
// Based on circular collision detection.
public static boolean collisionDetected(int[] rangeA, int[] rangeB) {
int distA = Math.abs(rangeA[1] - rangeA[0]) / 2;
int distB = Math.abs(rangeB[1] - rangeB[0]) / 2;
int midA = (rangeA[0] + rangeA[1]) / 2;
int midB = (rangeB[0] + rangeB[1]) / 2;
return Math.sqrt((midB - midA) * (midB - midA)) < (distA + distB);
}
public static int[] intersectingRange(int[] rangeA, int[] rangeB) {
if (collisionDetected(rangeA, rangeB)) {
return new int[] {
Math.max(rangeA[0], rangeB[0]),
Math.min(rangeA[1], rangeB[1])
};
}
return null;
}
}
Here is a visual example of the code; ported to JavaScript.
var palette = ['#393A3F', '#E82863', '#F6A329', '#34B1E7', '#81C683'];
var canvas = document.getElementById('draw');
var ctx = canvas.getContext('2d');
var rangeA = [10, 110];
var rangeB = [60, 160];
var rangeC = intersectingRange(rangeA, rangeB);
var collisionText = 'Range: [' + rangeC + ']';
var leftOffset = 18;
var topOffset = 24;
drawLines(ctx, [rangeA, rangeB], topOffset);
drawText(ctx, collisionText, leftOffset, topOffset);
drawBoundry(ctx, rangeC, topOffset);
// Based on circular collision detection.
function collisionDetected(rangeA, rangeB) {
var distA = Math.abs(rangeA[1] - rangeA[0]) / 2;
var distB = Math.abs(rangeB[1] - rangeB[0]) / 2;
var midA = (rangeA[0] + rangeA[1]) / 2;
var midB = (rangeB[0] + rangeB[1]) / 2;
return Math.sqrt((midB - midA) * (midB - midA)) < (distA + distB);
}
function intersectingRange(rangeA, rangeB) {
if (collisionDetected(rangeA, rangeB)) {
return [Math.max(rangeA[0], rangeB[0]), Math.min(rangeA[1], rangeB[1])];
}
return null;
}
function drawText(ctx, text, x, y) {
ctx.save();
ctx.font = '18px Arial';
ctx.fillText(text, x, y);
ctx.restore();
}
function drawLines(ctx, lines, topOffset) {
topOffset = topOffset || 0;
var sizeWidth = ctx.canvas.clientWidth;
var sizeHeight = ctx.canvas.clientHeight - topOffset;
var yOffset = sizeHeight / (lines.length + 1);
for (var i = 0; i < lines.length; i++) {
var color = palette[i % palette.length];
var yPos = (i + 1) * yOffset + topOffset;
drawLine(ctx, lines[i], yPos, color)
}
}
function drawLine(ctx, range, index, color) {
ctx.save();
ctx.beginPath();
ctx.moveTo(range[0], index);
ctx.lineTo(range[1], index);
ctx.strokeStyle = color;
ctx.lineWidth = 4;
ctx.stroke();
ctx.restore();
}
function drawBoundry(ctx, bounds, topOffset) {
var sizeHeight = ctx.canvas.clientHeight - topOffset;
var padding = sizeHeight * 0.25;
var y1 = topOffset + padding;
var y2 = sizeHeight + topOffset - padding;
ctx.save();
ctx.beginPath();
ctx.strokeStyle = palette[4];
ctx.setLineDash([5, 5]);
ctx.lineWidth = 2;
ctx.rect(bounds[0], y1, bounds[1] - bounds[0], sizeHeight * 0.5);
ctx.stroke();
ctx.restore();
}
canvas#draw {
background: #FFFFFF;
border: thin solid #7F7F7F;
}
<canvas id="draw" width="180" height="160"></canvas>
Let's make the ranges clearer:
(start1, end1) and (start2, end2)
Double totalRange = Math.max(end1, end2) - Math.min(start1, start2);
Double sumOfRanges = (end1 - start1) + (end2 - start2);
Double overlappingInterval = 0D;
if (sumOfRanges > totalRange) { // means they overlap
overlappingInterval = Math.min(end1, end2) - Math.max(start1, start2);
}
return overlappingInterval;
Based on this answer
Set x=max(a,b), y=min(c,d). If x < y (or x≤y) then (x-y) is a common part of the two ranges (degenerate in case x=y), otherwise they don't overlap.
Based on some other answers to this question I composed the following two code samples:
The first will only return a Boolean indicating whether two ranges overlap:
// If just a boolean is needed
public static boolean overlap(int[] arr1, int[] arr2) {
if((arr1[0] <= arr2[arr2.length - 1]) && (arr2[arr1.length - 1] >= arr2[0])) {
return true;
} else {
return false;
}
}
The second will return an Integer array of the overlapping values in cases where an overlap exists. Otherwise it will return an empty array.
// To get overlapping values
public static int[] overlap(int[] arr1, int[] arr2) {
int[] overlappingValues = {};
if((arr1[0] <= arr2[arr2.length - 1]) && (arr2[arr1.length - 1] >= arr2[0])) {
int z = 0;
for(int a : arr1) {
for(int b : arr2) {
if(a == b) {
overlappingValues[z] = a;
z = z + 1;
}
}
}
} else {
return {};
}
}
Hope this helps.
Based on the updated question I did some research via Google and could find this posting:
Java, find intersection of two arrays
To what I understand at the moment it should match the given requirements. And the code snippet used is quite short and from what I know also looks quite well.
And to account for the remarks in terms of discrete and continuous values I wanted to add another potential solution I could find for continuous ranges:
https://community.oracle.com/thread/2088552?start=0&tstart=0
This solution does not directly return the overlapped ranges but provides an interesting class implementation to do range comparison.

need dynamic programming solution

The problem is the following:
Given a matrix of characters. In the very beginning of the game I am at position (0, 0) in the matrix. Depending on the character in the current position (i, j) I can move Up (if the current character is 'U'), Down if the current character is 'D', Right if the current character is 'R' and Left if the current character is 'L'. Once I reach position with character '*' I cannot move any more (there is exactly one such position). I have some time K in which I have to reach the character. I also have right to change characters, s.t. I can reach character ' * ' faster, but for each changing I pay cost of 1. In the end I have to return the min number of changes I have performed s.t. I have to reach character ' * ' in time k. If it is not possible I have to return -1.
My idea is as follows:
traverse the whole matrix, to find the position of character ' * '.
create boolean method isReachable(x, y, k), which tells me if character at position (x, y) is reachable from position (0, 0) for time k. Here is the method:
public static boolean isReachable(int x, int y, int time){
if(time < 0){
return false;
}
if(x == 0 && y == 0){
return true;
}
if(isInBounds(x-1, y)){
if(maze[x-1][y] == 'D'){
return isReachable(x-1, y, time-1);
}
}
if(isInBounds(x, y-1)){
if(maze[x][y-1] == 'R'){
return isReachable(x, y-1, time-1);
}
}
if(isInBounds(x+1, y)){
if(maze[x+1][y] == 'U'){
return isReachable(x+1, y, time-1);
}
}
if(isInBounds(x, y+1)){
if(maze[x][y+1] == 'L'){
return isReachable(x, y+1, time-1);
}
}
return false;
}
private static boolean isInBounds(int x, int y) {
if(x >= 0 && x <= N-1 && y >= 0 && y <= M-1){
return true;
}
return false;
}
If the method return true - I output 0 (i.e. there is no need to change any square in the matrix).
If the method return false - I want to perform another method which will tell me the min number of changes. However I dont know how to write it. This is my draft that obiously doesnt work:
private static int find(int x, int y, int k) {
int res = 0;
if(k < 0){ //my idea is that if the time become < 0 it means that the point is unreachable, i.e. I have to output 0; Howevr this doesnt output 0, just gives 0 to the upper levels of a recursion tree;
return -1;
}
if(x == 0 && y == 0){
res = 0;
}
else{
int down;
if(isInBounds(x-1, y) ){
if(maze[x-1][y] == 'D'){
down = find(x-1, y, k-1);
}
else{
down = 1 + find(x-1, y, k-1);
}
}
else{
down = Integer.MAX_VALUE;
}
int left;
if(isInBounds(x, y+1) ){
if(maze[x][y+1] == 'L'){
left = find(x, y+1, k-1);
}
else{
left = 1 + find(x, y+1, k-1);
}
}
else{
left = Integer.MAX_VALUE;
}
int right;
if(isInBounds(x, y-1) ){
if(maze[x][y-1] == 'R'){
right = find(x, y-1, k-1);
}
else{
right = 1 + find(x, y-1, k-1);
}
}
else{
right = Integer.MAX_VALUE;
}
int up;
if(isInBounds(x+1, y) ){
if(maze[x+1][y] == 'U'){
up = find(x+1, y, k-1);
}
else{
up = 1 + find(x+1, y, k-1);
}
}
else{
up = Integer.MAX_VALUE;
}
res = min(left, right, up, down);
}
return res;
}
As I wrote in the comments I have two very basic cases which I dont know how to perform:
when the time < 0 -> it means that the point is unreachable, i.e. I have to output -1 (but I dont know how to do it)
if I am at point (0, 0) I dont have to do any changes - return 0
else I check the neighbouring squares for their letters and return what I have from them.
Can someone help me with general idea, because I think mine is wrong. I the problem description it was said that we have to use dynamic programming and recursion
I haven't solve the problem, but I think my solution is right.
create dp[i][j][dir][step], means the cost in pos(i,j), the direction is dir and need 'step' steps to the position '*'.
assume '*' in I,J, so we need to calculate dp[I][J][0|1|2|3][0].
tot time is states*move, which is (50*50*4*1000)*(4*4). It is enough to solve the problem.

Java Recursion over 2 parameters and in two directions

I wish to recurse over two parameters simultaneously in a generic way. Here some explanation:
This is how the function calls should look like Func(int a, int b):
Call 0: Func(0, 0)
Call 1: Func(0, 1)
Call 1: Func(1, 0)
Call 1: Func(0, -1)
Call 1: Func(-1, 0)
How would I implement this in code, ensuring the following statements:
All possible combinations of a INRANGE (-INF, INF) and b INRANGE (-INF, INF) are considered.
There is no overhead, with that I mean that the same function is not used several times in the recursion.
I later want to expand it to do the same thing over 7 parameters.
Regards.
Here's my take on the spiral approach:
// this is your function
static void func(int x, int y)
{
System.out.println("x = "+x+", y = "+y);
}
// this calls func for all possible combinations of signs of the variables in arr
static void allPossibleSigns(int pos, Integer... arr)
{
if (pos == arr.length)
{
func(arr[0], arr[1]); // not really generic
}
else
{
allPossibleSigns(pos+1, arr);
arr[pos] = -arr[pos];
if (arr[pos] != 0)
allPossibleSigns(pos+1, arr);
}
}
static void caller()
{
for (int t = 0; t < MAX; t++)
for (int x = 0; x <= t; x++)
{
int y = (t-x);
allPossibleSigns(0, x, y);
}
}
If you want something more generic than func(arr[0], arr[1]);, you can replace it with:
Method[] methods = NewMain.class.getMethods();
for (Method m: methods)
{
if (m.getName().equals("func"))
m.invoke(null, arr);
}
and add some error checking. I used Integer... instead of int... in printAllPossibleSigns because of this approach (the above doesn't work for int...). This assumes you only have one function called func. If this is not the case, you'll have to add some additional checks.
For MAX = 4, it prints:
x = 0, y = 0
x = 0, y = 1
x = 0, y = -1
x = 1, y = 0
x = -1, y = 0
x = 0, y = 2
x = 0, y = -2
x = 1, y = 1
x = 1, y = -1
x = -1, y = -1
x = -1, y = 1
x = 2, y = 0
x = -2, y = 0
x = 0, y = 3
x = 0, y = -3
x = 1, y = 2
x = 1, y = -2
x = -1, y = -2
x = -1, y = 2
x = 2, y = 1
x = 2, y = -1
x = -2, y = -1
x = -2, y = 1
x = 3, y = 0
x = -3, y = 0
How this will be extended to 3 variable may not entirely be clear, so here's caller for 3 variables:
static void caller()
{
for (int t = 0; t < MAX; t++)
for (int x = 0; x <= t; x++)
for (int y = 0; y <= (t-x); y++)
{
int z = (t-x-y);
printAllPossibleSigns(0, x, y, z);
}
}
And that's about all you have to change, along with your function, obviously, and func(arr[0], arr[1]); if you didn't choose the generic approach.
I propose a spiral, non-recursively easiest.
For ease of reading the move is selected again in every step.
int x = 0;
int y = 0;
for (int t = 0; t < 100; ++t) {
func(x, y);
if (x <= 0 && y == 0) { // Widen spiral.
--x;
++y; // So next condition takes next.
} else if (x < 0 && y >= 0) { // Left, upper quadrant.
++x;
++y;
} else if (x >= 0 && y > 0) { // Right, upper.
++x;
--y;
} else if (x >= 0 && y <= 0) { // Right, lower.
--x;
--y;
} else if (x < 0 && y < 0) { // Left, lower.
--x;
++y;
} else {
throw new IllegalStateException("x = " + x + ", y = " + y);
}
}
I did not try the code! Check the conditions.
Maybe some knowledge of combinatorics would help here. To me this looks like you have a set of elements from -N to to +N. Now you want to call a function with for each variation of length == 7 those elements.
Such a range may be really big. Depending on the cost of the operation you want to call this might take longer than you live.
I would write an Iterator which delivers a new variation of the elements (which are your function parameters) on each call of next().
The implementation of such an iterator could you BigInteger, if you need big numbers. You could use an Array or List and change it's elements on each iteration. If you search for combinatorial algorithms or permutation / variation algorithms you might find details and maybe even implementations.
Another (similar) way (with more overhead, I think) would be to use just one number (e.g. a BigInteger) to mark the current variation. On each iteration you add 1 to this variation index number.
To get your parameters from this number you must perform a base transformation on this variation index. The base will be the number of elements in your elements set. The resulting number's digits each have the range of 0 to the number of elements -1. From this you can use each digit to get the parameters for your function call from the list of elements.
I did than some time ago and it works fine. Can't promise than I can find it.
For n dimensions:
Below I use positive numbers for coordinates. For every positive (greater 0) coordinate in a solution making the coordinate negative also is a solution (almost factor 2^n solutions more). (Using positive numbers simplifies the reading of a solution.)
This is a solutions for a coordinate vector of dimension n. The coordinates are chosen with ever growing "radius" = sum of coordinates.
static void func(int[] x) {
System.out.printf("%s%n", Arrays.toString(x));
}
/**
* Call many funcs with several coordinates.
* #param x n-dimensional coordinates.
* #param fromI starting index for variable coordinates.
* #param r radius, equal to the sum of x[>= fromIndex].
* #param t downward counter limiting the number of calls.
* #return new value of t.
*/
static int callFuncsForRadius(int[] x, int fromIndex, int r, int t) {
if (t <= 0) {
return t;
}
if (fromIndex >= x.length) { // Nothing more to vary.
if (r == 0) { // Read radius sum.
func(x);
--t;
}
return t;
}
for (int rNext = r; rNext >= 0; --rNext) {
x[fromIndex] = rNext;
t = callFuncsForRadius(x, fromIndex + 1, r - rNext, t);
if (t <= 0) {
break;
}
}
return t;
}
static int callFuncs(int[] x, int t) {
int r = 0;
while (t > 0) {
t = callFuncsForRadius(x, 0, r, t);
++r;
}
return t;
}
public static void main(String[] args) {
int n = 3;
int[] x = new int[n];
int t = 10; // N^n, where N = 2^31.
callFuncs(x, t);
}

Java - Recursive function of the Euclidean Algorithm

I can't seem to convert the following algorithm into Java successfully, please forgive the horrible picture quality but a question I'm working on asks:
I have tried to use the following code to represent the Euclidean Algorithm, but it doesn't seem to work. I don't really know how I would go about representing it in Java code. Any help?
public static int gcd(int x, int y) {
if (y == 0) {
return x;
} else if (x >= y && y > 0) {
return gcd(y, (x % y));
}
}
Thank you.
There is no arbitrary order between x and y.
Your code is not complete!
What if x < y? Your code does not return a value then!
What the book fails to mention is that the two parameters to the function do not necessarily need to be in descending order (ie x >= y). What you need to do is compute the gcd considering this fact.
Simply you can do the following:
public static int gcd ( int x , int y )
{
if ( y == 0 )
return x;
else if ( x >= y && y > 0)
return gcd ( y , x % y );
else return gcd ( y , x ); // if x < y then go ahead and switch them around.
}
You are almost there. You need to consider what happens when y > x, and return the result from the final else branch (hint: x and y can freely switch places).
You are almost there.
Your code does not compile, because there is no catch all clause that return from the function.
It really depends on whether you are going to pass negative values of y into this function. If you expect only positive values, just throw an exception.
public static int gcd(int x, int y) {
if (y == 0) {
return x;
} else if (x >= y && y > 0) {
return gcd(y, (x % y));
}
throw
new IllegalArgumentException(
String.format(
"Unexpected values for x(%d) and y(%d)",
Integer.valueOf( x ),
Integer.valueOf( y )
)
);
}
Here's what I have that accounts for negative numbers:
public static int gcd(int x, int y)
{
if (y == 0)
return x;
if (x < 0)
return gcd(x * -1, y); //turns the first parameter to a positive if it's initally negative
if (y < 0)
return gcd(x, y * -1); //turns the second parameter to a positive if it's initally negative
if (y <= x && x % y == 0)
return y;
return gcd(y, x%y);
}
Note with negative numbers, if you try to find the greatest common divisor, and either of the numbers is negative, you can just change it to a positive and the result would be the same.
If both of the numbers are negative, then I'm not sure what the gcd should be. 1? -1? idk so I left that out. The code I have just treats it as if they were both positive.

Categories