I'm trying to implement a I.A. breadth-first-search-like algorithm to solve the Water Jugs problem but I encountered this problem:
Every time I add a new element to the array, it changes all elements inside the array to be like it.
In other words...
The "frontier" array is changing all elements inside it between each "jug" functions calls.
Could someone share some insight about this code?
(I was more worried about implementing the logic, so it isn't optimized)
The code:
J1SIZE = 3
J2SIZE = 4
frontier = []
explored = []
def IsEmpty(array):
result = False
if len(array) == 0:
result = True
return result
#Fill up a jug
def fillJug(tempNode, jug):
copyNode = tempNode
copyNode = copyNode
if jug == 1:
copyNode[0] = J1SIZE
elif jug == 2:
copyNode[1] = J2SIZE
print(copyNode)
return copyNode
#Empty a jug
def emptyJug(tempNode, jug):
copyNode = tempNode
if jug == 1:
copyNode[0] = 0
elif jug == 2:
copyNode[1] = 0
print(copyNode)
return copyNode
#Pass the content of a jug to another
def passJug(tempNode, jug):
copyNode = tempNode
if jug == 1:
copyNode[1] = copyNode.j1
copyNode[0] = 0
if copyNode[1] > J2SIZE:
copyNode[1] = J2SIZE
elif jug == 2:
copyNode[0] = copyNode[1]
copyNode[1] = 0
if copyNode[0] > J1SIZE:
copyNode[0] = J1SIZE
print(copyNode)
return copyNode
#Check if solution was found
def goalTest(goalNode,currentNode):
result = False
if goalNode == currentNode:
result = True
return result
def BFS(start,end,frontier,explored):
start_node = start
frontier = [start_node] + frontier
if goalTest(end,(frontier[0])):
print('Solution:', frontier[0])
exit()
#Loop start
while IsEmpty(frontier) == False:
tempNode = frontier.pop(0)
#Generate nodes (states)
if tempNode[0] < J1SIZE:
newNode = fillJug(tempNode, 1)
if newNode not in explored:
frontier = [newNode] + frontier
print('Frontier1: ', frontier)
if tempNode[1] < J2SIZE:
newNode = fillJug(tempNode, 2)
if newNode not in explored:
frontier = [newNode] + frontier
print('Frontier2: ', frontier)
if tempNode[0] == J1SIZE:
newNode = emptyJug(tempNode, 1)
if newNode not in explored:
frontier = [newNode] + frontier
print('Frontier3: ', frontier)
if tempNode[1] == J2SIZE:
newNode = emptyJug(tempNode, 2)
if newNode not in explored:
frontier = [newNode] + frontier
print('Frontier4: ', frontier)
if (tempNode[0] > 0) and (tempNode[1] < J2SIZE):
newNode = passJug(tempNode, 1)
if newNode not in explored:
frontier = [newNode] + frontier
print('Frontier5: ', frontier)
if (tempNode[1] > 0) and (tempNode[0] < J1SIZE):
new_node = passJug(tempNode, 2)
if new_node not in explored:
frontier = [new_node] + frontier
print('Frontier6: ', frontier)
print('Frontier: ', frontier)
print('Explored: ', explored)
for node in frontier:
if goalTest(end, node):
print('Solution:', frontier[0])
exit()
if node not in explored:
explored = [node] + explored
BFS([0,0],[0,2],frontier,explored)
The fixed code:
I've made a smaller code example that reproduces your error:
J1SIZE = 3
J2SIZE = 4
def fillJug(tempNode, jug):
copyNode = tempNode
copyNode = copyNode
if jug == 1:
copyNode[0] = J1SIZE
elif jug == 2:
copyNode[1] = J2SIZE
return copyNode
frontier = [[0, 0]]
tempNode = frontier.pop(0)
newNode = fillJug(tempNode, 1)
frontier = [newNode] + frontier # (1)
newNode = fillJug(tempNode, 2) # (2)
frontier = [newNode] + frontier
The problem is that in the line I've marked (1)
, you are setting newNode
, which refers to the same object as tempNode
to be the first item in frontier
. Then, in the line I've marked (2)
, you are changing tempNode
-- inside fillJug
. So the value in frontier
changes. Specifically, you are having copyNode
refer to the same object as tempNode
, and then you are changing the data to which copyNode
(and tempNode
) refer.
You should redefine copyNode
as
copyNode = tempNode[:]
This will cause copyNode
and tempNode
to refer to different objects in memory (with the same values). (Another option is to set copyNode
to copy.copy(tempNode)
, after importing copy
.)
So the new fillJug
would be
def fillJug(tempNode, jug):
copyNode = tempNode[:]
if jug == 1:
copyNode[0] = J1SIZE
elif jug == 2:
copyNode[1] = J2SIZE
return copyNode
The same fix applies to the other Jug
functions.