I have to use an algorithm to find a path for a school project. I have a sequence of 12 goals on the map, which must be reached before the end of the path. The map is a 70x70 array. What I already did:
I find all the goals and add in a list
The goals are marked by numbers 10 to 21, and i put on a list 0 to 12. (21 IS THE last goal)
private casa[] acharCasas(){
casa[] casas = new casa[12];
for (int linha = 0; linha < jogo.getMapa().length; linha++) {
for (int coluna = 0; coluna < jogo.getMapa()[0].length; coluna++) {
if(jogo.getMapa()[linha][coluna] >= 10 && jogo.getMapa()[linha][coluna] <= 21){
casas[jogo.getMapa()[linha][coluna]-10] = new casa (linha,coluna);
}
}
}
return casas;
}
I make heuristics total cost + distance from other houses unvisited
casas_visitadas are the number of visited goals, and posL = row, posC = Col. What I wanted to do is calculate the distance from where you are to the next uncompleted objective, + distance from this objective to the next objective... to the final goal.
public double distanciaDasOutrasCasas(){
double somaTotal = 0;
int posL = linha,posC = coluna;
for (int i = casas_visitadas; i < 12; i++) {
somaTotal += Math.sqrt(
Math.pow( (double) (casas[i].linha - posL),2) +
Math.pow( (double) (casas[i].coluna - posC),2)
);
posL = casas[i].linha;
posC = casas[i].coluna;
}
return somaTotal;
}
I check all possible movements
And I add them to the ArrayList. Here are the constructors:
public ArrayList<Direcao> movimentosPossiveis(){
ArrayList<Direcao> movimentos = new ArrayList<>();
if (linha > 0) movimentos.add(Direcao.CIMA); // if isnt the first row add UP
if (coluna > 0) movimentos.add(Direcao.ESQUERDA);//if inst the first col add LEFT
if (linha < jogo.getMapa().length) movimentos.add(Direcao.BAIXO); // last row add down
if (coluna < jogo.getMapa()[linha].length) movimentos.add(Direcao.DIREITA); // add right
return movimentos;
}
public ArrayList<cell> permutaCelula(){
ArrayList<Direcao> movimentos = movimentosPossiveis();
ArrayList<cell> novasCelulas = new ArrayList<>();
for (Iterator<Direcao> iterator = movimentos.iterator(); iterator.hasNext();) {
Direcao D = iterator.next();
switch (D){
case CIMA:
novasCelulas.add(
new cell(this /*father*/, Direcao.CIMA /*direction from father */, custo_total /*total cost*/, linha-1/*row-1*/, coluna/*col*/, casas_visitadas/*goals visiteds*/)
);
break;
case BAIXO:
novasCelulas.add(
new cell(this, Direcao.CIMA, custo_total, linha+1, coluna, casas_visitadas)
);
break;
case ESQUERDA:
novasCelulas.add(
new cell(this, Direcao.CIMA, custo_total, linha, coluna-1, casas_visitadas)
);
break;
case DIREITA:
novasCelulas.add(
new cell(this, Direcao.CIMA, custo_total, linha, coluna+1, casas_visitadas)
);
break;
}
}
return novasCelulas;
}
public double convertePesoDoChao(){
System.out.println(" L " + this.linha + " Coluna "+ coluna);
int chao = jogo.getMapa()[this.linha][this.coluna];
switch (chao) {
case 0:
return 200; //rock field, slowest field
case 1:
return 1;
case 2:
return 3.5;
case 3:
return 5;
case 22:
if(quantidadeDeCavaleirosVivos() != 0) return Double.MAX_VALUE; // cant go to master without find all goals
finalizado = true; // found
caminhoFinal = converteCelulaParaMovimentos(); // converts cells movements
return 0;
default:
casas_visitadas++;
return 0;
}
}
public cell(cell pai, Direcao oPaiMexeuPraQualDirecao, double custo_total, int linha, int coluna, int casas_visitadas) {
this.pai = pai; // father
this.oPaiMexeuPraQualDirecao = oPaiMexeuPraQualDirecao; // direction from father
this.linha = linha; //row
this.coluna = coluna; // col
this.casas_visitadas = casas_visitadas; //goals visited
this.custo_total = custo_total + convertePesoDoChao(); // fathers total cost + field cost
}
public cell(int linha, int coluna) { //starter cell
this.linha = linha;
this.coluna = coluna;
this.custo_total = 0;
this.pai = null;
}
The heuristic
my heuristic is total cost + distancefromgoals
public double heuristica(){
return custo_total *10 + distanciaDasOutrasCasas();
}
Primary loop
if(!finalizado){
this.jogo = jogo;
colunaInicial = jogo.getColuna();
linhaInicial = jogo.getLinha();
casas = acharCasas();
ArrayList<cell> franja = new ArrayList<>();
franja.add(new cell(linhaInicial,colunaInicial));
while(!finalizado){
Collections.sort(franja);
franja.addAll(franja.get(0).permutaCelula());
franja.remove(0);
}
}
The problem
It's taking too long, over three hours total, to find the way... any tips? I checked on the Netbeans profiler, and the problem is sorting the array.
My full code is here
obs: on full code, a game call the action move, and on the first call I need to find the best way, and save it, later I just pop the stack of movements
obs2: sorry my bad english, i used google translator on some parts
Related
I have an unsorted list with float values. I'm able to create graph from that.
But now, I want to create batches based on up and downs in that graph.
For example, I've an list like below
[6.17, 6.13, 6.12, 6.19, 6.2, 6.21, 6.28, 6.17, 6.2, 6.28]
First batch will be decreasing from 6.17 to 6.12 (index 0 to index 2).
Then second batch will be increasing from 6.12 to 6.28(index 3 to index 6)
All I can think of is to create two methods
increasing(List values) - to get all incremental values
decreasing(List values) - to get all decremental values
Call decreasing() method in increasing() method whenever I find sudden drop in values with sublist from last accessed element and vice-versa
But I don't think this is good idea.
Please find the graph image for reference
I've an object TimeAnalysis which contains start and end values.
In first case start=6.17 and end=6.12.
In second case start=6.12 and end=6.28
I want to get list of TimeAnalysis objects.
To actually split them up you can use Math.signum.
private List<List<Double>> splitAtInflectionPoints(List<Double> data) {
List<List<Double>> split = new LinkedList<>();
int start = 0;
for (int i = 1; i < data.size() - 1; i++) {
double leftSlope = Math.signum(data.get(i) - data.get(i - 1));
double rightSlope = Math.signum(data.get(i + 1) - data.get(i));
if (leftSlope != rightSlope) {
split.add(data.subList(start, i + 1));
start = i;
}
}
if (start < data.size()) {
split.add(data.subList(start, data.size()));
}
return split;
}
private void test() {
List<Double> data = Arrays.asList(6.17, 6.13, 6.12, 6.19, 6.2, 6.21, 6.28, 6.17, 6.2, 6.28);
for (List<Double> run : splitAtInflectionPoints(data)) {
System.out.println(run);
}
}
Prints:
[6.17, 6.13, 6.12]
[6.12, 6.19, 6.2, 6.21, 6.28]
[6.28, 6.17]
[6.17, 6.2, 6.28]
You can create a class that stores the list of values and type of Batch.
class Batch {
enum BatchType {
INCREASING,
DECREASING
};
BatchType batchType;
List<Float> values;
}
Now you can have a method called splitIntoBatches which returns a list of Batch.
public List<Batch> splitIntoBatches(List<Float> values) {
// Traverse through the list once and create list of batches.
}
You can loop through elements in your array and track if it's increasing or decreasing or possibly the same.
I used TimeAnalysis class you mentioned and wrote a static method splitList().
It is to split the list of time floats into list of TimeAnalysis.
public static List<TimeAnalysis> splitList(List<Float> timeList) {
if(timeList.size() == 0) return new ArrayList<>();
else if(timeList.size() == 1) {
List<TimeAnalysis> batches = new ArrayList<>();
batches.add(new TimeAnalysis(timeList.get(0), timeList.get(0)));
return batches;
}
ArrayList<TimeAnalysis> batches = new ArrayList<>();
// 0: same, 1: inc, 2: dec
int type = -1;
TimeAnalysis lastBatch = new TimeAnalysis(timeList.get(0), timeList.get
batches.add(lastBatch);
timeList.remove(0);
for(float t : timeList) {
switch(type) {
case 0: // same
if(t > lastBatch.end) { // inc
lastBatch = new TimeAnalysis(lastBatch.end, t);
batches.add(lastBatch);
type = 1;
} else if(t < lastBatch.end) { // dec
lastBatch = new TimeAnalysis(lastBatch.end, t);
batches.add(lastBatch);
type = 2;
}
break;
case 1: // inc
if(t > lastBatch.end) { // inc
lastBatch.end = t;
} else if(t < lastBatch.end) { // dec
lastBatch = new TimeAnalysis(lastBatch.end, t);
batches.add(lastBatch);
type = 2;
} else { // same
lastBatch = new TimeAnalysis(lastBatch.end, t);
batches.add(lastBatch);
type = 0;
}
break;
case 2: // dec
if(t > lastBatch.end) { // inc
lastBatch = new TimeAnalysis(lastBatch.end, t);
batches.add(lastBatch);
type = 1;
} else if(t < lastBatch.end) {
lastBatch.end = t;
} else {
lastBatch = new TimeAnalysis(lastBatch.end, t);
batches.add(lastBatch);
type = 0;
}
break;
default:
if(t > lastBatch.end) type = 1;
else if(t < lastBatch.end) type = 2;
else type = 0;
lastBatch.end = t;
break;
}
}
return batches;
}
When I run:
Scanner in = new Scanner(System.in);
ArrayList<Float> input = new ArrayList<>();
for(int i=0; i<10; i++) input.add(in.nextFloat());
List<TimeAnalysis> output = TimeAnalysis.splitList(input);
for(TimeAnalysis batch : output) System.out.println("start: " + batch.start + ", end: " + batch.end);
and give your data as input, I get:
start: 6.17, end: 6.12
start: 6.12, end: 6.28
start: 6.28, end: 6.17
start: 6.17, end: 6.28
I have two classes: class Creature which contains ArrayList boids, and class Food.
Boids have a few parameters:
Creature(float posX, float posY, int t, int bth, int ah) {
location = new PVector(posX, posY);
vel = new PVector(random(-5,5), random(-5, 5));
acc = new PVector();
type = t;
if (t == 1) { btype = bth; }
else { health = bth; }
if (t == 1) { age = ah; }
else { hunger = ah; }
wdelta = 0.0;
action = 0;
if (btype == 1) { mass = 5.0; }
else { mass = 7.0; }
}
Food class has this method:
void foodtime(ArrayList boids) {
for (int i = 0; i < boids.size(); i++) {
Creature boid = (Creature) boids.get(i);
float distance = PVector.dist(location, boid.location);
if (distance < 0.5) {
bnumadj = i;
count++;
if (count == quantity) {
planet.food.remove(this);
count = 0;
bnumadj = -1;
}
}
}
}
What I'm trying to achieve is that if a boid "eats" the food, their boid type (btype) changes from 2 to 1.
I'm trying to use bnumadj variable to feed it back to the boid in this method:
void boid(ArrayList boids) {
for (int i = 0; i < boids.size(); i++) {
if (i == bnumadj) {
this.btype = 1;
bnumadj = -1;
}
}
}
Where am I going wrong?
This seems like a very convoluted way to do this, so I'm not surprised you're having issues. You're comparing values to indexes, which doesn't make a ton of sense to me.
Instead, try using a simple nested loop to do what you want. You can use an Iterator to make it easier to remove items while iterating.
ArrayList<Creature> boids = new ArrayList<Creature>();
ArrayList<Food> food = new ArrayList<Food>();
//populate ArrayLists
void draw(){
for(Creature boid : boids){
Iterator<Food> foodIter = food.iterator();
while(foodIter.hasNext()){
Food f = foodIter.next();
float distance = PVector.dist(boid.location, food.location);
if (distance < 0.5) {
boid.btype = 1;
foodIter.remove(); //removes the food
}
}
}
//draw the scene
}
I suppose you could move the second iteration using the Iterator inside the Creature type, but the basic idea is this: keep it simple by using an Iterator to remove the Food instead of trying to match indexes.
I'm trying to write a program that opens a txt file and display information from that txt file. Java is my first language, and I'm taking java as a second language class since there's no beginning java class in my school. I'm struggling with this code for about an week. Any little help would be helpful. Appreciate for your help.
It keeps saying :
Exception in thread "main"java.lang.ArrayIndexOutOfBoundsException:6
at store.Franchise.<init>(Franchise.java:10)
at store.FileIO.readData(FileIO.java:10)
at store.Driver.main(Driver.java:9)
Here is what I've got:
Sample txt file:
Day1 Day2 Day3 Day4 Day5
2541.56 2258.96 2214 2256 2154
2041.56 1758.96 1714 1756 1654
3041.56 2758.96 2714 2756 2654
3563.54 3280.94 3235.98 3277.98 3175.98
2547.21 2264.61 2219.65 2261.65 2159.65
4040.55 3757.95 3712.99 3754.99 3652.99
Store.java:
package store;
import java.io.IOException;
public class Store {
private float salesByWeek[][];
public Store() {
salesByWeek = new float[5][7];
// assign the array value at index 5, t to salesByWeek
}
public void setSaleForWeekDayIntersection(int week, int day, float sale) {
salesByWeek[week][day] = sale;
// store the sale value to SalesByWeek array at the index pointed to by week, day
// for exaample, it can be week 2 and day 3 (Wednesday)
}
float[] getSalesForEntireWeek(int week) {
// this will find the total sales for the whole week - all 5 days or 7 days including week ends Saturday and Sunday
float[] sales = new float[7];
// declare an array of type float and of size 7 - name the array as sales
for (int d = 0; d < 7; d++)
{
sales[d] = salesByWeek[week][d];
// the index d runs from 0 to 7
}
return sales;
}
float getSaleForWeekDayIntersection(int week, int day) {
return salesByWeek[week][day];
// the return value is the arraycontent pointed to by index week and day
}
float getTotalSalesForWeek(int week) {
float total = 0;
for (int d = 0; d < 7; d++)
{
total += salesByWeek[week][d];
// increment total by adding the array content salesByWeek at index week, d ( if d is the day)
}
return total;
// send the value of total back to the caller function
}
float getAverageSalesForWeek(int week) {
return getTotalSalesForWeek(week) / 7;
// divide the total sales for the whole week by 7 so that we get the average sales and return it
}
float getTotalSalesForAllWeeks() {
float total = 0; // declare a total variable of type float and initialize to 0 ( zero)
for (int w = 0; w < 5; w++)
{
total += getTotalSalesForWeek(w);
// sum up the total for the whole week and store it to the total variable
}
return total;
// return the sum computed above
}
float getAverageWeeklySales() {
return getTotalSalesForAllWeeks() / 5;
// AVERAGE for 5 days - just Monday to Friday only - excludes the week ends
}
int getWeekWithHighestSaleAmount() {
// top performing sales in the whole week
int maxWeek = 0;
float maxSale = -1;
for (int w = 0; w < 5; w++)
// run the for loop from 0 to 5 in steps of 1
{
float sale = getTotalSalesForWeek(w);
// first store the total sales in to the sale variable of type float
if (sale > maxSale)
{ // if at all if we find any amount greater than the max sale then replace max sale with the new sale amount
// and also note down the contributor - in the sense that which w ( week) achieved top sales
maxSale = sale;
maxWeek = w;
}
}
return maxWeek;
}
int getWeekWithLowestSaleAmount() {
int minWeek = 0;
float minSale = Float.MAX_VALUE;
for (int w = 0; w < 5; w++)
{
float sale = getTotalSalesForWeek(w);
if (sale < minSale)
{
minSale = sale;
minWeek = w;
}
}
// comments are same as the top sales except in reverse order
// first store an arbitary minimum sale figure
// then compare each running week's vaue with the lowest
// if at all when we encounter any value lower than the preset value then replace it
return minWeek;
// finally return the minimum value in that week
}
public void analyzeResults() {
for (int w = 0; w < 5; w++) // run the for loop from 0 to 5
{
System.out.printf("---- Week %d ----\n", w); // print a title decoration
System.out.printf(" Total sales: %.2f\n", getTotalSalesForWeek(w)); // display or print out the total sales summed earlier in called function
System.out.printf(" Average sales: %.2f\n", getAverageSalesForWeek(w)); // display the average sales figure
}
System.out.printf("\n");
System.out.printf("Total sales for all weeks: %.2f\n", getTotalSalesForAllWeeks()); // print sum of the sales for the entire week
System.out.printf("Average weekly sales: %.2f\n", getAverageWeeklySales()); // print weekly average sales
System.out.printf("Week with highest sale: %d\n", getWeekWithHighestSaleAmount()); // print highest performing or top sales
System.out.printf("Week with lowest sale: %d\n", getWeekWithLowestSaleAmount()); // print lowest sales or the struggling week
}
public void setsaleforweekdayintersection(int week, int day, float f) {
}
}
Franchise.java:
package store;
public class Franchise {
private Store stores[];
public Franchise(int num) { // now for a franchise store
stores = new Store[num]; // instantiate an array object of type class Store
// the class is Store
// the objects are named as stores
for(int i=0; i<=num; i++) stores[i] = new Store();
}
public Store getStores(int i) { // GETTER display or return values
return stores[i];
}
public void setStores(Store stores, int i) { // setter assign values
this.stores[i] = stores;
}
}
FileIO.java:
package store;
import java.io.*;
import java.util.StringTokenizer;
public class FileIO {
// Franchise readData(String filename)
Franchise readData(String filename, int numstores) {
Franchise f1 = new Franchise(numstores);
boolean DEBUG = true;
int ctr = 0;
// open the file
// read the line
// parse the line - get one value
// and set it in the correct location in 2 d array
try {
FileReader file = new FileReader(filename); // file is equivalent to a file pointer in c/c++
BufferedReader buff = new BufferedReader(file); // buffered reader will read a chunk in to the variable buff
boolean eof = false;
while (!eof) {
String line = buff.readLine();
ctr++;
if (line == null)
eof = true;
else {
if (DEBUG)
System.out.println(line);
if (ctr > 1) {
StringTokenizer a = new StringTokenizer(line);
for (int week = 0; week < 5; week++) {
for (int day = 0; day < 7; day++) {
String l = a.nextToken();
float f = Float.parseFloat(l); // parseFloat will store to variable f of type float
f1.getStores(ctr - 2)
.setsaleforweekdayintersection(week,
day, f);
if (DEBUG)
System.out.print("f" + f + " ");
}
}
}
}
}
} catch (IOException f2) {
}
return f1;
}
}
Driver.java:
package store;
public class Driver
{
public static void main(String[] args)
{
FileIO readdata = new FileIO();
Franchise f1 = readdata.readData("E:/Files/Salesdat.txt", 6);
System.out.println("Data read");
}
}
DriverImpl.java ( I got no idea why I need this subclass, but my tutor told me that I need this):
package store;
public class DriverImpl extends Driver {
}
I would like to change the line 10 in Franchise.java to
for(int i=0; i<num; i++) stores[i] = new Store();
Notice I removed the <= and put an = instead. Whenever dealing with array indices, one should always use the < comparator with the size as a good practice.
Valid indexes in an array are 0 to length - 1. Change <= to < like,
stores = new Store[num];
// the class is Store
// the objects are named as stores
for(int i=0; i<num; i++) stores[i] = new Store(); //stores[num] is invalid.
I'm trying to write a recursive function in Java, to determine how to finish for a game of Darts. Basically, you have a maximum of 3 darts, en you have to finish with a double.
If you don't know the rule of Darts x01 games with Double Out finishing, it's difficult to understand this question... Let me try to explain. For simplicity, I keep the Bull's eye out of the equation for now.
Rules:
1) You have three darts which you can throw at number 1 through 20
2) A single hit can have a single, double or triple score
E.g. you can hit:
single 20 = 20 points or
double 20 = 40 points or
triple 20 = 60 points
3) In one turn, you can score a maximum of 180 points (3x triple 20 = 3*60 = 180). Anything higher than 180 is impossible. This doesn't mean anything below 180 IS possible. 179 for example, is impossible as well, because the next best score is triple20+triple20+triple19 = 167
4) Normally, you start at 501, and you throw 3 darts, untill you have exactly 0 points left.
5) Now, in Double Out, it is required that the last dart hits a Double
E.g. if you have 180 points left, you cannot finish, because your last dart has to be a double. So the maximum (with ignoring the bulls eye) = triple20 + triple20 + double20 = 160
And if your score is 16, you can simply finish using 1 dart by hitting the double 8.
Another example, if your score is 61, you can hit triple17 + double5 (= 51 + 10)
Current Code
Anyway, below is what I have so far. I know it's far from what I need, but no matter what I try, i always get stuck. Perhaps someone can share his thoughts on an another approach
private class Score{
int number; // the actual number, can be 1...20
int amount; // multiplier, can be 1, 2 or 3
public Score(int number, int amount){
this.number = number; // the actual number, can be 1...20
this.amount = amount; // multiplier, can be 1, 2 or 3
}
public int value()
{
return number * amount; // the actual score
}
public void increment()
{
if(this.amount == 0)
this.amount = 1;
this.number++;
if(this.number >= 20)
{
this.number = 0;
this.amount++;
if(this.amount >= 3)
this.amount = 3;
}
}
}
public ArrayList<Score> canFinish(int desired, ArrayList<Score> score){
// If this is the case -> we have bingo
if(eval(score) == desired) return score;
// this is impossible -> return null
if(eval(score) > 170) return null;
// I can't figure out this part!!
Score dart3 = score.remove(2);
Score dart2 = score.remove(1);
if(dart2.eval() < 60){
dart2.increment();
}
else if(dart3.eval() < 60){
dart3.increment();
}
score.add(dart2);
score.add(dart3);
return canFinish(desired, score);
}
public int eval(ArrayList<Score> scores)
{
int total = 0;
for(Score score : scores){
total += score.value();
}
return total;
}
I want to simply call:
ArrayList<Score> dartsNeeded = new ArrayList<Score>();
dartsNeeded.add(new Score(16, 2)); // Add my favourite double
dartsNeeded.add(new Score(0, 0));
dartsNeeded.add(new Score(0, 0));
// and call the function
dartsNeeded = canFinish(66, dartsNeeded);
// In this example the returned values would be:
// [[16,2],[17,2],[0,0]] -> 2*16 + 2*17 + 0*0 = 66
// So I can finish, by throwing Double 17 + Double 16
So, if it is impossible to finish, the function would return null, but if there is any possible finish, i reveive that ArrayList with the 3 darts that I need to make my desired score...
Short Summary
The problem is that the above code only helps to find 1 dart, but not for the combination of the two darts. So canFinish(66, darts) works -> but canFinish(120, darts) gives a StackOverflow Exception. For 120, I would expect to get somthing like triple20, double14, double16 or any other valid combination for that matter.
If you log the scores that canFinish tries, you can see that there are a lot of possibilities missed out. Values of 20 are ignored, and one dart is incremented completely before the other dart values are modified.
Instead, it can be solved recursively as follows. canFinish(desired, score) returns any combination of darts that can be added to score to give the total of desired. Call it with a list of however many darts you know, or any empty list to find any possibility.
canFinish(desired, score)
if darts sum to desired, return desired
if there are fewer than 3 darts in score
for each possible value of a dart (if it's the last dart, check for a double)
add dart to score
if canFinish(desired, score) != null
return canFinish(desired, score)
end
remove dart from score
end
end
return null
end
I ended up using the following functions. Which kind of is a combination of switch statments and recursion... Hope someone finds it as usefull as I
public static void getCheckout(int score, int fav_double, ICheckOutEvent listener)
{
if(score > 170) return;
if(score == 170) listener.onCheckOut("T20 T20 Bull");
ArrayList<Dart> darts = new ArrayList<Dart>();
darts.add(new Dart(fav_double, 2));
darts.add(new Dart(0,0));
darts.add(new Dart(0,0));
darts = getDarts(score, darts);
if(darts != null) {
listener.onCheckOut(toString(darts));
return;
}
for(int dubble = 20 ; dubble >= 1 ; dubble--)
{
if(dubble == fav_double) continue;
darts = new ArrayList<Dart>();
darts.add(new Dart(dubble, 2));
darts.add(new Dart(0,0));
darts.add(new Dart(0,0));
darts = getDarts(score, darts);
if(darts != null){
listener.onCheckOut(toString(darts));
return;
}
}
}
public static ArrayList<Dart> getDarts(int desired, ArrayList<Dart> score)
{
Dart dart1 = canFinish(desired);
if(dart1 != null){
score.set(0, dart1);
return score;
}
int rest = desired - score.get(0).value();
Dart dart2 = canScore(rest);
if(dart2 != null)
{
score.set(0, score.get(0));
score.set(1, dart2);
return score;
}
Dart temp = score.get(1);
if(temp.increment())
{
rest = desired - score.get(0).value() - temp.value();
score.set(0, score.get(0));
score.set(1, temp);
Dart dart3 = canScore(rest);
if(dart3 != null)
{
score.set(2, dart3);
return score;
}
if(rest > 60 && temp.increment())
temp.estimate(rest / 2);
score.set(1, temp);
return getDarts(desired, score);
}
return null;
}
public static int eval(ArrayList<Dart> scores)
{
int total = 0;
for(Dart score : scores){
total += score.value();
}
return total;
}
public static Dart canFinish(int points)
{
switch(points)
{
case 2: return new Dart(1, 2);
case 4: return new Dart(2, 2);
case 6: return new Dart(3, 2);
case 8: return new Dart(4, 2);
case 10: return new Dart(5, 2);
case 12: return new Dart(6, 2);
case 14: return new Dart(7, 2);
// etc. etc.
case 40: return new Dart(20, 2);
case 50: return new Dart(25, 2);
}
return null;
}
public static Dart canScore(int points)
{
switch(points)
{
case 1: return new Dart(1, 1);
case 2: return new Dart(2, 1);
case 3: return new Dart(3, 1);
// etc. etc.
case 20: return new Dart(20, 1);
case 21: return new Dart(7, 3);
case 22: return new Dart(11, 2);
//case 23: impossible
case 24: return new Dart(12, 2);
// etc. etc.
case 57: return new Dart(19, 3);
case 60: return new Dart(20, 3);
}
return null;
}
And for completeness, here's the Dart class I created as a helper
private static class Dart{
int number;
int amount;
public Dart(int number, int amount){
this.number = number;
this.amount = amount;
}
public int value()
{
return number * amount;
}
public void estimate(int estimate)
{
Dart temp = canScore(estimate);
if(temp != null){
this.amount = temp.amount;
this.number = temp.number;
} else{
this.number = estimate / 3;
if(number >= 19)
this.number = 19;
this.amount = 3;
}
}
public boolean increment()
{
if(this.amount == 3 && this.number == 20)
return false;
if(this.amount == 0)
this.amount = 1;
this.number++;
if(this.number >= 20)
{
this.number = 20;
this.amount++;
if(this.amount >= 3){
this.amount = 3;
}
}
return true;
}
public String toString()
{
return "["+number+","+amount+"]";
}
}
class RecursiveDartboard {
public Set<Out> outsFor(int target) {
HashSet<Out> outs = new HashSet<>();
for (Score doubleScore : doubles()) {
List<Score> scores = new ArrayList();
scores.add(doubleScore);
outs.addAll(recursiveOutsFor(target, scores)
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList())
);
}
return outs;
}
private List<Optional<Out>> recursiveOutsFor(int target, List<Score> scores) {
List<Optional<Out>> outs = new ArrayList<>();
Out possibleOut = new Out(scores);
if (possibleOut.target() == target) {
outs.add(of(possibleOut));
} else if (scores.size() == 3) {
outs.add(empty());
} else {
for (Score score : allPossibleScores()) {
List<Score> nextScores = new ArrayList<>();
nextScores.addAll(scores);
nextScores.add(score);
outs.addAll(recursiveOutsFor(target, nextScores));
}
}
return outs;
}
}
I'm solving this BFS homework problem, I believe the logic I'm following is right, but I got stuck in an implementation error I can't pinpoint. I'm looking for help debugging this solution, not proposing a new one.
Problem Statement:
A kids has two robots that he controls remotely, both robots are on a NxN checkerboard and should be placed on the positions A and B in the checkerboard.
Both robots are affected by the remote controller simultaneously, the same command affects both of their states.
The remote controller can only make both robots turn clockwise or counterclockwise 90 degreees at a time or order both robots to move forward.
Example:
The leftmost image shows the initial setting. The arrow pointing right is a robot facing east and the arraw pointing up is a robot facing north. Positions A and B are the robots destinies.
Center image shows the result of moving both robots one step forward.
Right image shows the result of making the robots rotate counterclockwise.
The kid desires to calculate the minimum number of movements necessary to take the robots from their initial positions to their destinies.
If a robot is commanded to run over a wall, it will remain on the same spot.
Both robots will remain on their original spot if they're commanded to move to the same spot.
Figure 2 shows this special cases.
Both robots should at arrive at a different destiny simultaneously.
Input:
Input consists of various test cases, the first line starts with an integer with the size N of the inputMatrix (the checkerboard), with 2<= N <=25.
The following N lines describe the checkerboard and have N characters each.
A '.' indicates an empty position.
N, E, S or O (Spanish for Oeste=West) indicates the original positiona and orientation of the robot.
D indicates a destiny for the robot in the checkerboard and '*' indicates an obstacle.
Input finishes with a case where N=0.
input.txt
5
D....
N...S
.....
*...*
....D
5
.....
S..S.
.....
.....
D..D.
3
SN.
***
.DD
0
correct output for input.txt:
8
3
-1
input2.txt:
5
.....
..D.S
.D...
.....
..N..
6
......
..S...
......
.ED...
......
.D....
11
....E......
....D......
...........
..S...D....
...........
...........
...........
...........
...........
...........
...........
13
.............
.............
.............
.............
.....N.......
.............
.........D...
..D..........
.............
...E.........
.............
.............
.............
25
...*.......*.*...........
........*..D...*.**....*.
*..*.*.........*..*..*..D
...*.**.*........*...*...
......**..*..***.***...**
.............*...........
....*...***.....*.**.....
......**.......**.*.*...*
.........*..*...*.*......
....**.*.*....**.*.*.*.*.
.......*............**...
..........*.*.....*......
...........**....*.**....
.....E.*.*..........**.*.
.........*.*.*.*..*..*...
*........**...*..........
................***..*...
........*....*....*...*..
......*...*.*...*.....**.
...*..........*.**.......
.**............*.*..*.*..
........*........*...*...
*......*..........*......
*...*......N*......*..*.*
. .....*..*.*..*...*......
0
"correct" (?) output for input2.txt:
-1
-1
9
-1
46
My solution:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
class Position {
int i;
int j;
char orientation;
Position() {
}
void setIJ(int i, int j){
this.i=i;
this.j=j;
}
void setOrientation(char c){
orientation = c;
}
public boolean equals(Object o){
if(o instanceof Position){
Position p = (Position)o;
if((p.i==this.i)&&(p.j==this.j)&&(p.orientation==this.orientation))
{
return true;
}
else return false;
}
return false;
}
} //end class Position
class TransitionState {
Position positionA;
Position positionB;
int counter;
public boolean equals (Object o){
if (o instanceof TransitionState){
TransitionState transitionState= (TransitionState)o;
if ((this.positionA.equals(transitionState.positionA))
&&(this.positionB.equals(transitionState.positionB)))
{
return true;
}
}
return false;
}
}
public class Robots {
static Position moveForward(Position oldPosition, int matrixSize, char orientation, char [][] inputMatrix){
// add possible new Position
Position newPosition= new Position();
//first return oldPosition in border positions in which the robot shouldn't move
if ((orientation=='O')&&(oldPosition.j==0))
return oldPosition;
if ((orientation=='E')&&(oldPosition.j==(matrixSize-1)))
return oldPosition;
if ((orientation=='N')&&(oldPosition.i==0))
return oldPosition;
if ((orientation=='S')&&(oldPosition.i==(matrixSize-1)))
return oldPosition;
if ((orientation=='O'))
newPosition.setIJ(oldPosition.i, oldPosition.j-1);
if ((orientation=='E'))
newPosition.setIJ(oldPosition.i, oldPosition.j+1);
if ((orientation=='S'))
newPosition.setIJ(oldPosition.i-1, oldPosition.j);
if ((orientation=='N'))
newPosition.setIJ(oldPosition.i+1, oldPosition.j);
//return oldPosition for positions in which the robot is blocked by *
if (inputMatrix[newPosition.i][newPosition.j]=='*'){
return oldPosition;
}
return newPosition; // if it got here, all ok
}
static char turnCounter (char orientation){
if(orientation=='N')
return 'O';
if(orientation=='O')
return 'S';
if (orientation=='S')
return 'E';
else
return 'N';
}
static char turnClock(char orientation){
if(orientation=='N')
return 'E';
if(orientation=='E')
return 'S';
if (orientation=='S')
return 'O';
else
return 'N';
}
static Position[] robotInitialPositions(char [][]inputMatrix){
Position [] helperArray = new Position[2];
int aux=0;
for (int i=0; i<(inputMatrix[0].length); i++)
for (int j=0; j<(inputMatrix[0].length); j++)
{
if((inputMatrix[i][j]=='N')||(inputMatrix[i][j]=='S')||(inputMatrix[i][j]=='O')||(inputMatrix[i][j]=='E'))
{
helperArray[aux]= new Position();
helperArray[aux].setIJ(i, j);
if (inputMatrix[i][j]=='N'){helperArray[aux].orientation='N'; }
if (inputMatrix[i][j]=='S'){helperArray[aux].orientation='S'; }
if (inputMatrix[i][j]=='E'){helperArray[aux].orientation='E'; }
if (inputMatrix[i][j]=='O'){helperArray[aux].orientation='O'; }
aux= aux+1;
}
}
return helperArray;
}
static Position[] getDestinies(char [][]inputMatrix){
Position [] helperArray = new Position[2];
int aux=0;
for (int i=0; i<(inputMatrix[0].length); i++)
for (int j=0; j<(inputMatrix[0].length); j++)
{
if((inputMatrix[i][j]=='D'))
{
helperArray[aux]= new Position();
helperArray[aux].i=i; helperArray[aux].j=j;
helperArray[aux].orientation='D';
aux=aux+1;
}
}
return helperArray;
}
static boolean [][]getUnvisitedMatrix(int matrixLength){
boolean[][] unvisitedMatrix = new boolean [matrixLength][matrixLength];
for (int i=0; i<matrixLength;i++)
for (int j=0; j<matrixLength; j++)
unvisitedMatrix[i][j]=false;
return unvisitedMatrix;
}
static Position[]getNewRobotPositions (Position oldR1Pos,Position oldR2Pos, String movement, char [][]inputMatrix){
Position[]newPositions = new Position[2];
Position newR1Pos = new Position();
Position newR2Pos = new Position();
if(movement.equals("counter")){
if (oldR1Pos.orientation=='N'){
newR1Pos.orientation='O';
}
if (oldR1Pos.orientation=='S'){
newR1Pos.orientation='E';
}
if (oldR1Pos.orientation=='E'){
newR1Pos.orientation='N';
}
if (oldR1Pos.orientation=='O'){
newR1Pos.orientation='S';
}
if (oldR2Pos.orientation=='N'){
newR2Pos.orientation='O';
}
if (oldR2Pos.orientation=='S'){
newR2Pos.orientation='E';
}
if (oldR2Pos.orientation=='E'){
newR2Pos.orientation='N';
}
if (oldR2Pos.orientation=='O'){
newR2Pos.orientation='S';
}
newR1Pos.i=oldR1Pos.i;
newR1Pos.j=oldR1Pos.j;
newR2Pos.i=oldR2Pos.i;
newR2Pos.j=oldR2Pos.j;
newPositions[0]=newR1Pos;
newPositions[1]=newR2Pos;
// System.out.println("MOVED COUNTERCLOCKWISE");
// System.out.println("previous Robot 1 position was "+oldR1Pos.i + ","+oldR1Pos.j + " orientation was " + oldR1Pos.orientation +
// " new Robot 1 position is " + newR1Pos.i + "," + newR1Pos.j+ " orientation is "+newR1Pos.orientation);
//
// System.out.println("previous Robot 2 position was "+oldR2Pos.i + ","+oldR2Pos.j + " orientation was " + oldR2Pos.orientation +
// " new Robot 2 position is " + newR2Pos.i + "," + newR2Pos.j+ " orientation is "+newR2Pos.orientation);
return newPositions;
}
if(movement.equals("clock")){
newR1Pos.i = oldR1Pos.i;
newR1Pos.j = oldR1Pos.j;
newR2Pos.i = oldR2Pos.i;
newR2Pos.j = oldR2Pos.j;
if (oldR1Pos.orientation=='N'){
newR1Pos.orientation= 'E';
}
if (oldR1Pos.orientation=='S'){
newR1Pos.orientation= 'O';
}
if (oldR1Pos.orientation=='E'){
newR1Pos.orientation= 'S';
}
if (oldR1Pos.orientation=='O'){
newR1Pos.orientation= 'N';
}
if (oldR2Pos.orientation=='N'){
newR2Pos.orientation= 'E';
}
if (oldR2Pos.orientation=='S'){
newR2Pos.orientation= 'O';
}
if (oldR2Pos.orientation=='E'){
newR2Pos.orientation= 'O';
}
if (oldR2Pos.orientation=='O'){
newR2Pos.orientation= 'N';
}
// System.out.println("MOVED CLOCKWISE");
// System.out.println("previous Robot 1 position was "+oldR1Pos.i + ","+oldR1Pos.j + " orientation was " + oldR1Pos.orientation +
// " new Robot 1 position is " + newR1Pos.i + "," + newR1Pos.j+ " orientation is "+newR1Pos.orientation);
/ /
// System.out.println("previous Robot 2 position was "+oldR2Pos.i + ","+oldR2Pos.j + " orientation was " + oldR2Pos.orientation +
// " new Robot 2 position is " + newR2Pos.i + "," + newR2Pos.j+ " orientation is "+newR2Pos.orientation);
newPositions[0]=newR1Pos;
newPositions[1]=newR2Pos;
return newPositions;
}
if(movement.equals("forward")){
//default case, if conditions not satisfied
newR1Pos.i=oldR1Pos.i;
newR1Pos.j=oldR1Pos.j;
newR1Pos.orientation = oldR1Pos.orientation;
newR2Pos.i=oldR2Pos.i;
newR2Pos.j=oldR2Pos.j;
newR2Pos.orientation = oldR2Pos.orientation;
if(oldR1Pos.orientation=='N'){
if(oldR1Pos.i-1>=0){ //doesn't exceed the upper border
//doesn't collide with '*'
if (inputMatrix[oldR1Pos.i-1][oldR1Pos.j]!='*'){
newR1Pos.i=oldR1Pos.i-1;
newR1Pos.j=oldR1Pos.j;
newR1Pos.orientation = oldR1Pos.orientation;
}
}
}
if(oldR1Pos.orientation=='S'){
if(oldR1Pos.i+1<inputMatrix.length){ //doesn't exceed the lower border
//doesn't collide with '*'
if (inputMatrix[oldR1Pos.i+1][oldR1Pos.j]!='*'){
newR1Pos.i=oldR1Pos.i+1;
newR1Pos.j=oldR1Pos.j;
newR1Pos.orientation = oldR1Pos.orientation;
}
}
}
if(oldR1Pos.orientation=='E'){
if(oldR1Pos.j+1<inputMatrix.length){ //doesn't exceed the right border
//doesn't collide with '*'
if (inputMatrix[oldR1Pos.i][oldR1Pos.j+1]!='*'){
newR1Pos.i=oldR1Pos.i;
newR1Pos.j=oldR1Pos.j+1;
newR1Pos.orientation = oldR1Pos.orientation;
}
}
}
if(oldR1Pos.orientation=='O'){
if(oldR1Pos.j-1>=0){ //doesn't exceed the left border
//doesn't collide with '*'
if (inputMatrix[oldR1Pos.i][oldR1Pos.j-1]!='*'){
newR1Pos.i=oldR1Pos.i;
newR1Pos.j=oldR1Pos.j-1;
newR1Pos.orientation = oldR1Pos.orientation;
}
}
}
//same for robot 2
if(oldR2Pos.orientation=='N'){
if(oldR2Pos.i-1>=0){ //doesn't exceed the upper border
//doesn't collide with '*'
if (inputMatrix[oldR2Pos.i-1][oldR2Pos.j]!='*'){
newR2Pos.i=oldR2Pos.i-1;
newR2Pos.j=oldR2Pos.j;
newR2Pos.orientation=oldR2Pos.orientation;
}
}
}
if(oldR2Pos.orientation=='S'){
if(oldR2Pos.i+1<inputMatrix.length){ //doesn't exceed the lower border
//doesn't collide with '*'
if (inputMatrix[oldR2Pos.i+1][oldR2Pos.j]!='*'){
newR2Pos.i=oldR2Pos.i+1;
newR2Pos.j=oldR2Pos.j;
newR2Pos.orientation=oldR2Pos.orientation;
}
}
}
if(oldR2Pos.orientation=='E'){
if(oldR2Pos.j+1<inputMatrix.length){ //doesn't exceed the right border
//doesn't collide with '*'
if (inputMatrix[oldR2Pos.i][oldR2Pos.j+1]!='*'){
newR2Pos.i=oldR2Pos.i;
newR2Pos.j=oldR2Pos.j+1;
newR2Pos.orientation=oldR2Pos.orientation;
}
}
}
if(oldR2Pos.orientation=='O'){
if(oldR2Pos.j-1>=0){ //doesn't exceed the left border
//doesn't collide with '*'
if (inputMatrix[oldR2Pos.i][oldR2Pos.j-1]!='*'){
newR2Pos.i=oldR2Pos.i;
newR2Pos.j=oldR2Pos.j-1;
newR2Pos.orientation=oldR2Pos.orientation;
}
}
}
//if robots collide in new positions, revert to their original positions
if ((newR1Pos.i==newR2Pos.i) && (newR1Pos.j==newR2Pos.j)){
//revert robot 1 position
newR1Pos.i=oldR1Pos.i;
newR1Pos.j=oldR1Pos.j;
newR1Pos.orientation = oldR1Pos.orientation;
//revert robot 2 position
newR2Pos.i=oldR2Pos.i;
newR2Pos.j=oldR2Pos.j;
newR2Pos.orientation = oldR2Pos.orientation;
}
newPositions[0] = newR1Pos;
newPositions[1] = newR2Pos;
// System.out.println("MOVED FORWARD");
// System.out.println("previous Robot 1 position was "+oldR1Pos.i + ","+oldR1Pos.j + " orientation was " + oldR1Pos.orientation +
// " new Robot 1 position is " + newR1Pos.i + "," + newR1Pos.j+ " orientation is "+newR1Pos.orientation);
//
// System.out.println("previous Robot 2 position was "+oldR2Pos.i + ","+oldR2Pos.j + " orientation was " + oldR2Pos.orientation +
// " new Robot 2 position is " + newR2Pos.i + "," + newR2Pos.j+ " orientation is "+newR2Pos.orientation);
} //end movement.equals("forward")
return newPositions;
}
//1 procedure BFS(Graph,source):
//2 create a queue Q
//3 enqueue source onto Q
//4 mark source
//5 while Q is not empty:
//6 dequeue an item from Q into v
//7 for each edge e incident on v in Graph:
//8 let w be the other end of e
//9 if w is not marked:
//10 mark w
//11 enqueue w onto Q
static void BFS (char [][] inputMatrix){
ArrayList<TransitionState> transitionStatesArray = new ArrayList<TransitionState>();
int moveCounter=0; //turns and forward movements add here
int tempMoveCounterRobot1=0; int tempMoveCounterRobot2=0;
int maxMoveCounter=0;
PositionsAndCounter positionsAndCounter= new PositionsAndCounter();
Queue <PositionsAndCounter>queue = new LinkedList<PositionsAndCounter>();
Position robotInitial[] = robotInitialPositions(inputMatrix); //get source
positionsAndCounter.positionPair=robotInitial; //used to encapsulate the positions with a counter to output
positionsAndCounter.counter=0;//first initialize to 0
Position destinies[] = getDestinies(inputMatrix); //get destinies
TransitionState firstTransitionState = new TransitionState();
firstTransitionState.positionA=robotInitial[0];
firstTransitionState.positionB=robotInitial[1];
transitionStatesArray.add(firstTransitionState);
//mark transition used , if the transition is new, it should be queued
queue.add(positionsAndCounter);
String [] movement = {"forward", "counter", "clock"};
//possible movements inside inputMatrix
outer: while (!queue.isEmpty()){ //while queue is not empty
PositionsAndCounter v= queue.poll(); //dequeue an item from Q into V
for(int k = 0; k<3; k++){ //for each edge e incident on v in Graph:
Position[] newRobotPositions = getNewRobotPositions(v.positionPair[0], v.positionPair[1], movement[k], inputMatrix);
TransitionState newTransitionState = new TransitionState();
newTransitionState.positionA=newRobotPositions[0];
newTransitionState.positionB=newRobotPositions[1];
if(!transitionStatesArray.contains(newTransitionState)){ //if the transition state is new add and enqueue new robot positions
transitionStatesArray.add(newTransitionState);
//if transition is new then queue
PositionsAndCounter newPositionsAndCounter = new PositionsAndCounter();
newPositionsAndCounter.positionPair=newRobotPositions;
newPositionsAndCounter.counter = v.counter +1;
queue.add(newPositionsAndCounter);
}
inputMatrix[v.positionPair[0].i][v.positionPair[1].j]='.';
inputMatrix[v.positionPair[1].i][v.positionPair[1].j]='.';
//inputMatrix[v[0].i][v[0].j]='.'; // mark old position of robot 1 with .
//inputMatrix[v[1].i][v[1].j]='.'; // mark old position of robot 2 with .
//update new robot positions
inputMatrix[newRobotPositions[0].i][newRobotPositions[0].j]= newRobotPositions[0].orientation;
inputMatrix[newRobotPositions[1].i][newRobotPositions[1].j]= newRobotPositions[1].orientation;
//check if solution has been found
if
(
((destinies[0].i==newRobotPositions[0].i)&&(destinies[0].j==newRobotPositions[0].j) //robot in 0 arrived to destiny
|| (destinies[1].i==newRobotPositions[0].i)&&(destinies[1].j==newRobotPositions[0].j))// in 0 or 1
&& //and
((destinies[0].i==newRobotPositions[1].i)&&(destinies[0].j==newRobotPositions[1].j) //robot in 1 arrived to destiny
|| (destinies[1].i==newRobotPositions[0].i)&&(destinies[1].j==newRobotPositions[0].j))//in 0 or 1
) //end if
{
System.out.println("robots arrived at destinies");
System.out.println("robot 1, starting at " + robotInitial[0].i + "," + robotInitial[0].j
+ " is in " + newRobotPositions[0].i + ","+ newRobotPositions[0].j);
System.out.println("robot 2, starting at " + robotInitial[1].i + "," + robotInitial[1].j
+ " is in " + newRobotPositions[1].i + ","+ newRobotPositions[1].j);
System.out.println("movements: " + (v.counter));
return;
//break outer;
}
}
}
System.out.println("robots can never arrive at their destinies");
System.out.println(-1);
}
static void printInputMatrix(char [][] inputMatrix){
for (int i=0; i<inputMatrix[0].length;i++)
for(int j=0; j<inputMatrix[0].length;j++)
{
System.out.print(" "+inputMatrix[i][j]+" ");
if(j==inputMatrix[0].length-1){System.out.println("");}
}
}
public static void main(String[] args) throws IOException {
// System.out.println("Test transition checker");
// Position p1 = new Position();
// p1.i=1;
// p1.j=1;
// p1.orientation='N';
// Position p2 = new Position();
// p2.i=1;
// p2.j=2;
// p2.orientation='N';
// Position p3 = new Position();
// p3.i=1;
// p3.j=1;
// p3.orientation='N';
// Position p4 = new Position();
// p4.i=1;
// p4.j=1;
// p4.orientation='N';
//
// TransitionState transitionChecker1 = new TransitionState();
// transitionChecker1.positionA=p1;
// transitionChecker1.positionB=p2;
//
// TransitionState transitionChecker2 = new TransitionState();
// transitionChecker2.positionA=p1;
// transitionChecker2.positionB=p2;
//
//
// ArrayList<TransitionState> arrayTransitions = new ArrayList<TransitionState>();
// arrayTransitions.add(transitionChecker1);
// System.out.println("Test contains? " + arrayTransitions.contains(transitionChecker2));
BufferedReader br = new BufferedReader(new FileReader(new File("input.txt")));
char [][] inputMatrix;
String line;
char [] lineAsCharArray;
int matrixSize;
while(true){
line = br.readLine();
matrixSize=Integer.parseInt(line);
inputMatrix = new char [matrixSize][matrixSize];
if (matrixSize==0){ // end outer looping
break;
}
else { //begin inner looping
for (int i=0; i<matrixSize; i++){
line = br.readLine();
inputMatrix[i] =line.toCharArray();
}
//matrix loaded
BFS(inputMatrix);
}
}
}
}
class PositionsAndCounter {
Position[] positionPair;
int counter;
PositionsAndCounter() {
positionPair = new Position[2];
counter=0;
}
}
Problems:
1) On the first input.txt file, it finds 9 movements to find the solution of the first course (when they should be 8) and 6 to find the solution of the second course (when it should be 3) though it correctly prints out -1 for the last (impossible) course configuration.
2) On the second input.txt file, professor says it should print -1 and -1 for the to first courses, though my program finds a plaussible solution for the second case and a bizarre one for the first (this is where I think a more experienced debugger could help, I'm at a loss tracking the reason for the displaced destiny output on the first case). Are the outputs proposed by my professor right? My algorithm is also getting stuck on that case where 46 should be printed.
The are 2 careless copy and paste problems causes the code not working,
1, in the clockwise turning part:
if (oldR2Pos.orientation == 'E') {
newR2Pos.orientation = 'O';
}
This is wrong... it should be a direct copy and paste from the above block
if (oldR2Pos.orientation == 'E') {
newR2Pos.orientation = 'S';
}
Yet you missed it.
Another problem is actually in the end condition testing block
//check if solution has been found
if
(
((destinies[0].i==newRobotPositions[0].i)&&(destinies[0].j==newRobotPositions[0].j) //robot in 0 arrived to destiny
|| (destinies[1].i==newRobotPositions[0].i)&&(destinies[1].j==newRobotPositions[0].j))// in 0 or 1
&& //and
((destinies[0].i==newRobotPositions[1].i)&&(destinies[0].j==newRobotPositions[1].j) //robot in 1 arrived to destiny
|| **(destinies[1].i==newRobotPositions[0].i)&&(destinies[1].j==newRobotPositions[0].j)**)//in 0 or 1
) //end if
The last part (code highlighted) should be
(destinies[1].i==newRobotPositions[1].i)&&(destinies[1].j==newRobotPositions[1].j)
It is obviously an copy and paste but forget to change error. The logic is a little bit hard to understand, but works,
(A in X or B in Y) and (A in Y or B in X)
Although it is the same (logically not exactly the same but it some how works for your case as A and B cannot occupy the same location), it is much clearer if you use
(A in X and B in Y) or (A in Y and B in X)
Apart from the fatal errors stated above, your program has a few other issues need to addressed.It looks like you are a university student taking Computer science, please, read the given source code before coding: TransistionState class is not used at all but you created your own PositionsAndCounter, turning logic is implemented twice, if you didn't rewrite the turning code, and use the one given, you won't commit problem 1.... If I were your professor, i may fail you on that. Plan your solution well before hitting the keyboard, and make sure your code is clear and readable as plan english, if you stare at your source code for 5 min and cannot figure out what the block of code is for, may be you didn't structure it correctly.
The listing below is an example solution for your question:
import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class DualRobot {
public enum Orientation{
E(1, 0), S(0, 1), O(-1, 0), N(0, -1);
public final int dx, dy;
private Orientation(int dx, int dy){
this.dx = dx;
this.dy = dy;
}
public Orientation left(){
return Orientation.values()[(this.ordinal() + 3) % 4];
}
public Orientation right(){
return Orientation.values()[(this.ordinal() + 1) % 4];
}
public static Orientation valueOf(char c){
for(Orientation o : Orientation.values()){
if(o.toString().equalsIgnoreCase("" + c)) return o;
}
return null;
}
}
public enum Action {FORWARD, COUNTER_CLOCKWISE, CLOCKWISE}; // F: forward, L: Counter clockwise, R: clockwise
private static class Robot{
Point position;
Orientation orientation;
public Robot(Robot r){
this.position = new Point(r.position);
this.orientation = r.orientation;
}
public Robot(int x, int y, Orientation orientation){
this.position = new Point(x, y);
this.orientation = orientation;
}
public void move(Action action, char[][] map){
switch (action) {
case FORWARD:
Point nextPosition = new Point(position);
nextPosition.translate(orientation.dx, orientation.dy);
if(isValidPosition(nextPosition, map)) position = nextPosition;
break;
case COUNTER_CLOCKWISE:
this.orientation = this.orientation.left();
break;
case CLOCKWISE:
this.orientation = this.orientation.right();
break;
}
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Robot) {
Robot r = (Robot) obj;
return r.position.equals(this.position) && r.orientation == this.orientation;
}
return super.equals(obj);
}
#Override
public int hashCode() {
return orientation.ordinal() + position.x * 10 + position.y * 1000;
}
private boolean isValidPosition(Point p, char[][] map){
return p.x >= 0 && p.x < map[0].length
&& p.y >= 0 && p.y < map.length
&& map[p.y][p.x] != '*';
}
}
private static class State{
private Robot a, b;
private int counter;
public State(Robot a, Robot b, int counter) {
this.a = new Robot(a);
this.b = new Robot(b);
this.counter = counter;
}
public List<State> nextStates(char[][] map){
List<State> states = new ArrayList<State>();
for(Action action : Action.values()){
State s = new State(this.a, this.b, this.counter + 1);
s.a.move(action, map);
s.b.move(action, map);
if(!s.a.position.equals(s.b.position)){ // Test for collision
states.add(s);
}
}
return states;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof State) {
State state = (State) obj; // Consider the state to be the same if the 2 robots are at identical location and orientation
return (this.a.equals(state.a) && this.b.equals(state.b))
|| (this.a.equals(state.b) && this.b.equals(state.a));
}
return super.equals(obj);
}
#Override
public int hashCode() {
// The quality of this hashCode can affect the program's speed
// Multiply is transitive, so if you swap a and b, the hashcode is the same
return a.hashCode() * b.hashCode();
}
}
public static void main(String[] args) throws IOException {
BufferedReader input = new BufferedReader(new FileReader("input.txt"));
int size;
while((size = Integer.parseInt(input.readLine())) > 0){
// Load the data;
char[][] map = new char[size][size];
for (int i = 0; i < size; i++) {
map[i] = input.readLine().toCharArray();
}
// Construct initial state
List<Robot> robots = new ArrayList<Robot>();
List<Point> destinations = new ArrayList<Point>();
for(int i = 0; i < size; i ++){
for(int j = 0; j < size; j ++){
Orientation orientation = Orientation.valueOf(map[i][j]);
if(orientation != null){
robots.add(new Robot(j, i, orientation));
}else if(map[i][j] == 'D'){
destinations.add(new Point(j, i));
}
}
}
System.out.println(BFSSearch(map, new State(robots.get(0), robots.get(1), 0), destinations));
}
}
private static int BFSSearch(char[][] map, State initialState, List<Point> destinations) throws IOException{
List<State> queue = new LinkedList<State>(); // Array list is slightly more efficient
queue.add(initialState); // Initial state
Map<State, Boolean> testedStates = new HashMap<State, Boolean>();
while(queue.size() > 0){
State currentState = queue.remove(0);
if(testedStates.containsKey(currentState)) continue;
// Testing for end condition
if((currentState.a.position.equals(destinations.get(0)) && currentState.b.position.equals(destinations.get(1)))
|| (currentState.a.position.equals(destinations.get(1)) && currentState.b.position.equals(destinations.get(0)))){
return currentState.counter;
}
testedStates.put(currentState, true);
queue.addAll(currentState.nextStates(map));
}
return -1;
}
}
This program spit out the final answer in around 10 seconds.
The main difference is I used an hash table to store the tested states to improve the speed, thus the quality of the hash function would have an effect on the speed.
Recommended reading: hash table, DRY principle.