Skip to content

Commit 3f5c931

Browse files
authored
Sync two bucket (exercism#2877)
1 parent 7edff84 commit 3f5c931

File tree

9 files changed

+262
-163
lines changed

9 files changed

+262
-163
lines changed

exercises/practice/two-bucket/.meta/config.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
],
55
"contributors": [
66
"FridaTveit",
7+
"jagdish-15",
78
"jmrunkle",
9+
"kahgoh",
810
"lemoncurry",
11+
"LoadingBG",
912
"mirkoperillo",
1013
"msomji",
1114
"muzimuzhi",
@@ -15,13 +18,17 @@
1518
],
1619
"files": {
1720
"solution": [
18-
"src/main/java/TwoBucket.java"
21+
"src/main/java/TwoBucket.java",
22+
"src/main/java/Result.java",
23+
"src/main/java/UnreachableGoalException.java"
1924
],
2025
"test": [
2126
"src/test/java/TwoBucketTest.java"
2227
],
2328
"example": [
24-
".meta/src/reference/java/TwoBucket.java"
29+
".meta/src/reference/java/TwoBucket.java",
30+
".meta/src/reference/java/Result.java",
31+
".meta/src/reference/java/UnreachableGoalException.java"
2532
],
2633
"invalidator": [
2734
"build.gradle"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
final class Result {
2+
3+
private final int totalMoves;
4+
private final String finalBucket;
5+
private final int otherBucket;
6+
7+
Result(int totalMoves, String finalBuckets, int otherBucket) {
8+
this.totalMoves = totalMoves;
9+
this.finalBucket = finalBuckets;
10+
this.otherBucket = otherBucket;
11+
}
12+
13+
public int getTotalMoves() {
14+
return totalMoves;
15+
}
16+
17+
public String getFinalBucket() {
18+
return finalBucket;
19+
}
20+
21+
public int getOtherBucket() {
22+
return otherBucket;
23+
}
24+
}
Lines changed: 99 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,124 @@
11
import java.util.ArrayList;
2-
import java.util.Objects;
2+
import java.util.List;
3+
import java.util.Map;
34

4-
class TwoBucket {
5+
final class TwoBucket {
6+
7+
private int totalMoves = Integer.MAX_VALUE;
8+
private String finalBucket = "";
9+
private int otherBucket = Integer.MAX_VALUE;
510

6-
private class State {
7-
int moves;
8-
int bucketOne;
9-
int bucketTwo;
10-
11-
State (int moves, int bucketOne, int bucketTwo) {
12-
this.moves = moves;
13-
this.bucketOne = bucketOne;
14-
this.bucketTwo = bucketTwo;
15-
}
16-
17-
@Override
18-
public boolean equals(Object o) {
19-
State otherState = (State) o;
20-
return this.moves == otherState.moves &&
21-
this.bucketOne == otherState.bucketOne &&
22-
this.bucketTwo == otherState.bucketTwo;
23-
}
24-
25-
@Override
26-
public int hashCode() {
27-
return Objects.hash(moves, bucketOne, bucketTwo);
28-
}
29-
}
30-
31-
private State finalState;
32-
33-
private int bucketOneCap;
34-
private int bucketTwoCap;
35-
private int desiredLiters;
36-
private String startBucket;
11+
private final List<Map.Entry<Integer, Integer>> statesReached = new ArrayList<>();
3712

3813
TwoBucket(int bucketOneCap, int bucketTwoCap, int desiredLiters, String startBucket) {
39-
this.bucketOneCap = bucketOneCap;
40-
this.bucketTwoCap = bucketTwoCap;
41-
this.desiredLiters = desiredLiters;
42-
this.startBucket = startBucket;
43-
44-
finalState = computeFinalState();
14+
checkIfImpossible(desiredLiters, bucketOneCap, bucketTwoCap);
15+
16+
fillBuckets(
17+
startBucket.equals("one") ? bucketOneCap : 0,
18+
bucketOneCap,
19+
startBucket.equals("two") ? bucketTwoCap : 0,
20+
bucketTwoCap,
21+
desiredLiters,
22+
1,
23+
startBucket
24+
);
4525
}
4626

47-
private ArrayList<State> getAdjacentStates (State state) {
48-
ArrayList<State> adjacentStates = new ArrayList<State>();
49-
50-
//Empty bucket one
51-
adjacentStates.add(new State(state.moves + 1, 0, state.bucketTwo));
52-
53-
//Empty bucket two
54-
adjacentStates.add(new State(state.moves + 1, state.bucketOne, 0));
55-
56-
//Fill bucket one
57-
adjacentStates.add(new State(state.moves + 1, bucketOneCap, state.bucketTwo));
58-
59-
//Fill bucket two
60-
adjacentStates.add(new State(state.moves + 1, state.bucketOne, bucketTwoCap));
61-
62-
//pour from bucket one to bucket two
63-
if (state.bucketOne + state.bucketTwo > bucketTwoCap) {
64-
adjacentStates.add(new State(state.moves + 1,
65-
state.bucketOne - (bucketTwoCap - state.bucketTwo),
66-
bucketTwoCap));
67-
} else {
68-
adjacentStates.add(new State(state.moves + 1, 0, state.bucketOne + state.bucketTwo));
27+
private void fillBuckets(
28+
int bucketOne,
29+
int bucketOneCap,
30+
int bucketTwo,
31+
int bucketTwoCap,
32+
int desiredLiters,
33+
int actionsTaken,
34+
String startingBucket
35+
) {
36+
if (startingBucket.equals("one") && bucketOne == 0 && bucketTwo == bucketTwoCap
37+
|| startingBucket.equals("two") && bucketTwo == 0 && bucketOne == bucketOneCap
38+
|| statesReached.contains(Map.entry(bucketOne, bucketTwo))
39+
|| bucketOne > bucketOneCap
40+
|| bucketTwo > bucketTwoCap) {
41+
return;
6942
}
70-
71-
//pour from bucket two to bucket one
72-
if (state.bucketTwo + state.bucketOne > bucketOneCap) {
73-
adjacentStates.add(new State(state.moves + 1,
74-
bucketOneCap,
75-
state.bucketTwo - (bucketOneCap - state.bucketOne)));
76-
} else {
77-
adjacentStates.add(new State(state.moves + 1, state.bucketTwo + state.bucketOne, 0));
43+
44+
if (bucketOne == desiredLiters) {
45+
if (actionsTaken < totalMoves) {
46+
this.totalMoves = actionsTaken;
47+
this.finalBucket = "one";
48+
this.otherBucket = bucketTwo;
49+
}
50+
return;
7851
}
79-
80-
return adjacentStates;
81-
}
82-
83-
private boolean isValid(State state) {
84-
if (state.bucketOne == bucketOneCap && state.bucketTwo == 0 && startBucket.equals("two")) {
85-
return false;
86-
} else if (state.bucketOne == 0 && state.bucketTwo == bucketTwoCap && startBucket.equals("two")) {
87-
return false;
88-
} else {
89-
return true;
52+
if (bucketTwo == desiredLiters) {
53+
if (actionsTaken < totalMoves) {
54+
this.totalMoves = actionsTaken;
55+
this.finalBucket = "two";
56+
this.otherBucket = bucketOne;
57+
}
58+
return;
9059
}
91-
}
9260

93-
private State computeFinalState() {
94-
ArrayList<State> paths = new ArrayList<State>();
61+
statesReached.add(Map.entry(bucketOne, bucketTwo));
9562

96-
State initialState;
97-
if (startBucket.equals("one")) {
98-
initialState = new State(1, bucketOneCap, 0);
99-
} else {
100-
initialState = new State(1, 0, bucketTwoCap);
63+
if (bucketOne != 0) {
64+
fillBuckets(0, bucketOneCap, bucketTwo, bucketTwoCap, desiredLiters, actionsTaken + 1, startingBucket);
10165
}
102-
103-
if (initialState.bucketOne == desiredLiters || initialState.bucketTwo == desiredLiters) {
104-
return initialState;
66+
if (bucketTwo != 0) {
67+
fillBuckets(bucketOne, bucketOneCap, 0, bucketTwoCap, desiredLiters, actionsTaken + 1, startingBucket);
10568
}
10669

107-
paths.add(initialState);
108-
109-
for (int i = 0; i < 10000; i++) {
110-
State currentState = paths.remove(0);
111-
ArrayList<State> adjacentStates = getAdjacentStates(currentState);
112-
for (State state : adjacentStates) {
113-
if (state.bucketOne == desiredLiters || state.bucketTwo == desiredLiters) {
114-
return state;
115-
}
116-
117-
if (!paths.contains(state) && isValid(state)) {
118-
paths.add(state);
119-
}
120-
}
70+
fillBuckets(
71+
bucketOneCap, bucketOneCap, bucketTwo, bucketTwoCap, desiredLiters,
72+
actionsTaken + 1, startingBucket
73+
);
74+
fillBuckets(
75+
bucketOne, bucketOneCap, bucketTwoCap, bucketTwoCap, desiredLiters,
76+
actionsTaken + 1, startingBucket
77+
);
78+
79+
if (bucketOne + bucketTwo <= bucketTwoCap) {
80+
fillBuckets(
81+
0, bucketOneCap, bucketTwo + bucketOne, bucketTwoCap, desiredLiters,
82+
actionsTaken + 1, startingBucket
83+
);
84+
} else {
85+
fillBuckets(
86+
bucketOne + bucketTwo - bucketTwoCap, bucketOneCap, bucketTwoCap, bucketTwoCap,
87+
desiredLiters, actionsTaken + 1, startingBucket
88+
);
89+
}
90+
if (bucketOne + bucketTwo <= bucketOneCap) {
91+
fillBuckets(
92+
bucketOne + bucketTwo, bucketOneCap, 0, bucketTwoCap, desiredLiters,
93+
actionsTaken + 1, startingBucket
94+
);
95+
} else {
96+
fillBuckets(
97+
bucketOneCap, bucketOneCap, bucketTwo + bucketOne - bucketOneCap, bucketTwoCap,
98+
desiredLiters, actionsTaken + 1, startingBucket
99+
);
121100
}
122-
123-
return null;
124101
}
125102

126-
int getTotalMoves() {
127-
return finalState.moves;
103+
private void checkIfImpossible(int desiredLiters, int bucketOneCap, int bucketTwoCap) {
104+
boolean exceedsCapacity = desiredLiters > bucketOneCap && desiredLiters > bucketTwoCap;
105+
boolean invalidDivision = desiredLiters % gcd(bucketOneCap, bucketTwoCap) != 0;
106+
107+
if (exceedsCapacity || invalidDivision) {
108+
throw new UnreachableGoalException();
109+
}
128110
}
129111

130-
String getFinalBucket() {
131-
if (finalState.bucketOne == desiredLiters) {
132-
return "one";
133-
} else if (finalState.bucketTwo == desiredLiters) {
134-
return "two";
135-
} else {
136-
return "No solution found in " + finalState.moves + " iterations!";
112+
private int gcd(int a, int b) {
113+
while (b != 0) {
114+
int temp = b;
115+
b = a % b;
116+
a = temp;
137117
}
118+
return a;
138119
}
139120

140-
int getOtherBucket() {
141-
if (getFinalBucket().equals("one")) {
142-
return finalState.bucketTwo;
143-
} else if (getFinalBucket().equals("two")) {
144-
return finalState.bucketOne;
145-
} else {
146-
return -1;
147-
}
121+
Result getResult() {
122+
return new Result(totalMoves, finalBucket, otherBucket);
148123
}
149124
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public class UnreachableGoalException extends RuntimeException {
2+
3+
public UnreachableGoalException() {
4+
super();
5+
}
6+
7+
public UnreachableGoalException(String message) {
8+
super(message);
9+
}
10+
11+
public UnreachableGoalException(String message, Throwable cause) {
12+
super(message, cause);
13+
}
14+
}

exercises/practice/two-bucket/.meta/tests.toml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
# This is an auto-generated file. Regular comments will be removed when this
2-
# file is regenerated. Regenerating will not touch any manually added keys,
3-
# so comments can be added in a "comment" key.
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
411

512
[a6f2b4ba-065f-4dca-b6f0-e3eee51cb661]
613
description = "Measure using bucket one of size 3 and bucket two of size 5 - start with bucket one"
@@ -19,3 +26,12 @@ description = "Measure one step using bucket one of size 1 and bucket two of siz
1926

2027
[eb329c63-5540-4735-b30b-97f7f4df0f84]
2128
description = "Measure using bucket one of size 2 and bucket two of size 3 - start with bucket one and end with bucket two"
29+
30+
[449be72d-b10a-4f4b-a959-ca741e333b72]
31+
description = "Not possible to reach the goal"
32+
33+
[aac38b7a-77f4-4d62-9b91-8846d533b054]
34+
description = "With the same buckets but a different goal, then it is possible"
35+
36+
[74633132-0ccf-49de-8450-af4ab2e3b299]
37+
description = "Goal larger than both buckets is impossible"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
final class Result {
2+
3+
private final int totalMoves;
4+
private final String finalBucket;
5+
private final int otherBucket;
6+
7+
Result(int totalMoves, String finalBuckets, int otherBucket) {
8+
this.totalMoves = totalMoves;
9+
this.finalBucket = finalBuckets;
10+
this.otherBucket = otherBucket;
11+
}
12+
13+
public int getTotalMoves() {
14+
return totalMoves;
15+
}
16+
17+
public String getFinalBucket() {
18+
return finalBucket;
19+
}
20+
21+
public int getOtherBucket() {
22+
return otherBucket;
23+
}
24+
}
Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
class TwoBucket {
2+
23
TwoBucket(int bucketOneCap, int bucketTwoCap, int desiredLiters, String startBucket) {
34
throw new UnsupportedOperationException("Please implement the TwoBucket(int, int, int, String) constructor.");
45
}
56

6-
int getTotalMoves() {
7-
throw new UnsupportedOperationException("Please implement the TwoBucket.getTotalMoves() method.");
8-
}
9-
10-
String getFinalBucket() {
11-
throw new UnsupportedOperationException("Please implement the TwoBucket.getFinalBucket() method.");
12-
}
13-
14-
int getOtherBucket() {
15-
throw new UnsupportedOperationException("Please implement the TwoBucket.getOtherBucket() method.");
7+
Result getResult() {
8+
throw new UnsupportedOperationException("Please implement the TwoBucket(int, int, int, String) constructor.");
169
}
10+
1711
}

0 commit comments

Comments
 (0)