-
-
Notifications
You must be signed in to change notification settings - Fork 632
/
Copy pathproof.ci.js
110 lines (93 loc) · 2.22 KB
/
proof.ci.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
export class TwoBucket {
constructor(size1, size2, goal, start) {
this.goal = goal;
this.buckets = [new Bucket('one', size1), new Bucket('two', size2)];
if (start === 'two') {
this.buckets.reverse();
}
this.validate();
}
get first() {
return this.buckets[0];
}
get second() {
return this.buckets[1];
}
validate() {
if (this.goal > Math.max(this.first.size, this.second.size)) {
throw new Error('Goal is bigger than the largest bucket.');
}
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.',
);
}
}
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;
}
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,
};
}
if (this.first.isEmpty) {
this.first.fill();
} else if (this.second.isFull) {
this.second.empty();
} else {
this.first.pourInto(this.second);
}
moves += 1;
}
}
}
class Bucket {
constructor(name, size) {
this.name = name;
this.size = size;
this.amount = 0;
}
// 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));