Calculate total percent from a suite of trading orders - java

I try to sum up percent from a suite of simple trading orders in Java.
Let say that an "order" is composed of a position (BUY/SELL), a price and a quantity.
If I have:
TIME | POSITION | QTY |PRICE
DAY1: BUY 1 at 10$
DAY2: SELL 1 at 12$
I have made 20% and it's quite easy to program it.
But... taking another example, I'm not smart enough to find the right way to get the total percent:
TIME | POSITION | QTY | MARKET PRICE
DAY1: BUY 1 at 10$
DAY2: SELL 1 at 12$ -> (20%)
DAY3: BUY 2 at 10$ -> (0%)
DAY4: SELL 1 at 13$ -> (30%)
DAY5: SELL 1 at 14$ -> (40%)
So a total of 90%.
First question, can these percentages be sum up ? (is it mathematically correct ?)
Second, how you will do that in java ?
Here is a sample method which is just working for first example:
public static double getTotalPercent(List<MarketOrder> orders) {
double percent = 0;
MarketOrder previousOrder = orders.get(0);
for (int i = 1; i < orders.size(); i++) {
MarketOrder order = orders.get(i);
percent += getPercent(previousOrder, order);
previousOrder = order;
}
return percent;
}

Assuming the following:
You have some type MarketOrder that looks like this,
class MarketOrder {
public static enum OrderType {
BUY,
SELL,
}
private final MarketOrder.OrderType type;
private final int amount;
private final int money;
public MarketOrder(MarketOrder.OrderType type, int amount, int money) {
if (amount < 1 || money < 1) {
throw new IllegalArgumentException();
}
this.type = type;
this.amount = amount;
this.money = money;
}
}
and you have some Collection<MarketOrder> that represents transactions that you want to calculate a profit for, then declare a static method on MarketOrder that calculates the profit or loss of a Collection<MarketOrder>,
public static double calculateProfit(Collection<MarketOrder> orders) {
if (orders == null) {
throw new NullPointerException("orders is null");
}
int buyTotal = 0, sellTotal = 0, buyMoney = 0, sellMoney = 0;
for (MarketOrder order : orders) {
if (order.type == MarketOrder.OrderType.BUY) { // accumulate the units bought and money
buyTotal += order.amount;
buyMoney += order.money * order.amount;
} else {
sellTotal += order.amount;
sellMoney += order.money * order.amount;
}
}
if (buyTotal != sellTotal) {
throw new IllegalArgumentException("buyTotal != sellTotal");
}
return ((double)sellMoney/(double)buyMoney) - 1;
}
Obviously, the exact code you'll need to work out depends upon your MarketOrder type, but the key bits (accumlating the amounts of the transactions, doing the math) is the same.

Related

Need to replace multiple independent if statements

I am a beginner to Java and I have a health insurance program, which returns a total quote based on if the customer has any health conditions already present or not. Each health condition increases the total amount by a different %, and there can be more than one health condition present, in which case the total will be increased according to the order of the if statements. For example, the customer may have "Bone marrow", in which case the total is multiplied by 20%, or they may have "Bone marrow" and "Cancer" in which case the total is increased by 20% and then 25% in that order.
I have this written in multiple independent if statements because unlike with an if else statement, there can be more than one health condition present. Is there a way I can write this in a way that's more elegant than just a long list of if statements?
if (customer.getHealthConditions().equals("Bone Marrow")) {
total *= 1.2;
}
if (customer.getHealthConditions().equals("Cancer")) {
total *= 1.25;
}
if (customer.getHealthConditions().equals("Cardiovascular Disease")) {
total *= 1.3;
}
if (customer.getHealthConditions().equals("Gastrointestinal")) {
total *= 1.1;
}
if (customer.getHealthConditions().equals("Infections")) {
total *= 1.1;
}
if (customer.getHealthConditions().equals("Kidneys")) {
total *= 1.25;
}
if (customer.getHealthConditions().equals("Lungs")) {
total *= 1.25;
}
if (customer.getHealthConditions().equals("Musculoskeletal")) {
total *= 1.3;
}
It seems that switch statement is more appropriate in this case:
double quotient = 1.0;
switch(customer.getHealthConditions()) {
case "Bone Marrow":
quotient = 1.2; break;
case "Cancer":
case "Kidneys":
case "Lungs":
quotient = 1.25; break;
case "Cardiovascular Disease":
case "Musculoskeletal":
quotient = 1.3; break;
case "Gastrointestinal":
case "Infections":
quotient = 1.1; break;
}
total *= quotient;
In Java 12+ switch statement was enhanced with multiple cases and arrow -> so it may be written as:
total *= switch(customer.getHealthConditions()) {
case "Bone Marrow" -> 1.2;
case "Cancer", "Kidneys", "Lungs" -> 1.25;
case "Cardiovascular Disease", "Musculoskeletal" -> 1.3;
case "Gastrointestinal", "Infections" -> 1.1;
default -> 1.0;
}
Update
If health conditions are multiple, then equals is not applicable at all, instead String::contains or Collection::contains should be used and it would be better to have a map or enum of the disease to quotient:
Map<String, Double> quotients = Map.of(
"Bone Marrow", 1.2,
"Cancer", 1.25,
"Kidneys", 1.25,
"Lungs", 1.25
// ...
);
total *= quotients.entrySet().stream()
.filter(e -> customer.getHealthConditions().contains(e.getKey()))
.map(Map.Entry::getValue)
.reduce(1.0, (p, v) -> p * v);
I think here an enum could be useful, we need not only to sum all the values but also the order may be important (the progressive premium increase changes otherwise)
import java.util.Comparator;
import java.util.Optional;
import static java.util.Arrays.*;
enum HealthConditionPremium {
boneMarrow(1,1.2, "Bone Marrow"),
cancer(2,1.25, "Cancer"),
cardiovascularDisease(3,1.3, "Cardiovascular Disease"),
gastrointestinal(4,1.1, "Gastrointestinal"),
infections(5,1.1, "Infections"),
kidneys(6,1.25, "Kidneys"),
lungs(7,1.25, "Lungs"),
musculoskeletal(8,1.3, "Musculoskeletal");
public final int order;
public final double premiumIncrease;
public final String matchString;
HealthConditionPremium(int order, double premiumIncrease, String matchString) {
this.order = order;
this.premiumIncrease = premiumIncrease;
this.matchString = matchString;
}
static Optional<HealthConditionPremium> of(String condition) {
return stream(values()).filter(healthCondition -> healthCondition.matchString.equals(condition)).findAny();
}
public static double totalForHealthConditions(String ...conditions) {
return stream(conditions).
filter(condition -> condition != null && !condition.isEmpty()).
map(HealthConditionPremium::of).
filter(Optional::isPresent).
map(Optional::get).
sorted(Comparator.comparingInt(hc -> hc.order)).
map(healthConditionPremium -> healthConditionPremium.premiumIncrease).
reduce(1.0, (total, additionalPremium) -> total * additionalPremium);
}
}
class Scratch {
public static void main(String[] args) {
Customer customer = new Customer("Gastrointestinal");
double total = 1;
if (customer.getHealthConditions().equals("Bone Marrow")) {
total *= 1.2;
}
if (customer.getHealthConditions().equals("Cancer")) {
total *= 1.25;
}
if (customer.getHealthConditions().equals("Cardiovascular Disease")) {
total *= 1.3;
}
if (customer.getHealthConditions().equals("Gastrointestinal")) {
total *= 1.1;
}
if (customer.getHealthConditions().equals("Infections")) {
total *= 1.1;
}
if (customer.getHealthConditions().equals("Kidneys")) {
total *= 1.25;
}
if (customer.getHealthConditions().equals("Lungs")) {
total *= 1.25;
}
if (customer.getHealthConditions().equals("Musculoskeletal")) {
total *= 1.3;
}
System.out.println("total = " + total);
System.out.println("----------------------------------");
double totalWithEnum = HealthConditionPremium.totalForHealthConditions("Gastrointestinal");
System.out.println("totalWithEnum = " + totalWithEnum);
System.out.println("----------------------------------");
double totalManyWithEnum = HealthConditionPremium.totalForHealthConditions("Gastrointestinal", "Cancer", "Kidneys");
System.out.println("totalManyWithEnum = " + totalManyWithEnum);
double totalManyWithEnumDifferentOrder = HealthConditionPremium.totalForHealthConditions("Cancer", "Gastrointestinal", "Kidneys");
System.out.println("totalManyWithEnumDifferentOrder = " + totalManyWithEnumDifferentOrder);
}
static class Customer {
private final String condition;
Customer(String condition) {
this.condition = condition;
}
public String getHealthConditions() {
return condition;
}
}
}
Output
total = 1.1
----------------------------------
totalWithEnum = 1.1
----------------------------------
totalManyWithEnum = 1.71875
totalManyWithEnumDifferentOrder = 1.71875
Some words of advice :)
Using a Double for premium (Money related) calculation is not advised, use BigDecimal Instead
The Reason for order in the enum is to make the order explicit and NOT rely on ordinal (order of definition of enum)
This way the definition of Health Conditions and Their Premiums is gathered in one place for easy reading.
The Assumption (and probably a big one) customer.getHealthConditions() actually returns an array of Strings to pass to totalForHealthConditions

Turning a Queue into a Priority Queue

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());

How to iterate to find the lowest value

Struggling to understand where I went wrong with the iteration at the get best fare method
The array holds [5.77, 2.44, 2.35] and should return the second index, however it seems that it is stuck at the double lowestPrice = lowestPriceRide[0];
I thought that maybe I was putting the return out of scope, but it didn't work.
> import java.lang.*;
import java.util.Arrays;
public class TransitCalculator {
double numberOfDays = 0.0;
double numberOfRides = 0.0;
double pricePerRide = 2.75;
double pricePerWeek = 33.00;
double priceUnlimited = 127.00;
double perRide = 0.00;
public TransitCalculator(double days, double rides){
numberOfDays = days;
numberOfRides = rides;
}
public double unlimited7Price(){
double numOfWeeks = Math.ceil(numberOfDays/7) ; // Math.ceil will return the largest integer that is divisble without a remainder //
double totalPrice = numOfWeeks * pricePerWeek;
return totalPrice / numberOfRides;
}
public double[] getRidePrices(){ // 28/06/2020 Sunday. Math is verified.
double perRide = pricePerRide * numberOfRides / numberOfDays;
double perWeek = unlimited7Price();
double unlimited = priceUnlimited / numberOfRides;
double ridePrices[]; // Declared Array //
ridePrices = new double[] {perRide, perWeek, unlimited}; // New array, with added elements. Could be a mistake since I failed to declare elements//
return ridePrices;
}
public String getBestFare(){ // Error in the iteration and lowest value find! //
double lowestPriceRide[];
lowestPriceRide = getRidePrices();
double lowestPrice = lowestPriceRide[0];
for(int i = 0; i< lowestPriceRide.length; i++) {
if (lowestPrice < lowestPriceRide[i]) {
lowestPriceRide[i] = lowestPrice;
}
}
if(lowestPrice == lowestPriceRide[0]){
System.out.println("You should take the 'Pay per Ride' option in our NYC transit");
}
else if(lowestPrice == lowestPriceRide[1]){
System.out.println("You should take the 'Weekly Unlimited' plan in our NYC Transit");
}
else if(lowestPrice == lowestPriceRide[2]){
System.out.println("You should take the Unlimited ride plan in our NYC Transit");
}
return "at " + lowestPrice + "$ per Ride";
}
public static void main(String[] args){
TransitCalculator test = new TransitCalculator(26, 54);
System.out.println(test.getBestFare()); //
}
}
You are not setting the right value; currently, you set the element in the array to the lowest price instead of setting the lowest price to the element of the array. You also compare against the wrong value; you should check that the current array element is less than the best price, instead of the other way around.
Change
if(lowestPrice < lowestPriceRide[i])
lowestPriceRide[i] = lowestPrice;
To
if(lowestPriceRide[i] < lowestPrice)
lowestPrice = lowestPriceRide[i];
See the updated code in action here.
Note that it is unnecessary to import java.lang, as the package is implicitly imported.
The problem is in your if condition:
if (lowestPrice < lowestPriceRide[i]) {
lowestPriceRide[i] = lowestPrice;
}
You need to see if the current lowestPriceRide[i] is less than the already existing lowestPrice then update your existing lowestPrice. So the condition would be now:
if (lowestPriceRide[i] < lowestPrice) {
lowestPrice = lowestPriceRide[i];
}
This should be your comparison for lowest price :
double lowestPrice = lowestPriceRide[0];
for(int i = 0; i< lowestPriceRide.length; i++) {
if (lowestPriceRide[i] < lowestPrice) {
lowestPrice = lowestPriceRide[i];
}
}

How to fix leaking accessor methods?

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.

Recursive function to calculate possible finish for darts

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;
}
}

Categories

Resources