I'm making a small game for a school project. In my game I have 9 cards on a 3x3 field, every card has 3 properties: count, shape and color. You have a set when 3 cards share the same or different properties, so when all colors are the same or different and all three shapes are the same or different and all the colors are the same or different.
For example:
1 red triangle, 1 red rectangle, 1 red ellipse.
1 blue triangle, 2 green triangle
The 3 priorities have to be the same of have to be different. As you see here above the first card has the same count, different shapes and the same color.
Now I have to make a function that will check how many sets there are on the table. I have a function that will check if 3 cards are a set, but I'm not able to make a function that will check all the possible combinations of 3 cards on the field. The cards on the field are saved in a 2 dimensional array.
My program is in Dutch:
setsOpTafel == sets on the table
kaarten == cards
aantalSets == number of sets
http://pastie.org/private/zqkfhqew1q8kwqvi6cvm0w
Can somebody help please?
edit:
How to function has to work.
so first of i have to check the combo of 3 cards -> card 1, card 2, card3.
Then card 1, card 2, card 4. and so forth till it has checked all the different combination of 3 different cards.
Here is simple solution. I dont know whether it is like you intended or not.
Run Code
class GAME {
static class Card {
public enum Shape {
square, triangle, circle
}
public enum Color {
red, blue, green
}
public int count;
public Shape shape;
public Color color;
public int gencode(boolean difcount, boolean difcolor, boolean difshape) {
int gen = -1;
if (difcount == true || difcolor == true || difshape == true) {
gen = 0;
}
if (difcount == true) {
gen = count+1;//for count=0
}
if (difcolor == true && shape != null) {
gen += 1000 * (shape.ordinal() + 1);
}
if (difshape == true && color != null) {
gen += 100000 * (color.ordinal() + 1);
}
return gen;
}
public String toString() {
String s = "Card::properties " + count;
if (color != null) {
switch (color) {
case red:
s += " red";
break;
case blue:
s += " blue";
break;
case green:
s += " green";
break;
default:
}
}
if (shape != null) {
switch (shape) {
case square:
s += " square";
break;
case triangle:
s += " triangle";
break;
case circle:
s += " circle";
break;
default:
}
}
return s;
}
}
public static void add(HashMap<Integer, ArrayList<GAME.Card>> cardmap, GAME.Card card, int key) {
ArrayList<GAME.Card> cardlist = cardmap.get(key);
if (cardlist == null) {
cardlist = new ArrayList<Card>();
cardlist.add(card);
cardmap.put(key, cardlist);
return;
}
cardlist.add(card);
return;
}
public static void showGroups( HashMap<Integer, ArrayList<GAME.Card>> cardmap){
for (List<GAME.Card> value : cardmap.values()) {
if(value.size()<=1) continue;
System.out.println("---------------------------------------------------");
int ii = 0;
for (GAME.Card p : value) {
ii += 1;
System.out.println("" + ii + ") "+p);
}
}
}
public static void main(String[] args) {
HashMap<Integer, ArrayList<GAME.Card>> cardmap = new HashMap< >();
GAME.Card[] allcards = new Card[9];
//init here (simple test
int lennn = GAME.Card.Shape.values().length;
int lennm = GAME.Card.Color.values().length;
Random randomGenerator = new Random();
for (int i = 0; i < allcards.length; i++) {
int ppp=randomGenerator.nextInt(9);
int kkk=randomGenerator.nextInt(2456);
allcards[i] = new Card();
allcards[i].count = ppp ;
allcards[i].shape = GAME.Card.Shape.values()[ppp % lennn];
allcards[i].color = GAME.Card.Color.values()[kkk % lennm];
}
System.out.println("######## Generated random cards");
int ii = 0;
for (GAME.Card p : allcards) {
ii += 1;
System.out.println("" + ii + ") "+p);
}
System.out.println("\n########Group at least by one property");
//group Cards by properties and its values
for (int i = 0; i < allcards.length; i++) {
//check one property
add(cardmap, allcards[i], allcards[i].gencode(true, false, false));
add(cardmap, allcards[i], allcards[i].gencode(false, true, false));
add(cardmap, allcards[i], allcards[i].gencode(false, false, true));
}
//show groups
showGroups(cardmap);
//check two
System.out.println("\n########Group at least by two properties");
cardmap = new HashMap< >();
//group Cards by properties and its values
for (int i = 0; i < allcards.length; i++) {
//check two properties
add(cardmap, allcards[i], allcards[i].gencode(true, true, false));
add(cardmap, allcards[i], allcards[i].gencode(true, false, true));
add(cardmap, allcards[i], allcards[i].gencode(false, true, true));
}
//show groups
showGroups(cardmap);
//check all
System.out.println("\n########Group by all properties");
cardmap = new HashMap< >();
//group Cards by properties and its values
for (int i = 0; i < allcards.length; i++) {
//check all properties
add(cardmap, allcards[i], allcards[i].gencode(true, true, true));
}
//show groups
showGroups(cardmap);
}
}
Related
I am following a tutorial which partially deals with printing the elements of ArrayLists. The program runs exactly as I'd expect when dealing with small lists. However the string formatting ( I believe ) causes some strange results when larger numbers are input.
My code is as follows:
public class Theatre {
private final String theatreName;
public List<Seat> seats = new ArrayList<>();
public Theatre(String theatreName, int numRows, int seatsPerRow) {
this.theatreName = theatreName;
int lastRow = 'A' + (numRows -1);
for (char row = 'A'; row <= lastRow; row++) {
for(int seatNum = 1; seatNum <= seatsPerRow; seatNum++) {
Seat seat = new Seat(row + String.format("%02d", seatNum));
seats.add(seat);
}
}
}
public String getTheatreName() {
return theatreName;
}
public boolean reserveSeat(String seatNumber) {
int low = 0;
int high = seats.size()-1;
while(low <= high) {
System.out.print(".");
int mid = (low + high) /2;
Seat midVal = seats.get(mid);
int cmp = midVal.getSeatNumber().compareTo(seatNumber);
if(cmp <0) {
low = mid + 1;
} else if(cmp > 0) {
high = mid -1;
} else {
return seats.get(mid).reserve();
}
}
System.out.println("There is no seat " + seatNumber);
return false;
}
// for testing
public void getSeats() {
for(Seat seat : seats) {
System.out.println(seat.getSeatNumber());
}
}
public class Seat implements Comparable<Seat > {
private final String seatNumber;
private boolean reserved = false;
public Seat(String seatNumber) {
this.seatNumber = seatNumber;
}
public boolean reserve() {
if(!this.reserved) {
this.reserved = true;
System.out.println("Seat " + seatNumber + " reserved");
return true;
} else {
return false;
}
}
public boolean cancel() {
if(this.reserved) {
this.reserved = false;
System.out.println("Reservation of seat " + seatNumber + " cancelled");
return true;
} else {
return false;
}
}
public String getSeatNumber() {
return seatNumber;
}
#Override
public int compareTo(Seat seat) {
// returns integer greater than 0 if greater than, less than if less than, 0 if equal
return this.seatNumber.compareTo(seat.getSeatNumber());
}
}
With a Main method class:
public static void main(String[] args) {
Theatre theatre = new Theatre("Olympian", 800, 12);
List<Theatre.Seat> seatCopy = new ArrayList<>(theatre.seats); // shallow copy, contains references to all
// elements of both lists, original and copy
printList(seatCopy);
seatCopy.get(1).reserve();
if (theatre.reserveSeat("A02")) {
System.out.println("Please pay for A02");
} else {
System.out.println("seat already reserved");
}
// see that they are clearly two separate array lists
Collections.reverse(seatCopy);
System.out.println("Printing seat copy");
printList(seatCopy);
System.out.println("Printing theatre.seats");
printList(theatre.seats);
System.out.println("Shuffling seatCopy");
Collections.shuffle(seatCopy);
printList(seatCopy);
}
public static void printList(List<Theatre.Seat> list) {
for (Theatre.Seat seat : list) {
System.out.print(" " + seat.getSeatNumber());
}
System.out.println();
System.out.println("===============================");
}
}
The output (I only quote enough to see ) is:
12 ͠11 ͠10 ͠09 ͠08 ͠07 ͠06 ͠05 ͠04 ͠03 ͠02 ͠01 ͟12 ͟
===============================
Printing theatre.seats
A01 A02 A03 A04 A05 A06 A07 A08 A09 A10
===============================
===============================
Shuffling seatCopy
V07 Ý11 11 ű05 Ú02 ̄06 ̓01 ŕ12 ȣ03 Ǔ05 S07
I am aware that I run out of alphabetical characters and that the formatting in this line:
Seat seat = new Seat(row + String.format("%02d", seatNum));
is intended only to deal with seats of the format "X##".
What I want to understand is specificallty why the odd characters appear ( the "~" and "'", etc. ). Obviously, the formatting is inappropriate. But why does it produce specifically this output?
Thank you for your help,
Marc
You said it yourself. You're running out of alphabetical characters. In fact, you're running out of ASCII characters altogether. From this line:
for (char row = 'A'; row <= lastRow; row++)
What you are doing is starting the row letters from 'A' and continuing across the Unicode character set. So, with more than 26 rows, you start getting symbols like ~, and with enough rows, you leave ASCII altogether and start getting weird row letters like Ý.
If you don't want this to happen, you'll need to ditch the for loop and come up with an entirely different (and more complex) way of assigning row labels.
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
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.
im working on a card pair game that generates two random value from an array of strings.. what i want to know is how to get the sum of two random values from the array of strings to determine the winner. here are the codes
import java.util.*;
public class Cards {
private String suit;
private String face;
private String[] cardSuits;
private String[] cardFaces;
private Random ran;
public Cards() {
ran = new Random();
cardSuits = new String[] { "of Spade", "of Hearts", "of Diamonds",
"of Clubs" };
cardFaces = new String[] { "Ace", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "Jack", "Queen", "King" };
}
public String setPlayerCardSuit() {
suit = cardSuits[ran.nextInt(4)];
return suit;
}
public String setPlayerCardFace() {
face = cardFaces[ran.nextInt(13)];
return face;
}
public String setPlayerCardSuit2() {
suit = cardSuits[ran.nextInt(4)];
return suit;
}
public String setPlayerCardFace2() {
face = cardFaces[ran.nextInt(13)];
return face;
}
public String setCompCardSuit() {
suit = cardSuits[ran.nextInt(4)];
return suit;
}
public String setCompCardFace() {
face = cardFaces[ran.nextInt(13)];
return face;
}
public String setCompCardSuit2() {
suit = cardSuits[ran.nextInt(4)];
return suit;
}
public String setCompCardFace2() {
face = cardFaces[ran.nextInt(13)];
return face;
}
public void getResults() {
System.out.println("Here are your cards: " + setPlayerCardFace() + " "
+ setPlayerCardSuit() + " and " + setPlayerCardFace2() + " "
+ setPlayerCardSuit2());
}
public void getCompCard() {
System.out.println("Here's the computer's cards: " + setCompCardFace()
+ " " + setCompCardSuit() + " and " + setCompCardFace2() + " "
+ setCompCardSuit2());
}
}
and here is the code to test the Cards Class:
import javax.swing.JOptionPane;
public class TestCards {
public static void main(String[] args) {
Cards playerCards = new Cards();
Cards computerCards = new Cards();
int confirm, x = 1;
while (x == 1) {
JOptionPane.showMessageDialog(null,
"Random Card game \nPlease press OK to Start Game",
"Card Pair Game", JOptionPane.INFORMATION_MESSAGE);
JOptionPane.showMessageDialog(
null,
"Here are your Cards: " + playerCards.setPlayerCardFace()
+ " " + playerCards.setPlayerCardSuit() + " and "
+ playerCards.setPlayerCardFace2() + " "
+ playerCards.setPlayerCardSuit2()
+ "\nThe Computer's Cards are: "
+ computerCards.setCompCardFace() + " "
+ computerCards.setCompCardSuit() + " and "
+ computerCards.setCompCardFace2() + " "
+ computerCards.setCompCardSuit2());
confirm = JOptionPane.showConfirmDialog(null, "Game Ends. Again?",
"Game Over", JOptionPane.YES_NO_OPTION);
if (confirm != JOptionPane.YES_OPTION) {
x = 2;
}
}
}
}
what lacks now is the code to determine the winner.
PS: im a beginner in java programming.. so please bear with me if u see an unusual use of codes :)
i have tried Dylan's suggestion but i can't seem to make it work.. instead used his idea and added this code to Cards class.
public int playerValues(){
int temp = 0;
if(face != cardFaces[0] && face != cardFaces[10] && face != cardFaces[11] && face != cardFaces[12]){
temp = Integer.parseInt(face);
}else if(face == cardFaces[0]){
temp = 1;
}else if(face == cardFaces[10]){
temp = 11;
}else if(face == cardFaces[11]){
temp = 12;
}else if(face == cardFaces[12]){
temp = 13;
}
return temp;
}
public int computerValues(){
int temp = 0;
if(face != cardFaces[0] && face != cardFaces[10] && face != cardFaces[11] && face != cardFaces[12]){
temp = Integer.parseInt(face);
}else if(face == cardFaces[0]){
temp = 1;
}else if(face == cardFaces[10]){
temp = 11;
}else if(face == cardFaces[11]){
temp = 12;
}else if(face == cardFaces[12]){
temp = 13;
}
return temp;
}
You have a problem that not all the cards are unique. Two players can have the same cards. They could all be the same card even.
Instead you should generate a list of all the possible cards. I suggest using a Card class. Use Collections.shuffle to shuffle them. This way all the players will have different cards.
You need rules for comparing a set of cards. Define them in English first, and then translate them into code.
You shouldn't use an array of Strings to hold the card faces, but an array of CardFace, having a name (for display), and a value (to compute the sum of the faces). And since there are only 13 values, it should be an enum:
public enum CardFace {
ACE("Ace, 1),
TWO("2", 2),
...
KINK("King", 13);
private String face;
private int value;
private CardFace(String face, int value) {
this.face = face;
this.value = value;
}
public String getFace() {
return this.face;
}
public int getValue() {
return this.value;
}
}
I would really suggest that your Card be a class or at least an enum. Then summing them will make sense.
Also, please explain these two
public String setCompCardFace()
public String setCompCardFace2() //identical
Pretty much what you need is for each player to have an instance of Cards much like you started
public class Cards extends ArrayList<Card>
but somewhere along the way you got lost and mixed up these two into one class that does nothing really.
so in your Card you put these
public class Card
{
private String suit;
private String face;
public Card(String face, String color)
{
suit = color; this.face = face;
}
#Override
public int compareTo(Card otherCard); //implement this yourself
}
Using the String.parseInt() method will return the number within a string. But, youll have issues with the ace,kind,queen,jack since they arent a number. i would Recommend creating a statement like:
if(!cardFaces[x].equals("Ace")&&!cardFaces[x].equals("Queen")&&!cardFaces[x].equals("King")&&!cardFaces[x].equals("Jack"))
{
int temp = cardFaces[x].parseInt();
}
else if(cardFaces[x].equals("Ace")
{
int temp = 1;
}
else if(cardFaces[x].equals("King") || cardFaces[x].equals("Queen") || cardFaces[x].equals("Jack"))
{
int temp = 10;
It seems that no one has talked about getting combinations, so I'll talk about it here. If there's anything you don't understand, feel free to comment here.
Straight flush - you need a for loop.
First, you should sort an array of the player's cards with ascending (1, 2, 3, 4, 5 etc.). Now, check from the first entry, the lowest.
Note that the array cards stores only the values of the hand in integer.
// Variables to store the current and previous cards.
int previous = 0;
int current = 0;
cards = Arrays.sort(cards); // Sort cards into ascending order
boolean straightFlush = false;
for (i = 0; i < cards.length; i++) {
current = cards[i];
if (!current == cards[0]) { // If current is not the first card, execute the rest. You don't want random errors popping up, do you? :)
// Check if it is the last card - a straight flush would then be present
if (current == cards[cards.length - 1]) {
straightFlush = true;
break;
}
// Checks if current - 1 != previous or the current card is not consecutive to the previous card
if (current - 1 != previous) {
break;
}
}
...
How to compare the hands then? Find the highest value among the cards if it is a straight flush.
I'll post more about this soon.
I have managed to determine the winner by modifying the TestCard class instead of showing two random values from the array of strings. I changed it so that it shows only one value from the array. With that I managed to determine who the winner is.
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.