Maximum recursion depth exceeded in dfs using recursion in python - python

I have written a python code to solve the missionaries and cannibals problem using recursive dfs in python. However I keep getting this error:
RecursionError: maximum recursion depth exceeded
I have no idea what to do about it, and I have been stuck at it for so long.
Any help or suggestion will be life saving for me. Thanks.
Here is the code:
class State(object):
#left = 1
#right = 0 for boat
def __init__(self, missionaries, cannibals, boat):
self.missionaries = missionaries
self.cannibals = cannibals
self.boat = boat
#def __str__(self):
# return "%s, %s %s %s" % (self.by_move, self.missionaries, self.cannibals, self.boat)
def is_valid(self):
if self.missionaries < 0 or self.missionaries > 3:
return False
if self.cannibals < 0 or self.cannibals > 3:
return False
if self.boat > 1 or self.boat < 0:
return False
if self.missionaries < self.cannibals and self.missionaries > 0:
return False
# Check for the other side
if self.missionaries > self.cannibals and self.missionaries < 3:
return False
return True
def is_goal(self):
return self.missionaries == 0 and self.cannibals == 0 and self.boat == 0
def new_states(self):
op = -1 # Subtract
boat_move = "from left shore to right"
if self.boat == 0:
op = 1 # Add
boat_move = "from right shore to left"
for x in range(3):
for y in range(3):
by_move = "Move %s missionaries and %s cannibals %s" % (x, y, boat_move)
new_state = State(self.missionaries + op * x, self.cannibals + op * y, self.boat + op * 1)
if x + y >= 1 and x + y <= 2 and new_state.is_valid():
yield new_state
class Node(object):
def __init__(self, parent, state, depth):
self.parent = parent
self.state = state
self.depth = depth
def children(self):
for state in self.state.new_states():
yield Node(parent=self, state=state, depth=self.depth + 1)
def extract_solution(self):
print
"Extracting soln"
solution = []
node = self
solution.append(node)
while node.parent is not None:
solution.append(node.parent)
node = node.parent
solution.reverse()
return solution
def dfs(root,visited,sol = None):
if root in visited:
return
if root is None:
return
visited.append(root)
if root.state.is_goal():
sol = root
return
for child in root.children():
if child not in visited:
dfs(child,visited,sol)
def main():
initial_state = State(3,3,1)
root = Node(parent = None, state = initial_state,depth = 0)
visited = []
sol = Node(parent = None, state = initial_state,depth = 0)
dfs(root,visited,sol)
ans = sol.extract_solution()
print(ans)
if __name__ == '__main__':
main()

There were two issues:
1: your list 'visited' didn't properly keep track of all the states. This can easily be fixed by making visited a global variable (by putting it in front of the def main() as done in the final solution)
2: The program was searching possibilities that weren't going to ever help (eg: bringing the same guy back and forth), this
if root in visited:
return
if root is None:
return
didn't solve this because it's never the same root object (even if the root.state.missionaries, cannibals and boat are the same value), so I changed this using a dictionary object:
if root is None:
return
state = str(root.state.missionaries) + ',' + str(root.state.cannibals) + ',' + str(root.state.boat)
if state in routes:
if routes[state] < root.depth:
return
else:
routes[state] = root.depth
else:
routes[state] = root.depth
visited.append(root)
This results in the following code (it returns an answer, I'm not sure if it's the correct one because I don't know the missionaries and cannibals problem)
class State(object):
#left = 1
#right = 0 for boat
def __init__(self, missionaries, cannibals, boat):
self.missionaries = missionaries
self.cannibals = cannibals
self.boat = boat
#def __str__(self):
# return "%s, %s %s %s" % (self.by_move, self.missionaries, self.cannibals, self.boat)
def is_valid(self):
if self.missionaries < 0 or self.missionaries > 3:
return False
if self.cannibals < 0 or self.cannibals > 3:
return False
if self.boat > 1 or self.boat < 0:
return False
if self.missionaries < self.cannibals and self.missionaries > 0:
return False
# Check for the other side
if self.missionaries > self.cannibals and self.missionaries < 3:
return False
return True
def is_goal(self):
return self.missionaries == 0 and self.cannibals == 0 and self.boat == 0
def new_states(self):
op = -1 # Subtract
boat_move = "from left shore to right"
if self.boat == 0:
op = 1 # Add
boat_move = "from right shore to left"
for x in range(3):
for y in range(3):
by_move = "Move %s missionaries and %s cannibals %s" % (x, y, boat_move)
new_state = State(self.missionaries + op * x, self.cannibals + op * y, self.boat + op * 1)
if x + y >= 1 and x + y <= 2 and new_state.is_valid():
yield new_state
class Node(object):
def __init__(self, parent, state, depth):
self.parent = parent
self.state = state
self.depth = depth
def children(self):
for state in self.state.new_states():
yield Node(parent=self, state=state, depth=self.depth + 1)
def extract_solution(self):
print "Extracting soln"
solution = []
node = self
solution.append(node)
while node.parent is not None:
solution.append(node.parent)
node = node.parent
solution.reverse()
return solution
def dfs(root,sol = None):
if root is None:
return
state = str(root.state.missionaries) + ',' + str(root.state.cannibals) + ',' + str(root.state.boat)
if state in routes:
if routes[state] < root.depth:
return
else:
routes[state] = root.depth
else:
routes[state] = root.depth
visited.append(root)
if root.state.is_goal():
sol = root
return
for child in root.children():
if child not in visited:
dfs(child,sol)
visited = []
routes = {}
def main():
initial_state = State(3,3,1)
root = Node(parent = None, state = initial_state,depth = 0)
sol = Node(parent = None, state = initial_state,depth = 0)
dfs(root,sol)
ans = sol.extract_solution()
print(ans)
if __name__ == '__main__':
main()
PS. as #PM 2Ring said, for next time: please fix your indentation when asking questions, it makes reading your code easier to understand. You can do this by selecting all your code, adding a tab to all lines selected and then copying it. Before you paste it make sure there's an empty line. :)

Related

N Puzzle with Depth First Search

I'm trying solve N Puzzle with Depth First Search using python 3.
With 3 x 3 puzzle it run good and fast but with 4 x 4 puzzle, it runs too slow and can't find solution with error: "MemoryError".
I also use "h(n) = depth + number of wrong tiles" to evaluate priority of each node.
I'm a newbie to python so hope you can help me with this
Here is my code:
import sys
import getopt
import random
import time
class State:
def __init__(self, parent, board, move, depth):
self.parent = parent
self.previousMove = move
self.board = board
self.map = ''.join(str(e) for e in board)
self.depth = depth
self.cost = self.calculateCost()
def calculateCost(self):
pos = 1
count = 0
for tile in self.board:
if tile == pos:
count += 1
pos += 1
return self.depth + 8 - count
class Puzzle:
def __init__(self, k, customBoard = None):
self.k = k
self.n = k*k - 1
self.sizeOfBoard = k*k
self.timeOfSolving = 0
self.timeOfGenerateSuccessors = 0
self.maxDeepSearch = 0
self.inititalState = State(None, self.createInitialBoard(customBoard), 'Start', 0)
self.goalBoard = self.createGoalBoard()
self.finalState = None
self.stateStorage = set() # Store states that have visited
self.path = [] # Store states that lead to goalstate
self.stack = []
def isSolvable(self, board):
# count invertion in puzzle's board
invCount = 0
for i in range(0, self.sizeOfBoard - 1):
if board[i] == 0:
continue
for j in range(i+1, self.sizeOfBoard):
if board[j] == 0:
continue
if board[i] > board[j]:
invCount += 1
# print(invCount)
if (invCount % 2 == 0):
return True
return False
def createInitialBoard(self, customBoard):
print("Creating initial state")
if customBoard is None:
board = []
lstAddSuccess = []
while 1:
board.clear()
lstAddSuccess.clear()
for count in range(0, self.k*self.k):
newTile = random.randint(0, self.n)
while newTile in lstAddSuccess:
newTile = random.randint(0, self.n)
lstAddSuccess += [newTile]
board += [newTile]
if self.isSolvable(board):
break
else:
board = [int(e) for e in customBoard]
if not self.isSolvable(board):
print("Cant find solution with this puzzle! Exiting...")
exit(-1)
return board
def createGoalBoard(self):
board = []
for count in range(1, self.n + 1):
board += [count]
board += [0]
return board
def printBoard(self, board):
for row in range(0, self.sizeOfBoard, self.k):
# for col in range(row, row + self.k):
print(board[row:row + self.k])
def generateSuccessors(self, currentState):
indexOfZero = currentState.board.index(0)
rowIndexOfZero = indexOfZero % self.k
colIndexOfZero = indexOfZero // self.k
lstSuccessors = []
# Slide to zero to up
if colIndexOfZero != 0:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero - self.k]
newState[indexOfZero - self.k] = 0
lstSuccessors.append(
State(currentState, newState, 'up', currentState.depth + 1))
# Slide zero to down
if colIndexOfZero != self.k - 1:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero + self.k]
newState[indexOfZero + self.k] = 0
lstSuccessors.append(
State(currentState, newState, 'down', currentState.depth + 1))
# slide zero to left
if rowIndexOfZero != 0:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero - 1]
newState[indexOfZero - 1] = 0
lstSuccessors.append(
State(currentState, newState, 'left', currentState.depth + 1))
# Slide zero to right
if rowIndexOfZero != self.k - 1:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero + 1]
newState[indexOfZero + 1] = 0
lstSuccessors.append(
State(currentState, newState, 'right', currentState.depth + 1))
lstSuccessorsCost = [ele.cost for ele in lstSuccessors]
lstSuccessorsInOrderOfCost = []
for i in range(0, len(lstSuccessorsCost)):
lstSuccessorsInOrderOfCost.append(lstSuccessors[lstSuccessorsCost.index(min(lstSuccessorsCost))])
lstSuccessorsCost[lstSuccessorsCost.index(min(lstSuccessorsCost))] = 100
return lstSuccessorsInOrderOfCost
def solvePuzzle(self, currentState):
self.stack.append(currentState)
self.stateStorage.add(currentState.map)
while len(self.stack) > 0:
currentState = self.stack.pop()
if currentState.board == self.goalBoard:
# find path
# self.printBoard(currentState.board)
self.finalState = currentState
print("Solving " + str(self.n) + " puzzle done!")
return
start_time_gen = time.time()
lstSuccessor = self.generateSuccessors(currentState)
end_time_gen = time.time()
timeOfGen = end_time_gen - start_time_gen
self.timeOfGenerateSuccessors += timeOfGen
for successor in lstSuccessor[::-1]:
if successor.map not in self.stateStorage:
self.stack.append(successor)
self.stateStorage.add(successor.map)
if successor.depth > self.maxDeepSearch:
self.maxDeepSearch += 1
print("Cant solve puzzle! Exiting...")
exit(-1)
def solve(self):
start_time = time.time()
self.solvePuzzle(self.inititalState)
end_time = time.time()
self.timeOfSolving = end_time - start_time
print("Running time: " + str(self.timeOfSolving))
print("Max Search Dept: " + str(self.maxDeepSearch))
print("Final State Dept: " + str(self.finalState.depth))
def printInitialBoard(self):
self.printBoard(self.inititalState.board)
def printPath(self):
if self.finalState is None:
print("No solution found!")
return
path = []
state = self.finalState
while (state is not None):
if state.previousMove is not None:
path.append(state.previousMove)
state = state.parent
print("path: "),
print(path[::-1])
def main(argv):
# if (len(argv) != 1 or int(argv[0]) not in range(1, 10000)):
# print("Input must be k of integer, which is k*k matrix of puzzle")
# exit()
# eight_puzzle = Puzzle(int(argv[0]))
k = int(input("Enter size of k * k puzzle, k = "))
while k not in range(2, 100):
print("k must be in range 2 - 100")
k = int(input("Enter size of k * k puzzle, k = "))
print("""
Choose:
1. Randome puzzle
2. Custome puzzle
""")
file = input()
if int(file) == 1:
puzzle = Puzzle(k)
elif int(file) == 2:
board = input("Enter puzzle: ")
puzzle = Puzzle(k ,list(board.split(" ")))
puzzle.printInitialBoard()
puzzle.solve()
puzzle.printPath()
if __name__ == "__main__":
main(sys.argv[1:])

Python 8-puzzle IDDFS with results greater than BFS

My Iterative Deepening Depth-First Search (IDDFS) of the 8 puzzle game returns a path length greater than my BFS. The total number of visited Nodes is 42 for the IDDFS while my BFS returns a total of 26. Is there something wrong with my IDDFS algorithm or is that just how it should behave???
import collections
import queue
import time
import itertools
class Node:
def __init__(self, puzzle, last=None):
self.puzzle = puzzle
self.last = last
#property
def seq(self): # to keep track of the sequence used to get to the goal
node, seq = self, []
while node:
seq.append(node)
node = node.last
yield from reversed(seq)
#property
def state(self):
return str(self.puzzle.board) # hashable so it can be compared in sets
#property
def isSolved(self):
return self.puzzle.isSolved
#property
def getMoves(self):
return self.puzzle.getMoves
class Puzzle:
def __init__(self, startBoard):
self.board = startBoard
#property
def getMoves(self):
possibleNewBoards = []
zeroPos = self.board.index(0) # find the zero tile to determine possible moves
if zeroPos == 0:
possibleNewBoards.append(self.move(0,1))
possibleNewBoards.append(self.move(0,3))
elif zeroPos == 1:
possibleNewBoards.append(self.move(1,0))
possibleNewBoards.append(self.move(1,2))
possibleNewBoards.append(self.move(1,4))
elif zeroPos == 2:
possibleNewBoards.append(self.move(2,1))
possibleNewBoards.append(self.move(2,5))
elif zeroPos == 3:
possibleNewBoards.append(self.move(3,0))
possibleNewBoards.append(self.move(3,4))
possibleNewBoards.append(self.move(3,6))
elif zeroPos == 4:
possibleNewBoards.append(self.move(4,1))
possibleNewBoards.append(self.move(4,3))
possibleNewBoards.append(self.move(4,5))
possibleNewBoards.append(self.move(4,7))
elif zeroPos == 5:
possibleNewBoards.append(self.move(5,2))
possibleNewBoards.append(self.move(5,4))
possibleNewBoards.append(self.move(5,8))
elif zeroPos == 6:
possibleNewBoards.append(self.move(6,3))
possibleNewBoards.append(self.move(6,7))
elif zeroPos == 7:
possibleNewBoards.append(self.move(7,4))
possibleNewBoards.append(self.move(7,6))
possibleNewBoards.append(self.move(7,8))
else:
possibleNewBoards.append(self.move(8,5))
possibleNewBoards.append(self.move(8,7))
return possibleNewBoards # returns Puzzle objects (maximum of 4 at a time)
def move(self, current, to):
changeBoard = self.board[:] # create a copy
changeBoard[to], changeBoard[current] = changeBoard[current], changeBoard[to] # switch the tiles at the passed positions
return Puzzle(changeBoard) # return a new Puzzle object
def printPuzzle(self): # prints board in 8 puzzle style
copyBoard = self.board[:]
for i in range(9):
if i == 2 or i == 5:
print((str)(copyBoard[i]))
else:
print((str)(copyBoard[i])+" ", end="")
print('\n')
#property
def isSolved(self):
return self.board == [0,1,2,3,4,5,6,7,8] # goal board
class Solver:
def __init__(self, Puzzle):
self.puzzle = Puzzle
def IDDFS(self):
def DLS(currentNode, depth):
if depth == 0:
return None
if currentNode.isSolved:
return currentNode
elif depth > 0:
for board in currentNode.getMoves:
nextNode = Node(board, currentNode)
if nextNode.state not in visited:
visited.add(nextNode.state)
goalNode = DLS(nextNode, depth - 1)
if goalNode != None: # I thought this should be redundant but it never finds a soln if I take it out
if goalNode.isSolved: # same as above ^
return goalNode
for depth in itertools.count():
visited = set()
startNode = Node(self.puzzle)
print(startNode.isSolved)
goalNode = DLS(startNode, depth)
if goalNode != None:
if goalNode.isSolved:
return goalNode.seq
startingBoard = [7,2,4,5,0,6,8,3,1]
myPuzzle = Puzzle(startingBoard)
mySolver = Solver(myPuzzle)
start = time.time()
goalSeq = mySolver.IDDFS()
end = time.time()
counter = -1 # starting state doesn't count as a move
for node in goalSeq:
counter = counter + 1
node.puzzle.printPuzzle()
print("Total number of moves: " + str(counter))
totalTime = end - start
print("Total searching time: %.2f seconds" % (totalTime))

Python: Recursion problems

I am trying to make a sudoku solver that solves boards very quickly. At the moment my solver works on easy boards but never terminates on harder boards. I believe it has something to do with my recursion because easy boards do not require recursion and hard boards do. Any help is appreciated.
import sys
def rowno(i):
return i // 9
def colno(i):
return i % 9
def boxno(i):
return (i // 9 // 3 )*3 + (i // 3) % 3
def isNeighbor(i, j):
if rowno(i) == rowno(j) or colno(i) == colno(j) or boxno(i) == boxno(j):
return True
else:
return False
def getFileName():
if sys.platform == "win32":
filename = input("Filename? ")
else:
filename = sys.argv[-1]
return filename
solutionlist = []
class Board(object):
def __init__(self, puzzle):
self.puzzle = puzzle
self.board = [Cell(int(value), idx) for idx, value in enumerate(puzzle)]
self.change = False
def printAll(self):
print [cell.candidates for cell in self.board]
#return str(" ")
def update(self):
self.change = False
l = [cell for cell in self.board if len(cell.candidates) == 1]
for i in l:
for j in xrange(81):
if isNeighbor(i.dex, j) and i.dex != j:
old = self.board[j].candidates
self.board[j].delCandidate(i.value)
if len(old) != len(self.board[j].candidates):
self.change = True
def toString(self):
str1 = ''.join(str(e.value) for e in self.board)
return str1
def solved(self):
for cell in self.board:
if len(cell.candidates) != 1:
return False
return True
def solve(self):
self.change = True
while self.change == True:
self.update()
if self.solved():
solutionlist.append(self.board)
return
l = [cell for cell in self.board if len(cell.candidates) > 1]
for i in l:
for j in i.candidates:
newBoard = Board(self.toString())
curLen = 12
curCell = -1
for u in l:
if len(u.candidates)<curLen:
curLen=len(u.candidates)
curCell = u.dex
for c in newBoard.board[curCell].candidates:
newBoard.board[curCell].candidates = [int(c)]
newBoard.board[curCell].value = int(c)
newBoard.solve()
return
def __repr__(self):
l = [cell.value for cell in self.board]
return str(l)
class Cell(object):
def __init__(self, value, dex):
self.value = value
self.dex = dex
if value == 0:
self.candidates = [1,2,3,4,5,6,7,8,9]
else:
self.candidates = [int(value)]
def __str__(self):
return str(self.value)
def delCandidate(self, value):
# deletes value from candidate list
#return self.candidate.remove(value);
self.candidates = [x for x in self.candidates if x != value]
if len(self.candidates) == 1:
self.value = self.candidates[0]
easy = "700583006006001405052006083300200958500078060648010300060802500003150072215600030"
twosol = "000805200800000401705040009000100702040000000006430000030900000010006080000000000"
hard = "040090008000000070060000120030020000005839060080600700050170600000043000003000200"
#easy solution: 794583216836721495152496783371264958529378164648915327967832541483159672215647839
b = Board(hard)
print b
b.solve()
print "end of the line"
for i in solutionlist:
print [cell.value for cell in i]
print "\n"
One major issue is the line for i in l: in the solve method. Since you're recursing, you only need to fill in one cell - the recursion will take care of the rest. So instead of for i in l:, just recurse on the one cell that is the best candidate (curCell):
l = [cell for cell in self.board if len(cell.candidates) > 1]
if len(l) > 0:
newBoard = Board(self.toString())
curLen = 12
curCell = -1
for u in l:
if len(u.candidates)<curLen:
curLen=len(u.candidates)
curCell = u.dex
for c in newBoard.board[curCell].candidates:
newBoard.board[curCell].candidates = [int(c)]
newBoard.board[curCell].value = int(c)
newBoard.solve()

Python Old object information being kept

I'm making a class of rooted Trees using a class of Nodes that have a "word" of the form [1,1,2], where [1,1] is the parent of [1,1,2] and [1,1,1] is the preceding sibling of [1,1,2], a list of children, and a parent. For some reason, in the for loop section, the second time the line nextChild = Node(word) it is taking as input one child (which is the previous nextChild) even though I am not passing in anything as the child. I have no idea why this is happening. I'll post more code if requested.
Edit: Here is the whole Tree.py file
import pdb
class Word:
def __init__(self, intList = []):
self.intList = intList
def __len__(self):
return len(self.intList)
def __getitem__(self, i):
if i < len(self):
return self.intList[i]
else:
raise AttributeError
def __str__(self):
if len(self.intList) == 0:
return "<e>"
selfStr = "<"
for i, val in enumerate(self.intList):
selfStr = selfStr + str(val)
selfStr = selfStr + ("" if i == (len(self.intList) - 1) else ", ")
selfStr = selfStr + ">"
return selfStr
def compare(self, word2):
shortestLength = len(self) if len(self) < len(word2) else len(word2)
for i in xrange(shortestLength):
if self[i] < word2[i]:
return -1
elif self[i] > word2[i]:
return 1
return -1 if len(self) < len(word2) else 1 if len(self) > len(word2) else 0
def isPrefixOf(self, word2):
if len(self) == 0 and len(word2) == 1:
return True
if len(self) != len(word2) + 1:
return False
for i in xrange(len(self)):
if self[i] != word2[i]:
return False
return True
class Node:
def __init__(self, word = Word(), children = [], parent = -1):
self.label = word
self.children = children
self.parent = parent
self.currentChild = 0
for i, child in enumerate(self.children):
if (not self.label.isPrefixOf(child.label)):
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
def __str__(self):
return str(self.label)
def addChild(self, child):
#check if these are valid brothers
if len(self.children) == 0 and child.label[len(child.label) - 1] != 1:
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
elif len(self.children) != 0 and not self.children[len(self.children) - 1].isPerviousBrotherOf(child):
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
#check if valid parent
if not self.label.isPrefixOf(child.label):
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
self.children.append(child)
def isValidParentOf(self, node):
return self.label.isPrefixOf(node.label)
def isPerviousBrotherOf(self, word2):
if len(self) != len(word2):
return False
return self[len(self) - 1] == word2[len(word2) - 1] - 1
def getParent(self):
return self.parent
def setParent(self, parent):
self.parent = parent
def getNextChild(self):
if self.currentChild >= len(self.children):
return -1
else:
self.currentChild = self.currentChild + 1
return self.children[self.currentChild - 1]
def resetPosition(self):
self.currentChild = 0
def numChildren(self):
return len(self.children)
class Tree:
def __init__(self, intList):
if len(intList) == 0:
raise ValueError("Trees cannot have size zero.")
wordList = map(lambda x: Word(x), intList)
wordList = sort(wordList)
self.root = Node(wordList[0])
currentNode = self.root
for i in xrange(1, len(wordList)):
word = wordList[i]
nextChild = Node(word)
while (currentNode != -1 and not currentNode.isValidParentOf(nextChild)):
currentNode.resetPosition()
currentNode = currentNode.getParent()
if (currentNode == -1):
raise ValueError("The list of words " + map(str, wordList) + " is not a valid tree.")
currentNode.addChild(nextChild)
nextChild.setParent(currentNode)
currentNode = currentNode.getNextChild()
while (currentNode.getParent() != -1):
currentNode.resetPosition()
currentNode = currentNode.getParent()
currentNode.resetPosition()
self.root = currentNode
self.size = len(wordList)
self.current = self.root
def __str__(self):
outStr = ""
outStr = createString(self.root)
def createString(self, node):
outStr = "(" + str(node)
child = node.getNextChild()
while child != -1:
outStr += " " + createString(child) + ")"
return outStr + ")"
def sort(inList):
if len(inList) <= 1:
return inList
return merge(sort(inList[:len(inList)/2]), sort(inList[len(inList)/2:]))
def merge(list1, list2):
outlist = []
i = 0
j = 0
while (i < len(list1) or j < len(list2)):
if i >= len(list1):
while (j < len(list2)):
outlist.append(list2[j])
j = j + 1
elif j >= len(list2):
while (i < len(list1)):
outlist.append(list1[i])
i = i + 1
elif list1[i].compare(list2[j]) == -1:
outlist.append(list1[i])
i = i + 1
else:
outlist.append(list2[j])
j = j + 1
return outlist
And here is some test code
from Tree import Tree
t = Tree([[], [1], [2], [3], [1, 1], [1, 2], [2, 1], [3, 1], [3, 2], [3, 3]])
print str(t)

Python minimax for tictactoe

After completely failing the minimax implementation for tic tac toe, I fail to see what's wrong. Right now, my AI just goes around in a circle...
import collections
class InvalidLocationError(Exception): pass
import copy
class Board(object):
def __init__(self, board=None):
if board is None:
self.clear()
else:
self._board = board[:]
def place(self, i, row, column):
if not ((0 <= row <= 2) and (0 <= column <= 2)):
raise InvalidLocationError("Invalid Location.")
if self._board[row][column]:
raise InvalidLocationError("There's already a piece there")
self._board[row][column] = i
return self.checkVictory()
def check(self, row, column):
return self._board[row][column]
def checkVictory(self, board=None):
if board is None:
board = self._board
draw = True
for i in xrange(3):
r = self.rowcount(i)
c = self.colcount(i)
if i < 3:
d = self.diagcount(i)
else:
d = {0: 0, 1: 0, 2: 0}
for j in xrange(1, 3):
if d[j] == 3 or r[j] == 3 or c[j] == 3:
return j
if r[0] > 0 or c[0] > 0:
draw = False
if draw:
return -1
return 0
def rowcount(self, row):
return collections.Counter(self._board[row])
def colcount(self, col):
return collections.Counter([self._board[i][col] for i in xrange(3)])
def diagcount(self, left=True):
if left:
a = [self._board[0][0], self._board[1][1], self._board[2][2]]
else:
a = [self._board[0][2], self._board[1][1], self._board[2][0]]
return collections.Counter(a)
def clear(self):
self._board = ([0, 0, 0], [0, 0, 0], [0, 0, 0])
def __str__(self):
return "\n".join(map(lambda x: " ".join(map(lambda y : str(y), x)), self._board))
#staticmethod
def flipPiece(p):
return int(not (p - 1)) + 1
class AI(object):
class Node(object):
def __init__(self, board, nextMove):
self.board = board
self.nextMove = nextMove
self.paths = []
self.score = None
template = self.board._board[:]
for r, row in enumerate(template):
for c, val in enumerate(row):
if val == 0:
template[r][c] = nextMove
self.paths.append(copy.deepcopy(template))
template[r][c] = 0
def __init__(self, mypiece, depth=8):
self.mypiece = mypiece
self.enemypiece = Board.flipPiece(mypiece)
self.depth = depth
def decide(self, board):
startNode = self.Node(board, self.mypiece)
best = self.minimax(startNode, self.depth)
for node in startNode.paths:
if node.value == best:
break
found = False
for row in xrange(3):
for col in xrange(3):
if board.check(row, col) != node.board.check(row, col):
found = True
break
if found:
break
print row, col
return row, col
def minimax(self, node, depth):
victory = node.board.checkVictory()
if victory:
if victory == self.mypiece:
h = 1
elif victory == -1:
h = 0
else:
h = -1
node.value = h
return h
if depth <= 0:
# h = self.heuristic(node.board, node.nextMove) # This is to the heuristic, which uses nextMove to evalate.
node.value = 0
return 0
h = -1
flip = Board.flipPiece(node.nextMove)
for i, board in enumerate(node.paths):
node.paths[i] = self.Node(Board(board), flip) # This is to the Node, which takes the nextMove of itself (which translates to the next next move from the current node)
score = self.minimax(node.paths[i], depth-1)
h = max(h, score) if node.nextMove == self.mypiece else min(h, score)
node.value = h
return h
Why is this happening?

Categories