-
Notifications
You must be signed in to change notification settings - Fork 0
/
ex23 - B - measure water (explore all cases).py
200 lines (152 loc) · 6.13 KB
/
ex23 - B - measure water (explore all cases).py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""
There are 3 buckets available for user to measure water with, 3 L, 5 L, and 8 L.
None of them has any indicater / marks to tell current level of water inside the bucket.
We had filled up 8 L bucket with waters and would like split it equally between 2 people
"""
from dataclasses import dataclass
from collections import deque
from copy import deepcopy
from enum import Enum
class CAPACITY(Enum):
THREE_L = 3
FIVE_L = 5
EIGHT_L = 8
class Bucket:
def __init__(self, capacity, water=0):
self.capacity = capacity
self.water = water
def __str__(self):
return f"{self.capacity}L bucket with {self.water} water"
def __eq__(self, other):
return self.capacity == other.capacity and self.water == other.water
def is_full(self):
return self.capacity == self.water
def has_water(self):
return self.water > 0
def remain_capacity(self):
return self.capacity - self.water
def add_water(self, water):
available_space = self.remain_capacity()
if available_space > 0:
self.water += water if available_space > water else available_space
def dump_water(self, water):
if self.water >= water:
self.water -= water
else:
self.water = 0
@dataclass
class Action:
poll_from: CAPACITY
add_to: CAPACITY
amount_of_water: int
def __repr__(self):
return f"poll from: {self.poll_from.value}L, add to: {self.add_to.value}L, water amount: {self.amount_of_water}"
class BucketState:
def __init__(self):
self.three_L_bucket = Bucket(capacity=CAPACITY.THREE_L.value)
self.five_L_bucket = Bucket(capacity=CAPACITY.FIVE_L.value)
self.eight_L_bucket = Bucket(
capacity=CAPACITY.EIGHT_L.value, water=CAPACITY.EIGHT_L.value
)
self.action = None
def __eq__(self, other):
return (
self.three_L_bucket.water == other.three_L_bucket.water
and self.five_L_bucket.water == other.five_L_bucket.water
and self.eight_L_bucket.water == other.eight_L_bucket.water
)
def get_bucket_by_capacity(self, bucket_capacity: CAPACITY):
switcher = {
CAPACITY.EIGHT_L: self.eight_L_bucket,
CAPACITY.FIVE_L: self.five_L_bucket,
CAPACITY.THREE_L: self.three_L_bucket,
}
return switcher.get(bucket_capacity, "Unknow capacity")
def get_current_state(self):
return (
self.eight_L_bucket.water,
self.five_L_bucket.water,
self.three_L_bucket.water,
)
def is_final_state(self):
return (
self.eight_L_bucket.water == 4
and self.five_L_bucket.water == 4
and self.three_L_bucket.water == 0
)
def is_water_can_move_between(
self, poll_from_bucket_capacity: CAPACITY, add_to_bucket_capacity: CAPACITY
):
poll_from = self.get_bucket_by_capacity(poll_from_bucket_capacity)
add_to = self.get_bucket_by_capacity(add_to_bucket_capacity)
return (
not poll_from == add_to and poll_from.has_water() and not add_to.is_full()
)
def set_action(self, poll_from, add_to, amount_of_water):
self.action = Action(poll_from, add_to, amount_of_water)
def take_action(
self, poll_from_bucket_capacity: CAPACITY, add_to_bucket_capcity: CAPACITY
):
poll_from = self.get_bucket_by_capacity(poll_from_bucket_capacity)
add_to = self.get_bucket_by_capacity(add_to_bucket_capcity)
if poll_from.has_water() and not add_to.is_full():
dump_water = (
add_to.remain_capacity()
if poll_from.water > add_to.remain_capacity()
else poll_from.water
)
add_to.add_water(dump_water)
poll_from.dump_water(dump_water)
self.set_action(
poll_from_bucket_capacity, add_to_bucket_capcity, dump_water
)
def get_water_quality_for_visual_validation(buckets, action):
def get_index_by_capacity(capacity):
switcher = {CAPACITY.EIGHT_L: 0, CAPACITY.FIVE_L: 1, CAPACITY.THREE_L: 2}
return switcher.get(capacity, "Invalid capacity")
poll_from = get_index_by_capacity(action.poll_from)
add_to = get_index_by_capacity(action.add_to)
buckets[poll_from] -= action.amount_of_water
buckets[add_to] += action.amount_of_water
def is_processed_state(states: [deque], current_state: deque):
for previous_state in states:
if previous_state == current_state:
return True
return False
def way_to_move_waters():
for bucket_one in CAPACITY:
for bucket_two in CAPACITY:
yield bucket_one, bucket_two
def explore_next_move(current_state: BucketState):
for poll_from_bucket, add_to_bucket in way_to_move_waters():
if current_state.is_water_can_move_between(poll_from_bucket, add_to_bucket):
new_bucket_state = deepcopy(current_state)
new_bucket_state.take_action(poll_from_bucket, add_to_bucket)
yield new_bucket_state
def search_solution(states: deque):
current_state = states[-1]
if current_state.is_final_state():
print(" --- Solutions ---")
visual_validation_bucket = [8, 0, 0]
for state in states:
action = state.action
if action:
get_water_quality_for_visual_validation(
visual_validation_bucket, action
)
print(
f"poll {action.amount_of_water}L of water from '{action.poll_from}L bucket' to '{action.add_to}L bucket' -> {visual_validation_bucket}"
)
else:
print(f"water start with 8L, 5L, 3L -> '{visual_validation_bucket}'")
# trying to use DFS to explore all solutions
for next_move in explore_next_move(current_state):
if not is_processed_state(states, next_move):
states.append(next_move)
search_solution(states)
states.pop()
if __name__ == "__main__":
init_state = BucketState()
states = deque()
states.append(init_state)
search_solution(states)