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;
}
}
Related
In the CarWash program that I have right now there is currently a normal queue that I would like to change into a priority queue. My goal is to take one of the basic server characteristics and use that for priority but I am lost on how to do that. In previous attempts I have tried to change the normal queue into a priority queue and have ran into issues on how I am supposed to base it off a server characteristic.
public class CarWash {
public static void main(String[ ] args) {
Scanner kb = new Scanner (System.in);
System.out.println("Enter wash time: ");
int WASHTIME = kb.nextInt();
System.out.println("Enter arrival probability: ");
double ARRIVALPROB = kb.nextDouble();
System.out.println("enter time for simulation: ");
int TOTALTIME = kb.nextInt();
carWashSimulate(WASHTIME, ARRIVALPROB, TOTALTIME);
}
public static void carWashSimulate(int washTime, double arrivalProb, int totalTime) { //simulates the car wash
Queue<Integer> arrivalTimes = new LinkedList<Integer>( );
int next;
ClientGenerator arrival = new ClientGenerator(arrivalProb);
Server machine = new Server(washTime);
ExpressServer newM = new ExpressServer(washTime);
Averager waitTimes = new Averager( );
Averager lostCustomer = new Averager();
int currentSecond;
// Write the parameters to System.out.
System.out.println("Seconds to wash one car: " + washTime);
System.out.print("Probability of customer arrival during a second: ");
System.out.println(arrivalProb);
System.out.println("Total simulation seconds: " + totalTime);
// Check the precondition:
if (washTime <= 0 || arrivalProb < 0 || arrivalProb > 1 || totalTime < 0)
throw new IllegalArgumentException("Values out of range");
for (currentSecond = 0; currentSecond < totalTime; currentSecond++) {
// Simulate the passage of one second of time
// Check whether a new customer has arrived.
if (arrival.query( )){
System.out.println("Customer arrived at " + currentSecond);
if(arrivalTimes.size() <= 8){
arrivalTimes.add(currentSecond);
}
else{
System.out.println("They left, line was too long");
lostCustomer.addNumber(1);
}
// Check whether we can start washing another car.
if ((!machine.isBusy( )) && (!arrivalTimes.isEmpty( )))
{
next = arrivalTimes.remove( );
waitTimes.addNumber(currentSecond - next);
machine.start( );
System.out.println("Server started at " + currentSecond + " serving customer " + next);
}
// Subtract one second from the remaining time in the current wash cycle.
machine.reduceRemainingTime( );
} // end of for loop
// Write the summary information about the simulation.
System.out.println("Customers served: " + waitTimes.howManyNumbers( ));
if (waitTimes.howManyNumbers( ) > 0)
System.out.println("Average wait for customers served: " + waitTimes.average( ) + " sec");
System.out.println("The number of customers lost was " + lostCustomer);
}
}
}
Client Generator Class:
public class ClientGenerator {
private double probability;
// The approximate probability of query( ) returning true.
public ClientGenerator(double p) {
if ((p < 0) || (1 < p))
throw new IllegalArgumentException("Illegal p: " + p);
probability = p;
}
public void adjust(double a) {
if(a > 0 && a+probability < 1) {
probability = probability + a;
} else if (a < 0 && probability + a > 0) {
probability = probability + a;
}
}
public double getProbability() {
return probability;
}
public boolean query( ) {
return (Math.random( ) < probability);
}
}
Server Class:
public class Server {
private int secondsForService; // Seconds for a single wash
private int timeLeft; // Seconds until this Server is no longer busy
public Server(int s) {
secondsForService = s;
timeLeft =0;
}
public boolean isBusy( ) {
return (timeLeft > 0);
}
public void reduceRemainingTime( ) {
if (timeLeft > 0) timeLeft--;
}
public void start( ) {
if (timeLeft > 0)
throw new IllegalStateException("Server is already busy.");
timeLeft = secondsForService;
}
}
Averager class:
public class Averager
{
private int count; // How many numbers have been given to this averager
private double sum; // Sum of all the numbers given to this averager
public Averager( )
{
count =0;
sum = 0;
}
public void addNumber(double value)
{
if (count == Integer.MAX_VALUE)
throw new IllegalStateException("Too many numbers");
count++;
sum += value;
}
public double average( )
{
if (count == 0)
return Double.NaN;
else
return sum/count;
}
public int howManyNumbers( )
{
return count;
}
}
The question seems to be about how to configure the priority rules employed by a java.util.PriorityQueue. That's relatively straightforward. Depending on which constructor you use to instantiate one, PriorityQueue relies either on the natural order of its elements (see Comparable) or on the order defined by a specified Comparator. Whenever such a queue contains any elements, its head is the least with respect to the operative ordering, or among the least if there are multiple elements such that no other element is less.
In comments you clarified
my goal is to implement some way of randomly assigning a value that represents the type of car, which will then prioritize the luxury car before the other cars.
Note well that PriorityQueue uses the properties of the enqueued objects to establish their relative order. Right now you are enqueuing integer arrival times, which don't confer an ability to distinguish between classes of car. If you want to carry more information about each vehicle that arrives then you would probably want to create a new class for that, maybe something like this:
class ClientArrival {
enum Category { NORMAL, LUXURY }
Category category;
int arrivalTime;
// ...
}
You would then be able to create one or more implementations of Comparator<ClientArrival> to use to define the priority rule for a PriorityQueue<ClientArrival>. For example,
class LuxuryFirstComparator implements Comparator<ClientArrival> {
int compare(ClientArrival o1, ClientArrival o2) {
if (o1.getCategory() == o2.getCategory()) {
// ... order based on arrival time ...
} else if (o1.getCategory() == ClientArrival.Category.LUXURY) {
return -1;
} else {
return 1;
}
}
}
One might set up a PriorityQueue<ClientArrival> using that to determine priority via
Queue<ClientArrival> arrivals = new PriorityQueue<>(new LuxuryFirstComparator());
so I am having trouble figuring out why my test in JUnit is failing. I have a Bill class, a Money class, and a Date class. A new Bill object is being created in the test and the line
assertTrue( myBill.getAmount().getCents() == 0);
is failing. So I am aware of where it is happening but I'm not exactly sure how to fix it. I have tried changing my mutator methods to things like
return new Date(dueDate);
instead of just
return dueDate;
but it is still failing in JUnit. Please help!
Test code:
#Test
public void testBillConstructorPrivacyLeak()
{
Date date1 = new Date( 1, 1, 2020);
Money money1 = new Money( 10);
Bill myBill = new Bill( money1, date1, "sam");
date1.setYear( 2021);
money1.setMoney( 5, 10);
//Now get values and make sure they have not changed
assertTrue( myBill.getAmount().getCents() == 0);
assertTrue( myBill.getDueDate().getYear() == 2020);
}
My classes:
public class Bill
{
private Money amount;
private Date dueDate;
private Date paidDate;
private String originator;
//paidDate set to null
public Bill (Money amount, Date dueDate, String originator) {
this.amount = amount;
this.dueDate = dueDate;
this.originator = originator;
paidDate = null;
}
//copy constructor
public Bill (Bill toCopy) {
this.amount = toCopy.amount;
this.dueDate = toCopy.dueDate;
this.paidDate = toCopy.paidDate;
this.originator = toCopy.originator;
}
public Money getAmount () {
return new Money(amount);
}
public Date getDueDate () {
return new Date(dueDate);
}
public String getOriginator () {
return originator;
}
//returns true if bill is paid, else false
public boolean isPaid () {
return (paidDate != null);
}
//if datePaid is after the dueDate, the call does not update anything and returns false.
//Else updates the paidDate and returns true
//If already paid, we will attempt to change the paid date.
public boolean setPaid (Date datePaid) {
if (datePaid.isAfter(dueDate)) {
return false;
}
else {
paidDate = new Date(datePaid);
return true;
}
}
//Resets the due date – If the bill is already paid, this call fails and returns false.
//Else it resets the due date and returns true.
public boolean setDueDate (Date newDueDate) {
if (isPaid()) {
return false;
}
else {
dueDate = new Date(newDueDate);
return true;
}
}
//Change the amount owed.
//If already paid returns false and does not change the amount owed else changes
//the amount and returns true.
public boolean setAmount (Money amount) {
if (isPaid()) {
return false;
}
else {
amount = new Money(amount);
return true;
}
}
public void setOriginator (String originator) {
this.originator = originator;
}
//Build a string that reports the amount, when due, to whom, if paid, and if paid
//the date paid
public String toString () {
return "Amount: " + amount + " Due date: " + dueDate + " To: " + "originator" + " Paid?" + isPaid() + "Paid date: " + paidDate;
}
//Equality is defined as each field having the same value.
public boolean equals (Object toCompare) {
if (toCompare instanceof Bill) {
Bill that = (Bill) toCompare;
return this.amount.equals(that.amount) &&
this.dueDate.equals(that.dueDate) &&
this.paidDate.equals(that.paidDate) &&
this.originator.equals(that.originator);
}
return false;
}
}
public class Money
{
private int dollars;
private int cents;
//Constructor which sets the dollar amount, and sets cents to 0
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException
public Money (int dol) {
if (dol < 0) {
throw new IllegalArgumentException ("Must be greater than 0.");
}
this.dollars = dol;
cents = 0;
}
//Constructor which initialized dollars and cents.
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException
public Money (int dol, int cent) {
if (dol < 0 || cent < 0) {
throw new IllegalArgumentException ("Must be greater than 0.");
}
this.dollars = dol;
this.dollars += cent / 100;
this.cents = cent % 100;
}
//Copy constructor
public Money (Money other) {
this.dollars = other.dollars;
this.cents = other.cents;
}
public int getDollars () {
return dollars;
}
public int getCents () {
return cents;
}
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException
public void setMoney (int dollars, int cents) {
if (dollars < 0 || cents < 0) {
throw new IllegalArgumentException ("Must be greater than 0.");
}
this.dollars = dollars;
this.dollars += cents / 100;
this.cents = cents % 100;
}
//Gets the money amount as a double
//For example it might return 5.75
public double getMoney () {
return dollars + (cents / 100.0);
}
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException4
public void add (int dollars) {
if (dollars < 0) {
throw new IllegalArgumentException ("Must be greater than 0.");
}
this.dollars += dollars;
}
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException
public void add (int dollars, int cents) {
if (dollars < 0 || cents < 0) {
throw new IllegalArgumentException ("Must be greater than 0.");
}
this.dollars += dollars;
this.cents += cents;
this.dollars += this.cents / 100;
this.cents = this.cents % 100;
}
//Adds the amounts in other to our money object – reducing cents appropriately.
public void add (Money other) {
this.dollars += other.dollars;
this.cents += other.cents;
this.dollars += this.cents / 100;
this.cents = this.cents % 100;
}
//Two money objects are the same if they have the same value for dollars and cents.
public boolean equals (Object o) {
if( o instanceof Money) {
return this.dollars == ((Money)o).dollars && this.cents == ((Money)o).cents;
}
return false;
}
//Prints out the amount as a string IE “$3.75” or “$4.00” Note the number of digits displayed for cents.
//Again for testing and grading purposes use this EXACT output format
public String toString () {
String c = String.format("%.02d",cents);
return "$" + dollars + "." + c;
}
}
Your problem results from the fact that in your constructor for Bill you store references to the Money and Date objects. Then, when you modify those objects in the test case you are modifying the same objects.
If you don't want that behavior you have to make a deep copy of the Money and Date objects in the Bill constructor, i.e.:
public Bill (Money amount, Date dueDate, String originator) {
this.amount = new Money(amount);
this.dueDate = new Date(dueDate);
this.originator = originator;
paidDate = null;
}
You don't have to do this for originator because Strings are immutable.
Although you do not show the implementation of your Money class, the fact that it has a setMoney method suggests it's mutable. In that case, your problem is that Bill's constructor isn't making copies of the objects it's passed in, and thus any changes to money1 also change the state of myBill. Similar remarks apply to the Date objects.
Try modifying your code as follows:
public Bill (Money amount, Date dueDate, String originator) {
this.amount = new Money(amount); // needs copy-constructor for Money
this.dueDate = new Date(dueDate); // likewise for Date
this.originator = originator; // no copying needed as String is immutable
paidDate = null;
}
//copy constructor
public Bill (Bill toCopy) {
// Make copies also in the copy-constructor
this.amount = new Money(toCopy.amount);
this.dueDate = new Date(toCopy.dueDate);
this.paidDate = (toCopy.paidDate == null) ? null : new Date(toCopy.paidDate);
this.originator = toCopy.originator;
}
In general, designing your objects to be mutable means you have to copy defensively in constructors and elsewhere.
On the other hand, designing your objects to be immutable is better as it avoids such problems (and is in fact the advice given by Joshua Bloch in his "Effective Java" book), but it turns out that Java doesn't help you a lot with them either and it's likely you'll struggle for quite some time with getting them done right.
My recommendation is for you to explore the http://immutables.github.io/ library for a better starting point with this design approach.
When I am trying to replicate your code, I am getting error in this line:
public Date getDueDate () {
return new Date(dueDate);
}
Can you please tell what Date constructor you are using. As java.util.date has no such constructor which takes Date as an arguments.
Please elaborate so that I can proceed with debug and answer your query.
Thanks.
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 find the PowerScript's CHOOSE CASE statement very useful, as it make so that the code is more clearly than a lot of ifs and else ifs.
Here a example of how it works, from the above link:
CHOOSE CASE weight
CASE IS < 16
Postage=Weight*0.30
Method="USPS"
CASE 16 to 48
Postage=4.50
Method="UPS"
CASE ELSE
Postage=25.00
Method="FedEx"
END CHOOSE
a CASE 5 to 11 is the same as CASE 5, 6, 7, 8, 9, 10, 11
Note that the CHOOSE CASE is not equivalent to java's switch
In Java, you can use multiple case statements, but there isn't a nice way to specify an expression as the case qualifier, just literals:
switch(weight) {
case 1:
case 2:
case 3:
postage = weight * 0.30;
method = "USPS";
break;
case 4:
case 5:
case 6:
postage = 4.5;
method = "UPS";
break;
default:
postage = 25.0;
method = "FedEx";
break;
}
To get nice ranges, stick with if/else:
if(weight > 0 && weight <= 3) {
postage = weight * 0.30;
method = "USPS";
}
else if(weight > 3 && weight <= 6) {
postage = 4.5;
method = "UPS";
}
else {
postage = 25.0;
method = "FedEx";
}
If your objective is cleaning up the decision point, you could encapsulate the code that decides what case applies separately from the code that uses that decision, as in:
enum WeightClass { LOW, MEDIUM, HIGH };
public WeightClass determineWeightClass(int weight)
{
return (weight < 16)
? WeightClass.LOW
: (weight <= 48
? WeightClass.MEDIUM
: WeightClass.HIGH);
}
And at the decision point:
switch(determineWeightClass(weight))
{
case LOW:
...
break;
case MEDIUM:
...
break;
case HIGH:
...
break;
}
Not exactly the same. If you want to implement such fragment in Java, you have to use if-else[-if] statement.
Basically, it should look like this:
if (weight < 16) {
//something
} else if (weight >= 16 && weight <= 48) {
//something else
} else {
//some other thing
}
Hope it works for you. :)
If there are only 3 cases, a series of if/else is fine. If you have many conditions, you could use a Navigable map and couple it with an enum for a nice and slick design:
public class Test1 {
public static void main(String[] args) {
printDelivery(0);
printDelivery(5);
printDelivery(16);
printDelivery(48);
printDelivery(50);
}
private static void printDelivery(int weight) {
Delivery d = Delivery.getDelivery(weight);
System.out.println("Weight: " + weight + " => $" + d.getPostage(weight) + " with " + d.getMethod());
}
static enum Delivery {
LOW_WEIGHT(15) {
public double getPostage(int weight) { return 0.3 * weight; }
public String getMethod() { return "USPS"; }
}, MEDIUM_WEIGHT(47) {
public double getPostage(int weight) { return 4.5; }
public String getMethod() { return "UPS"; }
}, HIGH_WEIGHT(Integer.MAX_VALUE){
public double getPostage(int weight) { return 25.0; }
public String getMethod() { return "FedEx"; }
};
private static final NavigableMap<Integer, Delivery> deliveries = new TreeMap<> ();
static {
for (Delivery e : values()) {
deliveries.put(e.maxWeight, e);
}
}
private final int maxWeight;
Delivery(int maxWeight) {
this.maxWeight = maxWeight;
}
public static Delivery getDelivery(int weight) {
return deliveries.ceilingEntry(weight).getValue();
}
abstract double getPostage(int weight);
abstract String getMethod();
}
}
No. You would have to use a series of if-elseif-else statements.
I have an algorithm that recursively makes change in the following manner:
public static int makeChange(int amount, int currentCoin) {
//if amount = zero, we are at the bottom of a successful recursion
if (amount == 0){
//return 1 to add this successful solution
return 1;
//check to see if we went too far
}else if(amount < 0){
//don't count this try if we went too far
return 0;
//if we have exhausted our list of coin values
}else if(currentCoin < 0){
return 0;
}else{
int firstWay = makeChange(amount, currentCoin-1);
int secondWay = makeChange(amount - availableCoins[currentCoin], currentCoin);
return firstWay + secondWay;
}
}
However, I'd like to add the capability to store or print each combination as they successfully return. I'm having a bit of a hard time wrapping my head around how to do this. The original algorithm was pretty easy, but now I am frustrated. Any suggestions?
CB
Without getting into the specifics of your code, one pattern is to carry a mutable container for your results in the arguments
public static int makeChange(int amount, int currentCoin, List<Integer>results) {
// ....
if (valid_result) {
results.add(result);
makeChange(...);
}
// ....
}
And call the function like this
List<Integer> results = new LinkedList<Integer>();
makeChange(amount, currentCoin, results);
// after makeChange has executed your results are saved in the variable "results"
I don't understand logic or purpose of above code but this is how you can have each combination stored and then printed.
public class MakeChange {
private static int[] availableCoins = {
1, 2, 5, 10, 20, 25, 50, 100 };
public static void main(String[] args) {
Collection<CombinationResult> results = makeChange(5, 7);
for (CombinationResult r : results) {
System.out.println(
"firstWay=" + r.getFirstWay() + " : secondWay="
+ r.getSecondWay() + " --- Sum=" + r.getSum());
}
}
public static class CombinationResult {
int firstWay;
int secondWay;
CombinationResult(int firstWay, int secondWay) {
this.firstWay = firstWay;
this.secondWay = secondWay;
}
public int getFirstWay() {
return this.firstWay;
}
public int getSecondWay() {
return this.secondWay;
}
public int getSum() {
return this.firstWay + this.secondWay;
}
public boolean equals(Object o) {
boolean flag = false;
if (o instanceof CombinationResult) {
CombinationResult r = (CombinationResult) o;
flag = this.firstWay == r.firstWay
&& this.secondWay == r.secondWay;
}
return flag;
}
public int hashCode() {
return this.firstWay + this.secondWay;
}
}
public static Collection<CombinationResult> makeChange(
int amount, int currentCoin) {
Collection<CombinationResult> results =
new ArrayList<CombinationResult>();
makeChange(amount, currentCoin, results);
return results;
}
public static int makeChange(int amount, int currentCoin,
Collection<CombinationResult> results) {
// if amount = zero, we are at the bottom of a successful recursion
if (amount == 0) {
// return 1 to add this successful solution
return 1;
// check to see if we went too far
} else if (amount < 0) {
// don't count this try if we went too far
return 0;
// if we have exhausted our list of coin values
} else if (currentCoin < 0) {
return 0;
} else {
int firstWay = makeChange(
amount, currentCoin - 1, results);
int secondWay = makeChange(
amount - availableCoins[currentCoin],
currentCoin, results);
CombinationResult resultEntry = new CombinationResult(
firstWay, secondWay);
results.add(resultEntry);
return firstWay + secondWay;
}
}
}
I used the following:
/**
* This is a recursive method that calculates and displays the combinations of the coins included in
* coinAmounts that sum to amountToBeChanged.
*
* #param coinsUsed is a list of each coin used so far in the total. If this branch is successful, we will add another coin on it.
* #param largestCoinUsed is used in the recursion to indicate at which coin we should start trying to add additional ones.
* #param amountSoFar is used in the recursion to indicate what sum we are currently at.
* #param amountToChange is the original amount that we are making change for.
* #return the number of successful attempts that this branch has calculated.
*/private static int change(List<Integer> coinsUsed, Integer currentCoin, Integer amountSoFar, Integer amountToChange)
{
//if last added coin took us to the correct sum, we have a winner!
if (amountSoFar == amountToChange)
{
//output
System.out.print("Change for "+amountToChange+" = ");
//run through the list of coins that we have and display each.
for(Integer count: coinsUsed){
System.out.print(count + " ");
}
System.out.println();
//pass this back to be tallied
return 1;
}
/*
* Check to see if we overshot the amountToBeChanged
*/
if (amountSoFar > amountToChange)
{
//this branch was unsuccessful
return 0;
}
//this holds the sum of the branches that we send below it
int successes=0;
// Pass through each coin to be used
for (Integer coin:coinAmounts)
{
//we only want to work on currentCoin and the coins after it
if (coin >= currentCoin)
{
//copy the list so we can branch from it
List<Integer> copyOfCoinsUsed = new ArrayList<Integer>(coinsUsed);
//add on one of our current coins
copyOfCoinsUsed.add(coin);
//branch and then collect successful attempts
successes += change(copyOfCoinsUsed, coin, amountSoFar + coin, amountToChange);
}
}
//pass back the current
return successes;
}