In [48]:
from collections import namedtuple
from queue import Queue
import itertools
import copy
from functools import reduce
import operator
In [19]:
BucketGameState = namedtuple('BucketGameState', ['bucketCapacityTuple', 'pathToPresentTuple'])
In [20]:
def genarateNextMoves(presentBucketState, bucketCapacities):
assert len(presentBucketState) == len(bucketCapacities)
assert all(present <= capacity for present, capacity in zip(presentBucketState, bucketCapacities))
validNextBucketStates = set()
# fill up any bucket from Universe
# empty any bucket to Universe
for ind,capacity in enumerate(bucketCapacities):
nextBucketState = list(presentBucketState)
nextBucketState[ind] = capacity
validNextBucketStates.add(tuple(nextBucketState))
assert all(present <= capacity for present, capacity in zip(nextBucketState, bucketCapacities))
nextBucketState[ind] = 0
validNextBucketStates.add(tuple(nextBucketState))
assert all(present <= capacity for present, capacity in zip(nextBucketState, bucketCapacities))
# transfer contents from b1 to b2 till b1 empty
# transfer contents from b1 to b2 till b2 full{}
numBucket = len(bucketCapacities)
for b1,b2 in itertools.permutations(range(numBucket),2):
spaceInb2 = bucketCapacities[b2] - presentBucketState[b2]
if spaceInb2>=presentBucketState[b1]:
nextBucketState = list(presentBucketState)
nextBucketState[b2] = presentBucketState[b2] + presentBucketState[b1]
nextBucketState[b1] = 0
assert sum(nextBucketState)==sum(presentBucketState)
assert all(present <= capacity for present, capacity in zip(nextBucketState, bucketCapacities))
validNextBucketStates.add(tuple(nextBucketState))
elif spaceInb2<presentBucketState[b1]:
nextBucketState = list(presentBucketState)
nextBucketState[b2] = bucketCapacities[b2]
nextBucketState[b1] = presentBucketState[b1] - spaceInb2
assert sum(nextBucketState)==sum(presentBucketState)
assert all(present <= capacity for present, capacity in zip(nextBucketState, bucketCapacities))
validNextBucketStates.add(tuple(nextBucketState))
return validNextBucketStates
In [21]:
pbs = (0,0,0)
bc = (2,3,4)
print(genarateNextMoves(pbs, bc))
pbs = (1,1,1)
bc = (2,3,4)
print(genarateNextMoves(pbs, bc))
pbs = (0,0,4)
bc = (2,3,4)
genarateNextMoves(pbs, bc)
Out[21]:
In [52]:
class BucketGame:
def __init__(self, bucketCapacity, targetVolume):
self.__bucketCapacityTuple = tuple(bucketCapacity)
self.__targetVolume = targetVolume
self.__exploredBucketStates = set()
self.__statesToExploreQueue = Queue()
self.__solution = None
def __isSolved(self, bucketState):
return sum(bucketState) == self.__targetVolume
def solve(self):
if sum(self.__bucketCapacityTuple) < self.__targetVolume:
return None
if any(bucketCapacity<=0 for bucketCapacity in self.__bucketCapacityTuple):
return None
firstState = BucketGameState(bucketCapacityTuple=tuple([0]*len(self.__bucketCapacityTuple)), pathToPresentTuple=[])
self.__statesToExploreQueue.put_nowait(firstState)
while self.__statesToExploreQueue.qsize()>0 and self.__solution is None:
self.__solve()
return self.__solution
def __solve(self):
assert self.__statesToExploreQueue.qsize()>0
presentState = self.__statesToExploreQueue.get_nowait()
presentBucketState = presentState.bucketCapacityTuple
if presentBucketState in self.__exploredBucketStates:
return
elif self.__isSolved(presentBucketState):
self.__solution = presentState
return
else:
self.__exploredBucketStates.add(presentBucketState)
pathToNextTuple = copy.deepcopy(presentState.pathToPresentTuple)
pathToNextTuple.append(presentBucketState)
futureBucketStates = genarateNextMoves(presentBucketState, self.__bucketCapacityTuple)
for futureBucketState in futureBucketStates - self.__exploredBucketStates:
futureState = BucketGameState(bucketCapacityTuple=tuple(futureBucketState),
pathToPresentTuple=pathToNextTuple)
self.__statesToExploreQueue.put_nowait(futureState)
In [59]:
bucketCapacities = [7,11]
targetVolume = 6
bg = BucketGame(bucketCapacities, targetVolume)
solution = bg.solve()
pathLength = len(solution.pathToPresentTuple)
print(solution)
In [54]:
possibleStates = reduce(operator.mul, (i+1 for i in bucketCapacities))
exploredStates = len(bg._BucketGame__exploredBucketStates)
print("possibleStates = ",possibleStates)
print("exploredStates = ",exploredStates)
In [66]:
bucketCapacities = [7,11]
for targetVolume in range(sum(bucketCapacities)+1):
bg = BucketGame(bucketCapacities, targetVolume)
solution = bg.solve()
pathLength = len(set(solution.pathToPresentTuple+[solution.bucketCapacityTuple]))-1 if solution is not None else None
print(targetVolume,pathLength)
In [67]:
bucketCapacities = [2,4]
for targetVolume in range(sum(bucketCapacities)+1):
bg = BucketGame(bucketCapacities, targetVolume)
solution = bg.solve()
pathLength = len(set(solution.pathToPresentTuple+[solution.bucketCapacityTuple]))-1 if solution is not None else None
print(targetVolume,pathLength)
In [ ]: