I have been practicing algorithms and I have found one that took me weeks and still couldn't solve. I couldn't come up with a complete algorithm but I have been working on an idea, and the code I wrote so far is :
Note: The reason I shared comprehensive problem is not to enlong question rather that I might have misunderstood the main point of problem at first place.
PROBLEM
A PropBot can only make two distinct movements. It can either move 10 cm forward, or turn towards the right by 45 degrees. Each of these individual movements takes one second of time.
Input
Your module has two inputs: the Cartesian coordinates of a point on the plane that the PropBot wants to get as close to as possible, and the maximum number of seconds that can be used to do this. At the beginning of the navigation, the robot is located at the origin, pointed in the +x direction. The number of seconds will be an integer between 0 and 24, inclusive. Both the x and y coordinates of the desired destination point will be a real number between -100 and 100, inclusive. The first entry in the input file will be the number of test
cases, t (0 < t ≤ 100). Following this line will be t lines, with each line containing three entries separated by spaces. The first entry will be the number of seconds PropBot has to get close to the point. The second entry is the x-coordinate of the point, and the third entry is the y coordinate of the point.
Output
Your program must return the distance between the goal point and the closest point the robot can get to within the given time. Your result should include at least one digit to the left of the decimal point, and exactly six digits to the right of the decimal point. To eliminate the chance of round off error affecting the results, we have constructed the test data so the seventh digit to the right of the decimal point of the true result is never a 4 or a 5.
Sample Input
2
24 5.0 5.0
9 7.0 17.0
Sample Output
0.502525 <- HOW ?
0.100505 OK
JAVA CODE
Enum Direction
public enum Direction {
EAST(1), N_EAST(2), NORTH(3), N_WEST(4), WEST(5), S_WEST(6), SOUTH(7), S_EAST(8);
private int direction;
private int index;
Direction(){
direction = 1;
index = 0;
}
Direction(int dir){
direction = dir;
}
int getDirection(){
return direction;
}
public int incrementDir(){
if(direction > 1 && direction <= 8){
direction = 8 - index++;
// Rotate towards right side
}
else if(direction == 1){
direction = 8;
index = 1;
}
return direction;
}
}
Abstract - Calculation.java
import java.awt.Point;
public abstract class Calculation {
public static Direction getDir(Point p){
int line = getCloseLine(p);
switch (line) {
case 1:
return Direction.EAST;
case 2:
return Direction.N_EAST;
// 2nd Quadrant
case 3:
return Direction.NORTH;
case 4:
return Direction.N_WEST;
// 3rd Quadrant
case 5:
return Direction.WEST;
case 6:
return Direction.S_WEST;
// 4th Quadrant
case 7:
return Direction.SOUTH;
case 8:
return Direction.S_EAST;
default:
return Direction.EAST;
}
}
public static int getSelectedLine(Point p){
int a = getCloseLine(p);
return a;
}
public static int getQuadrant(Point target) {
double x = target.getX();
double y = target.getY();
if (x > 0 && y > 0)
return 1;
else if (x < 0 && y > 0)
return 2;
else if (x < 0 && y < 0)
return 3;
else if (x > 0 && y < 0)
return 4;
// Means point lies on an Axis not in any Quadrant
return -1;
}
public static int getAxis(Point target) {
double x = target.getX();
double y = target.getY();
if (x > 0 && y == 0)
return 1;
else if (x == 0 && y > 0)
return 2;
else if (x < 0 && y == 0)
return 3;
else if (x == 0 && y < 0)
return 4;
else if( x == 0 && y == 0)
return 0;
return -1;
}
public static double getAngle(Point v2) {
double d = v2.getY() / v2.getX();
double ang = Math.toDegrees(Math.atan(d));
return ang;
}
public static int getSector(Point point) {
double angle = getAngle(point);
int quad = getQuadrant(point);
if(quad == -1)
return -1;
switch (quad) {
case 1:
if (angle < 45.0)
return 1;
else
return 2;
case 2:
if (angle < -45.0)
return 3;
else
return 4;
case 3:
if (angle < 45.0)
return 5;
else
return 6;
case 4:
if (angle < -45.0)
return 7;
else
return 8;
}
return -1;
}
public static int getCloseLine(Point p) {
int sec = getSector(p);
double angle = getAngle(p);
System.out.println("ANGLE : " + angle);
if(sec == -1){
int axis = getAxis(p);
switch(axis){
case 1:
return 1;
case 2:
return 3;
case 3:
return 5;
case 4:
return 7;
case 0:
return 0;
}
}
switch (sec) {
case 1:
if (angle < 22.5)
return 1;
else
return 2;
case 2:
if (angle < 67.5)
return 2;
else
return 3;
// 2nd Quadrant
case 3:
if (angle < -67.5)
return 3;
else
return 4;
case 4:
if (angle < -22.5)
return 4;
else
return 5;
// 3rd Quadrant
case 5:
if (angle < 22.5)
return 5;
else
return 6;
case 6:
if (angle < 67.5)
return 6;
else
return 7;
// 4th Quadrant
case 7:
if (angle < -67.5)
return 7;
else
return 8;
case 8:
if (angle < -22.5)
return 8;
else
return 1;
}
return -1;
}
}
Main.java
import java.awt.Point;
import java.util.Scanner;
public class Main {
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
int time = 0;
Point p = new Point(0, 0);
System.out.println("Enter time: ");
time = s.nextInt();
System.out.println( " X: ");
p.x = s.nextInt();
System.out.println( " Y: " );
p.y = s.nextInt();
if(!(time > 0 && time <= 24)){
s.close();
System.err.println("INPUT ERROR!");
return;
}
// Initialize bot facing +x direction
Bot b = new Bot(p, Direction.EAST);
int line = Calculation.getCloseLine(p);
while(time != 0 && b.getDirectionInt() != line){
// Rotate the face towards the point
b.rotate();
time--;
}
s.close();
}
}
Bot.java
import java.awt.Point;
public class Bot {
private Point location;
private Direction direction;
public Bot(){
location = new Point(0, 0);
direction = Direction.EAST;
}
public Bot(Point loc, Direction dir){
location = loc;
direction = dir;
}
public Point move(){
return location;
}
public int rotate(){
direction.incrementDir();
return direction.getDirection();
}
public int getDirectionInt(){
return direction.getDirection();
}
}
My approach was to divide the Cartesian plane into sectors and get a closet line to the input point and rotate the bot and then move ahead.
First issue : I got how the second case output was evaluated but I don't have any idea about the first one.
Line distribution as follows :
Second issue : If the bot moves diagonally (45 degrees) and then move either horizontally or vertically, after that it seems as if the whole Cartesian plane has moved and the code I wrote is not valid anymore.
Is my approach correct? if yes then how I improve it further?
If my approach is wrong? Please suggest a better alternative.
I think you could create a graph for every possible stop of the robot and its orientation. Which will be a triple (x, y, orientation) and orientation is one of 0,1,..,7. For each orientation i moving forward is given by the vector orientation_vec[i]
import networkx as nx
def one_second(G):
# In each step you can either go forward or turn. Create one edge
# for each node and these two possible moves.
orientation_vec = [(10000000, 0), (7071067, -7071067), (0, -10000000),
(-7071067, -7071067), (-10000000, 0), (-7071067, 7071067),
(0, 10000000), (7071067, 7071067)]
for node in G.nodes():
# edge to next orientation
G.add_edge(node, (node[0], node[1], (node[2] + 1)%8))
# edge going forward
G.add_edge(node, (node[0] + orientation_vec[node[2]][0],
node[1] + orientation_vec[node[2]][1],
node[2]))
def create_graph(max_time):
G = nx.Graph()
G.add_node(n=(0, 0, 0))
for t in range(max_time):
one_second(G)
print(len(G.nodes()))
We can now go through all nodes and find the closest to the target.
Once the graph is created we can find the shortest path by using dijkstra or A*. I used the networkx package for that.
import math
def find_closest_path(paths, end):
min_dist = end[0]**2 + end[1]**2
best_path = None
for key in paths.keys():
end_path = paths[key][-1]
path_to_end = (end_path[0] - end[0])**2 + (end_path[1]-end[1])**2
if path_to_end < min_dist:
min_dist = path_to_end
best_path = paths[key]
min_dist = math.sqrt(min_dist)/10**6
x = [p[0]/10**6 for p in best_path]
y = [p[1]/10**6 for p in best_path]
return min_dist, x, y
def robot_path(end, max_time):
create_graph(max_time)
paths = nx.single_source_shortest_path(G, (0, 0, 0), cutoff=max_time)
return find_closest_path(paths, end)
A plotting function i copy pasted from stackoverflow somewhere.
from pylab import *
import matplotlib.pyplot as plt
def plot_robot_path(x,y, end):
assert len(x) == len(y)
color=['b']*len(x) + ['r']
fig = plt.figure()
ax = fig.add_subplot(111)
scatter(x+[end[0]], y+[end[1]], s=100 ,marker='o', c=color)
[plot([x[i], x[i+1]], [y[i], y[i+1]], '-', linewidth=3 ) for i in range(len(x)-1)]
left,right = ax.get_xlim()
low,high = ax.get_ylim()
arrow(left, 0, right -left, 0, length_includes_head = True, head_width = 0.15 )
arrow(0, low, 0, high-low, length_includes_head = True, head_width = 0.15 )
grid()
show()
If we run it i get the following results:
end1=(5*10**6, 5*10**6)
max_time = 24
min_dist1, x1, y1 = robot_path(end1, max_time)
print(min_dist1)
plot_robot_path(x1, y1, (5, 5))
max_time=9
end2=(7*10**6, 17*10**6)
min_dist2, x2, y2 = robot_path(end2, max_time)
print(min_dist2)
plot_robot_path(x2, y2, (7, 17))
Here's compact backtracking solution:
import math
min_d = 1e+10
c = 0.70710678
xt = 5.0
yt = 5.0
def solve(remaining_t, x, y, dir_x, dir_y):
global min_d
d = math.sqrt((x - xt)**2 + (y - yt)**2)
if d < min_d:
min_d = d
if remaining_t == 0 or d - remaining_t * 10 >= min_d:
return
solve(remaining_t - 1, x + dir_x, y + dir_y, dir_x, dir_y)
solve(remaining_t - 1, x, y, c * (dir_x - dir_y), c * (dir_x + dir_y))
solve(24, 0.0, 0.0, 10.0, 0.0)
print(min_d)
It takes ~5 sec. on my computer.
A bit of explanation:
min_d - is the current minimum distance, we initialized it with a large value (should be greater than any distance could possibly be)
The solve function takes the following parameters:
remaining_t - remaining time in seconds, on each step it's decreased by 1
x and y - current coordinates of the bot
dir_x, dir_y - coordinates of bot's current direction vector. This vector has length 10 an we start with this vector pointing towards the x axis: (10, 0)
At each step the solve function considers current distance to the target point and updates min_d if needed. If there's no remaining time left or we have gone too far from the target point we stop going forward immediately. Otherwise we try 1) go forward 2) turn 45 degrees.
When bot is going forward (considering current direction) x becomes x+dir_x and y becomes y+dir_y. The direction vector stays the same.
When the bot turns, its coordinates stay the same but the direction vector changes. See https://en.m.wikipedia.org/wiki/Rotation_matrix (our constant c = sin 45 = cos 45)
Here is the Java Code for Algrid's answer :
public class Main {
static double min_d = 1e+10;
// diagonal distance factor cos(45), needs to multiply with hypotenuse
static double c = 0.707110678;
static double xt = 7.0;
static double yt = 17.0;
public static void solve(int time, double x, double y, double dirX, double dirY) {
double d = Math.sqrt(Math.pow((x - xt), 2) + Math.pow((y - yt), 2));
if( d < min_d )
min_d = d;
if( time == 0 || (d-time * 10) >= min_d ){
return;
}
solve(time - 1, x + dirX, y + dirY, dirX, dirY);
solve(time - 1, x, y, c * (dirX - dirY), c * (dirX + dirY));
}
public static void main(String[] args)
{
solve(9, 0.0, 0.0, 10.0, 0.0);
}
}// Class END
I am trying to draw a line between to co-ordinates on an array of pixels. The following code almost works, it produces a line going to the right places, however it does not fill in all of the pixels, but creates more of a dotted line :
double pixelsAcross = xFinish - xStart;
double pixelsUp = yFinish - yStart;
double acrossTemp = pixelsAcross;
double HCF = pixelsUp;
if (acrossTemp > 0) {
while (acrossTemp > 0) {
double temp = acrossTemp;
acrossTemp = HCF % acrossTemp;
HCF = temp;
}
pixelsUp /= HCF;
pixelsAcross /= HCF;
}
double repeats = (xFinish - xStart) / (pixelsAcross);
if (xFinish - xStart == 0 || xFinish + xStart == 0) {
repeats = (yFinish - yStart) / (pixelsUp);
}
int count = 0;
int xPos = xStart;
int yPos = yStart;
while (count <= repeats) {
screen.pixels.setColor(RED, xPos, yPos);
xPos += pixelsAcross;
yPos += pixelsUp;
count++;
}
It works by calculating the gradient as a fraction, then uses the numerator to work out how many pixels to go up and the denominator for across per repeat (these are simplified to the lowest available form).
How can I make it fill in the pixels between the ones I am currently creating, to make it a full line instead of a dotted line? Thanks.
I'm trying to create a bouncing ball that can move in the x, y, and z coordinates. The ball originally starts a couple units in the y-axis above the origin. I've managed to figure out how to get my ball to bounce up and down in the y plane, but am having troubles figuring out what I am doing wrong whenever I try adding the x and z trajectories.
double initialVelocity = userInputY;
double initVelX = userInputX;
double initVelZ = userInputZ;
double speed = 1/500.0;
double time = 0;
double x, y, z =0;
if (time == 0){
velocity+= initialVelocity;
}
time += speed
velocity = velocity - 9.8 * speed;
if (y+velocity < 0.1){ //collision detection against the floor
velocity *= -1;
}
if (z + initVelZ < 100){ //Collision detection for ceiling of 100 units
initVelZ *= -1;
}
if (x + initVelX < 50){ //Collision detection for if ball moves 50 units away from origin in x
initVelX *= -1;
}
else{
y += velocity;
x += initVelX;
z += initVelZ;
}
gl.gltranslated(x, y, z);
glu.gluSphere() //not really that important to specify
When I only use my y variable my ball has a bouncing animation but only moves up and down.
The comparisons for the z and x coordinates look backwards:
if (z + initVelZ < 100){ //Collision detection for ceiling of 100 units
initVelZ *= -1;
}
if (x + initVelX < 50){ //Collision detection for if ball moves 50 units away from origin in x
initVelX *= -1;
}
This inverts the velocity every time the new position is found to be inside the bounds. But they need to be inverted when the new position would end up being outside the bounds.
Also, you probably need to test for collision with the walls at both ends of each coordinate direction, so that the ball can bounce off all 6 walls.
For example for the z coordinate, the logic could look like this:
if (z + initVelZ < 0.0 || z + initVelZ > 100.0) {
initVelZ *= -1.0;
}
Same thing for the other coordinates.
So I am working on a blackjack game, I have wrote a render process which will render a card going out of the cards stack and sliding to the place where it shows all dealer's cards.
My method works fine, except one problem which I will elaborate:
Whenever Y coordinate reaches the target Y coordinate first, the sprite will only move on X-asis because it cant move Y anymore, instead of making a straight angle to the point.
So what it will do is move up diagonally and then instantly go to the right (in my case)
GIF:
(source: gyazo.com)
MP4 (choose mp4 in the (..) menu http://gyazo.com/bec6daadcb46bedc4777a3e4c5ff8c77)
As you can see, it does what I just said.
What did I do wrong? how can I make it motion in a straight angle to the target without going diagonal up and than turn right away?
My process code:
// If the target is on the right side of the start point
if (startPoint.getX() < endPoint.getX()) {
if (current.getX() < endPoint.getX()) {
current.x += moveSpeed;
if (current.getX() > endPoint.getX()) {
current.x = (int) endPoint.getX();
}
}
else {
xDone = true;
}
}
else {
if (current.getX() > endPoint.getX()) {
current.x -= moveSpeed;
if (current.getX() < endPoint.getX()) {
current.x = (int) endPoint.getX();
}
}
else {
xDone = true;
}
}
// Vise-versa
if (startPoint.getY() < endPoint.getY()) {
if (current.getY() < endPoint.getY()) {
current.y += moveSpeed;
if (current.getY() > endPoint.getY()) {
current.y = (int) endPoint.getY();
}
}
else {
yDone = true;
}
}
else {
if (current.getY() > endPoint.getY()) {
current.y -= moveSpeed;
if (current.getY() < endPoint.getY()) {
current.y = (int) endPoint.getY();
}
}
else {
yDone = true;
}
}
// Callback, dw about it
CardContainer.getCardSprite(CardContainer.SPECIAL, 0).drawSprite((int) current.getX(), (int) current.getY());
// Alert finished, in poisiuton
if (xDone && yDone) {
ch.submitCard(card);
}
current = current position
startPoint = the start point
endPoint = the end point
Thanks!
EDited code:
private void applyMovement(double alpha) {
double dx = endPoint.getX() - startPoint.getX();
double dy = endPoint.getY() - startPoint.getY();
this.current.setLocation(startPoint.getX() + alpha * dx, startPoint.getY() + alpha * dy);
}
public void process() {
double alpha = (double) stepsDone / distance;
applyMovement(alpha);
stepsDone++;
// Callback, dw about it
CardContainer.getCardSprite(CardContainer.SPECIAL, 0).drawSprite((int) current.getX(), (int) current.getY());
// Alert finished, in poisiuton
if (stepsDone >= distance) {
ch.submitCard(card);
}
}
Distance calculation:
this.distance = (int) start.distance(end);
Used Point2D distance method:
public double distance(Point2D pt) {
double px = pt.getX() - this.getX();
double py = pt.getY() - this.getY();
return Math.sqrt(px * px + py * py);
}
I would recommend to not use any form of "slope" in such a computation. You will run into problems when the difference in x-direction approaches zero, because then the slope will tend towards infinity.
Assuming that your points are Point2D.Double (or something similar - you should include this kind of information in your questions!), you can compute the movement as follows:
private Point2D.Double initial = ... // The initial position
private Point2D.Double current = ... // The current position
private Point2D.Double target = ... // The target position
void applyMovment(double alpha) {
double dx = target.getX() - initial.getX();
double dy = target.getY() - initial.getY();
current.x = initial.getX() + alpha * dx;
current.y = initial.getY() + alpha * dy;
}
The applyMovment method sketched here can be called with a double value between 0.0 and 1.0, where 0.0 corresponds to the initial position and 1.0 corresponds to the target position. This is just a Linear Interpolation.
So for example, when you have some sort of loop for the animation, you can use the method as follows:
int numberOfSteps = 10;
for (int i=0; i<=numberOfSteps; i++)
{
double alpha = (double)i / numberOfSteps;
applyMovement(alpha);
repaint();
}
This works for any arrangement of the start- and end points, without any sign- or direction issues. It just interpolates between the two positions.
Your calculation needs to be based upon moving the currentY and currentX along a specific line, not a specific set of intervals (moveSpeed). The formula for graphing points on a line is:
y = mx + b
Where x and y are the varying points, m is equal to the slope of the line, and b is what's called the y-intercept.
Your slope is calculated by the formula:
double slope = ((double) endPoint.getY() - startPoint.getY()) / ((double) endPoint.getX() - startPoint.getX());
And the Y intercept can be calculated by just plugging in a bunch of known values once you have them:
double yIntercept = (double) endPoint.getY() - (slope * endPoint.getX())
Then, just loop through the count of the difference in X:
for (int xVal = startPoint.getX(); xVal < endPoint.getX(); xVal++){
currentX = xVal;
currentY = (slope * xVal) + yIntercept;
}
And you should be good.
Warning: this is all off of the top of my head, I don't know if it'll compile.
I need to make the game impossible to win, so I used this code to move AI paddle.
My code may be slightly unreadable at first, so here's the theory behind it: the paddle always hits the ball with the middle point, unless the ball is closer to the edge than half of the paddle length, then the paddle stops to move with one of its ends touching top or bottom window frame.
if (ball.getY() < getHeight() - HEIGHT / 2
&& ball.getY() > HEIGHT / 2) {
paddleRight.setLocation(getWidth() - 3 * WIDTH, ball.getY()
- HEIGHT / 2);
image2.setLocation(getWidth() - 3 * WIDTH, ball.getY() - HEIGHT
/ 2);
image2.sendToFront();
} else if (ball.getY() < HEIGHT / 2) {
paddleRight.setLocation(getWidth() - 3 * WIDTH, 0);
image2.setLocation(getWidth() - 3 * WIDTH, 0);
image2.sendToFront();
} else {
paddleRight.setLocation(getWidth() - 3 * WIDTH, getHeight()
- HEIGHT);
image2.setLocation(getWidth() - 3 * WIDTH, getHeight() - HEIGHT);
image2.sendToFront();
}
My ball also speeds up randomly during the game:
boolean bool = rand.nextBoolean();
if (bool)
if (dx > 0)
dx += 1;
else
dx -= 1;
else if (dy > 0)
dy += 0.5;
else
dy -= 0.5;
ball movement consist of X and Y axis movement
And at some specific speed, if the paddle gets to one of the corners it starts to blink back and forth between top and bottom corner. I can't find the reason for that in my code.
Full code here
Your if statement does not correctly handle conditions where the ball is exactly at boundaries between conditions. You have:
if (ball.getY() < getHeight()-HEIGHT/2 && ball.getY() > HEIGHT/2) {
...
} else if (ball.getY() < HEIGHT/2) {
...
} else {
...
}
But what happens when ball.getY() == HEIGHT/2? In this case, it will fall through to the third block, but this is not the desired behavior - it will briefly jump to the third condition when ball.getY() == HEIGHT/2, hence flicker when the ball's Y position is at that edge. Instead, make a simple change to your second condition:
if (ball.getY() < getHeight()-HEIGHT/2 && ball.getY() > HEIGHT/2) {
...
} else if (ball.getY() <= HEIGHT/2) { // note <= instead of <
...
} else { // ball.getY() >= getHeight()-HEIGHT/2
...
}
This should take care of the transition cases.
By the way, unrelated, you should use curly braces in nested if statements like the one you have, e.g.:
if (bool) {
if (dx > 0)
dx += 1;
else
dx -= 1;
} else {
if (dy > 0)
dy += 0.5;
else
dy -= 0.5;
}
While your original logic happens to have the same effect in this case, your original code, despite its slightly misleading indentation, actually had this structure:
if (bool)
...
else if (dy > 0)
...
else // !bool && dy <= 0
...
That probably doesn't reflect the actual intent of that if and, even though it happens to work here, adding the curly braces is a good habit to get into because it can prevent potential subtle logic errors in more complex code.