Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions exercises/practice/two-bucket/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Instructions.append

## Output format

The `solve()` method is expected to return an object with these properties:

- `moves` - the number of bucket actions required to reach the goal
(includes filling the start bucket),
- `goalBucket` - the name of the bucket that reached the goal amount,
- `otherBucket` - the amount contained in the other bucket.

Example:

```json
{
"moves": 5,
"goalBucket": "one",
"otherBucket": 2
}
```
30 changes: 17 additions & 13 deletions exercises/practice/two-bucket/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Instructions

Given two buckets of different size, demonstrate how to measure an exact number of liters by strategically transferring liters of fluid between the buckets.
Given two buckets of different size and which bucket to fill first, determine how many actions are required to measure an exact number of liters by strategically transferring fluid between the buckets.

Since this mathematical problem is fairly subject to interpretation / individual approach, the tests have been written specifically to expect one overarching solution.
There are some rules that your solution must follow:

To help, the tests provide you with which bucket to fill first. That means, when starting with the larger bucket full, you are NOT allowed at any point to have the smaller bucket full and the larger bucket empty (aka, the opposite starting point); that would defeat the purpose of comparing both approaches!
- You can only do one action at a time.
- There are only 3 possible actions:
1. Pouring one bucket into the other bucket until either:
a) the first bucket is empty
b) the second bucket is full
2. Emptying a bucket and doing nothing to the other.
3. Filling a bucket and doing nothing to the other.
- After an action, you may not arrive at a state where the starting bucket is empty and the other bucket is full.

Your program will take as input:

Expand All @@ -15,19 +22,16 @@ Your program will take as input:

Your program should determine:

- the total number of "moves" it should take to reach the desired number of liters, including the first fill
- which bucket should end up with the desired number of liters (let's say this is bucket A) - either bucket one or bucket two
- how many liters are left in the other bucket (bucket B)
- the total number of actions it should take to reach the desired number of liters, including the first fill of the starting bucket
- which bucket should end up with the desired number of liters - either bucket one or bucket two
- how many liters are left in the other bucket

Note: any time a change is made to either or both buckets counts as one (1) move.
Note: any time a change is made to either or both buckets counts as one (1) action.

Example:
Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters. Let's say bucket one, at a given step, is holding 7 liters, and bucket two is holding 8 liters (7,8). If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one "move". Instead, if you had poured from bucket one into bucket two until bucket two was full, leaving you with 4 liters in bucket one and 11 liters in bucket two (4,11), that would count as only one "move" as well.
Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters. Let's say at a given step, bucket one is holding 7 liters and bucket two is holding 8 liters (7,8). If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one action. Instead, if you had poured from bucket one into bucket two until bucket two was full, resulting in 4 liters in bucket one and 11 liters in bucket two (4,11), that would also only count as one action.

To conclude, the only valid moves are:

- pouring from either bucket to another
- emptying either bucket and doing nothing to the other
- filling either bucket and doing nothing to the other
Another Example:
Bucket one can hold 3 liters, and bucket two can hold up to 5 liters. You are told you must start with bucket one. So your first action is to fill bucket one. You choose to empty bucket one for your second action. For your third action, you may not fill bucket two, because this violates the third rule -- you may not end up in a state after any action where the starting bucket is empty and the other bucket is full.

Written with <3 at [Fullstack Academy](http://www.fullstackacademy.com/) by Lindsay Levine.
208 changes: 89 additions & 119 deletions exercises/practice/two-bucket/.meta/proof.ci.js
Original file line number Diff line number Diff line change
@@ -1,141 +1,111 @@
export class TwoBucket {
constructor(x, y, z, starter) {
this.starter = starter;
this.x = x;
this.y = y;
this.z = z;
}
constructor(size1, size2, goal, start) {
this.goal = goal;
this.buckets = [new Bucket('one', size1), new Bucket('two', size2)];

reachedGoal(measurements) {
const j = measurements[0];
const k = measurements[1];
if (start === 'two') {
this.buckets.reverse();
}

if (j === this.z || k === this.z) {
if (j === this.z) {
this.goalBucket = 'one';
this.otherBucket = k;
} else {
this.goalBucket = 'two';
this.otherBucket = j;
}
this.validate();
}

get first() {
return this.buckets[0];
}
get second() {
return this.buckets[1];
}

return true;
validate() {
if (this.goal > Math.max(this.first.size, this.second.size)) {
throw new Error('Goal is bigger than the largest bucket.');
}

return false;
if (this.goal % gcd(this.first.size, this.second.size) !== 0) {
throw new Error(
'Goal must be a multiple of the GCD of the sizes of the two buckets.'
);
}
}

bigFirst(measurements, moveCount, prBool) {
let measure = measurements;
let mvCount = moveCount;
let j = measure[0];
let k = measure[1];
let bool = prBool;

while (!this.reachedGoal(measure)) {
if (k > this.x && j === 0 && mvCount === 0) {
j = this.x;
k = this.y - j;
} else if (j === this.x) {
j = 0;
} else if (k > this.x && (j !== 0 || k > this.x) && bool) {
k -= this.x - j;
j = this.x;
} else if (k > this.x || j === 0) {
j = k;
k -= j;
} else if (k === 0) {
k = this.y;
}
measure = [j, k];
mvCount += 1;
bool = !bool;
solve() {
this.first.empty();
this.second.empty();
let moves = 0;

// fill the start bucket with the first move
this.first.fill();
moves += 1;

// optimization: if the other bucket is the right size,
// fill it immediately with the second move
if (this.second.size === this.goal) {
this.second.fill();
moves += 1;
}

return mvCount;
}
/* eslint-disable-next-line no-constant-condition */
while (true) {
if (this.first.amount === this.goal) {
return {
moves: moves,
goalBucket: this.first.name,
otherBucket: this.second.amount,
};
}

smallFirst(measurements, moveCount, prBool) {
let measure = measurements;
let mvCount = moveCount;
let j = measure[0];
let k = measure[1];
let bool = prBool;

while (!this.reachedGoal(measure)) {
if (j === this.x && mvCount === 0) {
j = 0;
k = this.x;
} else if (j === 0) {
j = this.x;
} else if (j === this.x && k < this.y) {
const tempK = k;
if (k + j > this.y) {
k = this.y;
} else {
k = tempK + j;
}

if (tempK + j > this.y) {
j -= this.y - tempK;
} else {
j = 0;
}
} else if (k === this.y) {
k = 0;
} else if (k === 0 && j < this.x) {
k = j;
j = 0;
if (this.second.amount === this.goal) {
return {
moves: moves,
goalBucket: this.second.name,
otherBucket: this.first.amount,
};
}
measure = [j, k];
mvCount += 1;
bool = !bool;
}

return mvCount;
}
if (this.first.isEmpty) {
this.first.fill();
} else if (this.second.isFull) {
this.second.empty();
} else {
this.first.pourInto(this.second);
}

gcd(a, b) {
// greatest common divisor
if (!b) {
return a;
moves += 1;
}
return this.gcd(b, a % b);
}
}

moves() {
// j will be running val of bucket one, k = running val of bucket two
let j = 0;
let k = 0;

// if the goal is not a multiple of the gcd of bucket one and bucket two,
// or the goal is bigger than both buckets,
// the solution will be impossible.
if (
this.z % this.gcd(this.x, this.y) !== 0 ||
(this.z > this.x && this.z > this.y)
) {
throw new Error('Cannot reach the goal.');
}

if (this.starter === 'one') {
j = this.x;
} else {
k = this.y;
}
class Bucket {
constructor(name, size) {
this.name = name;
this.size = size;
this.amount = 0;
}

const measurements = [j, k];
let moveCount = 0;
// pour / receive boolean - need to pour or receive every other turn
const prBool = true;
// accessors
get available() {
return this.size - this.amount;
}
get isFull() {
return this.amount === this.size;
}
get isEmpty() {
return this.amount === 0;
}

if (this.starter === 'one') {
moveCount = this.smallFirst(measurements, moveCount, prBool);
} else {
moveCount = this.bigFirst(measurements, moveCount, prBool);
}
fill() {
this.amount = this.size;
}
empty() {
this.amount = 0;
}

// accounts for first move made before loop (and moveCount starts at zero before loop)
return moveCount + 1;
pourInto(other) {
const quantity = Math.min(this.amount, other.available);
this.amount -= quantity;
other.amount += quantity;
}
}

const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
10 changes: 1 addition & 9 deletions exercises/practice/two-bucket/two-bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,7 @@ export class TwoBucket {
throw new Error('Remove this statement and implement this function');
}

moves() {
throw new Error('Remove this statement and implement this function');
}

get goalBucket() {
throw new Error('Remove this statement and implement this function');
}

get otherBucket() {
solve() {
throw new Error('Remove this statement and implement this function');
}
}
Loading