Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
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;
}

reachedGoal(measurements) {
const j = measurements[0];
const k = measurements[1];

if (j === this.z || k === this.z) {
if (j === this.z) {
this.goalBucket = 'one';
this.otherBucket = k;
} else {
this.goalBucket = 'two';
this.otherBucket = j;
}
constructor(size1, size2, goal, start) {
this.goal = goal;
this.buckets = [new Bucket('one', size1), new Bucket('two', size2)];

return true;
if (start === 'two') {
this.buckets.reverse();
}

return false;
this.validate();
}

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

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

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;
}
measure = [j, k];
mvCount += 1;
bool = !bool;
validate() {
if (this.goal > Math.max(this.first.size, this.second.size)) {
throw new Error('Goal is bigger than the largest bucket.');
}

return mvCount;
}

gcd(a, b) {
// greatest common divisor
if (!b) {
return a;
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.'
);
}
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.');
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;
}

if (this.starter === 'one') {
j = this.x;
} else {
k = this.y;
}
/* 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,
};
}

if (this.second.amount === this.goal) {
return {
moves: moves,
goalBucket: this.second.name,
otherBucket: this.first.amount,
};
}

const measurements = [j, k];
let moveCount = 0;
// pour / receive boolean - need to pour or receive every other turn
const prBool = true;
if (this.first.isEmpty) {
this.first.fill();
} else if (this.second.isFull) {
this.second.empty();
} else {
this.first.pourInto(this.second);
}

if (this.starter === 'one') {
moveCount = this.smallFirst(measurements, moveCount, prBool);
} else {
moveCount = this.bigFirst(measurements, moveCount, prBool);
moves += 1;
}
}
}

class Bucket {
constructor(name, size) {
this.name = name;
this.size = size;
this.amount = 0;
}

// accounts for first move made before loop (and moveCount starts at zero before loop)
return moveCount + 1;
// accessors
get available() {
return this.size - this.amount;
}
get isFull() {
return this.amount === this.size;
}
get isEmpty() {
return this.amount === 0;
}

fill() {
this.amount = this.size;
}
empty() {
this.amount = 0;
}

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));
8 changes: 0 additions & 8 deletions exercises/practice/two-bucket/two-bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,4 @@ export class TwoBucket {
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() {
throw new Error('Remove this statement and implement this function');
}
}
62 changes: 31 additions & 31 deletions exercises/practice/two-bucket/two-bucket.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ describe('TwoBucket', () => {
// indicates which bucket to fill first
const starterBuck = 'one';
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
const result = twoBucket.moves();
// includes the first fill
expect(twoBucket.moves()).toEqual(4);
expect(result.moves).toEqual(4);
// which bucket should end up with the desired # of liters
expect(twoBucket.goalBucket).toEqual('one');
expect(result.goalBucket).toEqual('one');
// leftover value in the "other" bucket once the goal has been reached
expect(twoBucket.otherBucket).toEqual(5);
expect(result.otherBucket).toEqual(5);
});

xtest('start with bucket two', () => {
const starterBuck = 'two';
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
expect(twoBucket.moves()).toEqual(8);
expect(twoBucket.goalBucket).toEqual('two');
expect(twoBucket.otherBucket).toEqual(3);
const result = twoBucket.moves();
expect(result.moves).toEqual(8);
expect(result.goalBucket).toEqual('two');
expect(result.otherBucket).toEqual(3);
});
});

Expand All @@ -35,35 +37,39 @@ describe('TwoBucket', () => {
xtest('start with bucket one', () => {
const starterBuck = 'one';
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
expect(twoBucket.moves()).toEqual(14);
expect(twoBucket.goalBucket).toEqual('one');
expect(twoBucket.otherBucket).toEqual(11);
const result = twoBucket.moves();
expect(result.moves).toEqual(14);
expect(result.goalBucket).toEqual('one');
expect(result.otherBucket).toEqual(11);
});

xtest('start with bucket two', () => {
const starterBuck = 'two';
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
expect(twoBucket.moves()).toEqual(18);
expect(twoBucket.goalBucket).toEqual('two');
expect(twoBucket.otherBucket).toEqual(7);
const result = twoBucket.moves();
expect(result.moves).toEqual(18);
expect(result.goalBucket).toEqual('two');
expect(result.otherBucket).toEqual(7);
});
});

describe('Measure one step using bucket one of size 1 and bucket two of size 3', () => {
xtest('start with bucket two', () => {
const twoBucket = new TwoBucket(1, 3, 3, 'two');
expect(twoBucket.moves()).toEqual(1);
expect(twoBucket.goalBucket).toEqual('two');
expect(twoBucket.otherBucket).toEqual(0);
const result = twoBucket.moves();
expect(result.moves).toEqual(1);
expect(result.goalBucket).toEqual('two');
expect(result.otherBucket).toEqual(0);
});
});

describe('Measure using bucket one of size 2 and bucket two of size 3', () => {
xtest('start with bucket one and end with bucket two', () => {
const twoBucket = new TwoBucket(2, 3, 3, 'one');
expect(twoBucket.moves()).toEqual(4);
expect(twoBucket.goalBucket).toEqual('two');
expect(twoBucket.otherBucket).toEqual(1);
const result = twoBucket.moves();
expect(result.moves).toEqual(2);
expect(result.goalBucket).toEqual('two');
expect(result.otherBucket).toEqual(2);
});
});

Expand All @@ -72,33 +78,27 @@ describe('TwoBucket', () => {
const buckTwo = 15;

xtest('Not possible to reach the goal, start with bucket one', () => {
const starterBuck = 'one';
const goal = 5;
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
expect(() => twoBucket.moves()).toThrow();
expect(() => new TwoBucket(buckOne, buckTwo, 5, 'one')).toThrow();
});

xtest('Not possible to reach the goal, start with bucket two', () => {
const starterBuck = 'two';
const goal = 5;
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
expect(() => twoBucket.moves()).toThrow();
expect(() => new TwoBucket(buckOne, buckTwo, 5, 'two')).toThrow();
});

xtest('With the same buckets but a different goal, then it is possible', () => {
const starterBuck = 'one';
const goal = 9;
const twoBucket = new TwoBucket(buckOne, buckTwo, goal, starterBuck);
expect(twoBucket.moves()).toEqual(10);
expect(twoBucket.goalBucket).toEqual('two');
expect(twoBucket.otherBucket).toEqual(0);
const result = twoBucket.moves();
expect(result.moves).toEqual(10);
expect(result.goalBucket).toEqual('two');
expect(result.otherBucket).toEqual(0);
});
});

describe('Goal larger than both buckets', () => {
xtest('Is impossible', () => {
const twoBucket = new TwoBucket(5, 7, 8, 'one');
expect(() => twoBucket.moves()).toThrow();
expect(() => new TwoBucket(5, 7, 8, 'one')).toThrow();
});
});
});