I am making a n-puzzle solver for my Artificial Intelligence class using A* in Python. The problem I have with my solution is , that the solve_puzzle() is not working as it should. While searching through the nodes it gets just deeper and deeper, but it never ends, the depth goes into really high numbers (depth of some child node is 5k for example).
I think it gets stuck in some loop and it keeps just looping through the same nodes (That is just what I think, I am not sure about that). I don't really know, I tried 3 different A* solutions, but the result is the same - the search loop never ends and never reaches the Goal.
I can solve very primitive puzzle like
start -> 0, 1, 2, 3, 4, 5, 6, 7, 8 goal -> 1, 2, 0, 3, 4, 5, 6, 7, 8 (just two moves to left are made)
but for just a little more complex puzzle like
start = 0, 1, 2, 3, 4, 5, 6, 7, 8
goal = 1, 2, 5, 0, 3, 4, 6, 7, 8 it is a never ending search.
I move the tile with number, not the blank tile.
Below is the whole code:
'''
class Node():
def __init__(self, n_size, m_size, current_state, goal_state, choosen_heuristic, parent):
self.n_size = n_size
self.m_size = m_size
self.dimension = self.n_size * self.m_size
self.current_state = current_state
self.goal_state = goal_state
self.choosen_heuristic = choosen_heuristic
self.parent = parent
self.child = [None, None, None, None]
self.last_operator = None
self.heuristic_cost = 0
self.depth = 0
self.priority = self.depth + self.heuristic_cost
def check_if_goal_is_reached(self):
if (self.heuristic_cost == 0):
print("GOAL IS REACHED!")
exit(1) #for now
#return
def get_blank_tile_position(self):
blank_position = 0
for i in range(self.dimension):
if (self.current_state[i] == 0):
blank_position = i
return blank_position
def misplaced_tiles_heuristic(self):
misplaced_sum = 0
for i in range(self.dimension):
if self.current_state[i] != self.goal_state[i]:
misplaced_sum += 1
#print("Count of misplaced tiless in this node is : ", misplaced_sum)
self.heuristic_cost = "misplaced"
self.heuristic_cost = misplaced_sum
self.check_if_goal_is_reached()
return misplaced_sum
def manhattan_distance(self):
distance_sum = 0
for i in range(self.dimension):
current_x = self.current_state[i] % self.n_size
goal_x = self.goal_state[i] % self.n_size
current_y = self.current_state[i] // self.m_size
goal_y = self.goal_state[i] // self.m_size
distance_sum += abs(current_x - goal_x) + abs(current_y - goal_y)
#print("Sum of Manhattan distance for this node is : ", distance_sum)
self.heuristic_cost = "manhattan"
#print("Hĺbka tohto uzla : ", self.depth)
self.check_if_goal_is_reached()
return distance_sum
def generate_children(self, choosen_heuristic):
possible_directions = []
current_node_blank_position = self.get_blank_tile_position()
# UP - I move a tile with number on it, not the blank tile
if current_node_blank_position < (self.dimension - self.n_size):
self.child[0] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
self.child[0] = copy.deepcopy(self)
self.child[0].parent = self.current_state
self.child[0].last_operator = "UP"
self.child[0].depth = self.depth + 1
new_blank_position = current_node_blank_position + self.m_size
temp = self.child[0].current_state[current_node_blank_position]
self.child[0].current_state[current_node_blank_position] = self.child[0].current_state[new_blank_position]
self.child[0].current_state[new_blank_position] = temp
if choosen_heuristic == "misplaced":
self.child[0].misplaced_tiles_heuristic()
if choosen_heuristic == "manhattan":
self.child[0].manhattan_distance()
possible_directions.append("UP")
print("Depth of this node is : : ", self.child[0].depth)
else:
self.child[0] = None
# DOWN - I move a tile with number on it, not the blank tile
if current_node_blank_position > (self.n_size - 1):
self.child[1] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
self.child[1] = copy.deepcopy(self)
self.child[1].parent = self.current_state
self.child[1].last_operator = "DOWN"
self.child[1].depth = self.depth + 1
new_blank_position = current_node_blank_position - self.m_size
temp = self.child[1].current_state[current_node_blank_position]
self.child[1].current_state[current_node_blank_position] = self.child[1].current_state[new_blank_position]
self.child[1].current_state[new_blank_position] = temp
if choosen_heuristic == "misplaced":
self.child[1].misplaced_tiles_heuristic()
if choosen_heuristic == "manhattan":
self.child[1].manhattan_distance()
possible_directions.append("DOWN")
#print("Depth of this node is : : ", self.child[1].depth)
else:
self.child[1] = None
# RIGHT - I move a tile with number on it, not the blank tile
if (current_node_blank_position + self.n_size) % self.m_size:
self.child[2] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
self.child[2] = copy.deepcopy(self)
self.child[2].parent = self.current_state
self.child[2].last_operator = "RIGHT"
self.child[2].depth = self.depth + 1
new_blank_position = current_node_blank_position - 1
temp = self.child[2].current_state[current_node_blank_position]
self.child[2].current_state[current_node_blank_position] = self.child[2].current_state[new_blank_position]
self.child[2].current_state[new_blank_position] = temp
if choosen_heuristic == "misplaced":
self.child[2].misplaced_tiles_heuristic()
if choosen_heuristic == "manhattan":
self.child[2].manhattan_distance()
possible_directions.append("RIGHT")
#print("Depth of this node is : : ", self.child[2].depth)
else:
self.child[2] = None
# LEFT - I move a tile with number on it, not the blank tile
if (current_node_blank_position + 1) % self.m_size:
self.child[3] = Node(self.n_size, self.m_size, self.current_state, self.goal_state, self.choosen_heuristic, self.current_state)
self.child[3] = copy.deepcopy(self)
self.child[3].parent = self.current_state
self.child[3].last_operator = "LEFT"
self.child[3].depth = self.depth + 1
new_blank_position = current_node_blank_position + 1
temp = self.child[3].current_state[current_node_blank_position]
self.child[3].current_state[current_node_blank_position] = self.child[3].current_state[new_blank_position]
self.child[3].current_state[new_blank_position] = temp
if choosen_heuristic == "misplaced":
self.child[3].misplaced_tiles_heuristic()
if choosen_heuristic == "manhattan":
self.child[3].manhattan_distance()
possible_directions.append("LEFT")
#print("Depth of this node is : ", self.child[3].depth)
else:
self.child[3] = None
#print("From this node (the parent node) I can move to -> ", possible_directions)
def get_node_priority(node):
return node.priority
def solve_puzzle(n_size, m_size, current_state, goal_state, choosen_heuristic):
open_list = []
closed_list = []
init_node_parent = None
init_node = Node(n_size, m_size, current_state, goal_state, choosen_heuristic, init_node_parent)
open_list.append(init_node)
while len(open_list) != 0:
if (len(open_list) == 0):
print("Fail - solution not found !")
else:
node = open_list.pop(0)
if (node.parent != None):
node.check_if_goal_is_reached()
node.generate_children(choosen_heuristic)
closed_list.append(node)
temp_list = []
for i in range(4):
if node.child[i] != None:
temp_list.insert(0, node.child[i])
sorted_temp_list = sorted(temp_list, key=get_node_priority, reverse=True)
for x in range(len(sorted_temp_list)):
if sorted_temp_list[x] != None:
open_list.insert(0, sorted_temp_list[x])
def print_current_node(current_node):
puzzle = ""
if current_node is None:
puzzle = ""
print(puzzle)
else:
puzzle = ""
count_lines = 0
for i in range(current_node.dimension):
puzzle += (str(current_node.current_state[i]) + " ")
count_lines += 1
if (count_lines % current_node.m_size == 0):
puzzle += "\n"
print(puzzle)
def main():
###################
# Static Data #
###################
# static for now, later I will let user to choose
n_size = 3
m_size = 3
current_state = [0, 1, 2, 3, 4, 5, 6, 7, 8]
goal_state = [8, 0, 6, 5, 4, 7, 2, 3, 1]
choosen_heuristic = "manhattan"
start_time = time.process_time()
solve_puzzle(n_size, m_size, current_state, goal_state, choosen_heuristic)
end_time = time.process_time()
search_time = end_time - start_time
print("Solved in : ", search_time, "seconds.")
main()
'''
Make sure you test with a solvable puzzle.
Here are some solvable tests:
goal: {1,2,3,4,5,6,7,8,0}
6 steps: {1,3,5,4,0,2,7,8,6}
18 steps: {1,4,0,5,2,8,7,6,3}
26 steps: {2,1,7,5,0,8,3,4,6}
27 steps: {8,5,3,4,7,0,6,1,2}
28 steps: {0,6,7,3,8,5,4,2,1}
30 steps: {5,7,0,4,6,8,1,2,3}
31 steps: {8,6,7,2,5,4,3,0,1} (highest number of steps possible for 3x3 )
Related
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:])
I'm VERY new to Python (self learning) and am writing some code, and have read as much as possible (both on this website and youtube) to figure it out, and I'm perplexed as to why it's not working for me
I've generated this dictionary of dictionaries (may not be most efficient, please let me know how to improve, been doing this a couple weeks only):
graphx = []
all_locs = []
def graph(width, height):
for r in range(height):
row = []
for c in range(width):
t = (r, c)
row.append(t)
all_locs.append(t)
graphx.append(row)
graph(width, height)
# # Builds a dictionary of all nodes, and their weight
weighted_grid = {}
for node in graphx:
for c in node:
n = {}
s = {}
e = {}
w = {}
if (c[0] < height) and (c[0] > 0):
n[c[0] + 1, c[1]] = 1
s[c[0] - 1, c[1]] = 1
elif c[0] == 0:
n[c[0] + 1, c[1]] = 1
elif c[0] == height:
s[c[0] - 1, c[1]] = 1
if c[1] < width and c[1] > 0:
e[c[0], c[1] + 1] = 1
w[c[0], c[1] - 1] = 1
elif c[1] == 0:
e[c[0], c[1] + 1] = 1
elif c[1] == height:
w[c[0], c[1] - 1] = 1
temp = {}
blank = {}
if n != blank:
temp[c[0] + 1, c[1]] = 1
if e != blank:
temp[c[0], c[1] + 1] = 1
if s != blank:
temp[c[0] - 1, c[1]] = 1
if w != blank:
temp[c[0], c[1] - 1] = 1
weighted_grid[c[0],c[1]] = temp
When I run dijikstras, using the the tuples as start and destination, I get an error. Here's the version of dijkstras I'm running:
def dijkstra(graph, start, goal):
shortest_distance = {} # records the current cost to reach that node.
track_predecessor = {} # keeps track of the path that led to this node.
unseen_nodes = graph # Iterate through the graph to check all nodes.
infinity = 99999 # Make it any large number,greater than possible path weights.
track_path = [] # gives us the trace-back path of the optimal route
for node in unseen_nodes:
shortest_distance[node] = infinity
shortest_distance[start] = 0
while unseen_nodes:
min_distance_node = None
for node in unseen_nodes:
if min_distance_node is None:
min_distance_node = node
elif shortest_distance[node] < shortest_distance[min_distance_node]:
min_distance_node = node
path_options = graph[min_distance_node].items()
for child_node, weight in path_options:
if weight + shortest_distance[min_distance_node] < shortest_distance[child_node]:
shortest_distance[child_node] = weight + shortest_distance[min_distance_node]
track_predecessor[child_node] = min_distance_node
unseen_nodes.pop(min_distance_node)
current_node = goal
while current_node != start:
try:
track_path.insert(0, current_node)
current_node = track_predecessor[current_node]
except KeyError:
break
track_path.insert(0, start)
if shortest_distance[goal] != infinity:
pass
The error I get is:
Traceback (most recent call last):
File "C:/Users/Dave/Desktop/Important/PycharmProjects/DMT2/dungeonmasterstome/Main.py", line 318, in <module>
dijkstra(weighted_grid, (0, 0), (0,1))
File "C:/Users/Dave/Desktop/Important/PycharmProjects/DMT2/dungeonmasterstome/Main.py", line 300, in dijkstra
if weight + shortest_distance[min_distance_node] < shortest_distance[child_node]:
KeyError: (0, 30)
Thoughts and thanks for any help and constructive criticism.
I am writing a scheduling algorithm for testing purposes and following the "Introduction to Algorithm book", but this is as far as I can get. As of now the job is dying in the weed "Endless loop somewhere". Generally, the algorithm should have a profit and we sort all other arrays according to profit "But I am just ignoring that for now" and my inputs are already sorted. So, the deadline and jobs passed are already sorted. Need somehelp as its still not working.
#!/usr/bin/python3.6
class Scheduling:
def schedule(self, n, deadline, jobs):
self.fdeadline = deadline
self.J = []
self.J.append(jobs[0])
self.i = 1
while self.i <= n:
self.K = self.J.copy()
self.K.append(jobs[self.i])
self.i = self.i + 1
if self.feasible(self.K, self.fdeadline) == True :
self.J = self.K
return self.J
def feasible(self, K, fdl):
self.tmp = K
self.isFeasible = True
self.i = 0
self.j = 1
self.k = 0
while self.i < len(self.tmp):
while self.j < len(self.tmp):
self.index1 = self.i
self.index2 = self.j
if (fdl[self.index1] > fdl[self.index2]):
self.tmp[i], self.tmp[j] = self.tmp[j], self.tmp[i]
while self.k < len(self.tmp):
self.job = self.tmp[self.k]
if (fdl[self.job] < k + 1):
isFeasible = False
break
return isFeasible
def main():
sins = Scheduling()
n = 4
deadline = [1,1,2,2]
jobs = [4, 2, 1, 3]
sjobs = sins.schedule(n, deadline, jobs)
print (sjobs)
if __name__ == "__main__":
main()
Here is a fully functioning code to the scheduling, it might need some small refinements, but its working fine.
#!/usr/bin/python3.6
from operator import itemgetter
class Scheduling:
def __init__(self, jobs):
self.jobs = jobs
def schedule(self, n, deadline):
self.sdl = deadline
self.J = []
self.J.append(self.jobs[1])
self.x = 2
while self.x < n:
self.K = self.J.copy()
self.K.append(self.jobs[self.x])
self.x = self.x + 1
self.feasibility = self.feasible(self.K, self.sdl)
if self.feasibility == True:
self.J = self.K.copy()
return self.J
def feasible(self, K, fdl):
self.tmp = K
self.isFeasible = True
self.i = 0
self.j = 1
self.k = 0
while self.i < len(self.tmp):
while self.j < len(self.tmp):
self.index1 = self.jobs.index(self.tmp[self.i])
self.index2 = self.jobs.index(self.tmp[self.j])
self.j = self.j + 1
if (fdl[self.index1] > fdl[self.index2]):
self.tmp[self.i], self.tmp[self.j] = self.tmp[self.j], self.tmp[self.i]
self.i = self.i + 1
while self.k < len(self.tmp):
self.job = self.tmp[self.k]
self.jobindex = self.jobs.index(self.job)
self.dlineval = fdl[self.jobindex]
self.ftest = self.k + 1
self.k = self.k + 1
if (self.dlineval < self.ftest):
self.isFeasible = False
break
return self.isFeasible
def main():
n = 7
jobs = [0, 1, 2, 3, 4, 5, 6]
deadline = [0, 2, 4, 3, 3, 2, 1]
profit = [0 , 46, 52, 30, 36 ,56, 40]
midresult = [list(x) for x in zip(deadline, profit ,jobs)]
midresult.sort(key=lambda k: (k[0], -k[1]))
deadline, profit, jobs = map(list, zip(*midresult))
sins = Scheduling(jobs)
sjobs = sins.schedule(n, deadline)
print("\n Jobs", sjobs)
finalprofit = []
finaldl = []
for c in range(len(sjobs)):
item = sjobs[c]
jobsindex = jobs.index(item)
finalprofit.append(profit[jobsindex])
finaldl.append(deadline[jobsindex])
print("\n profit", finalprofit)
print("\n Deadline", finaldl)
if __name__ == "__main__":
main()
First of all understand self in python. Read link
Secondly understand the job sequencing problem. Read link
Then, look at the below code
class Scheduling:
def schedule(self, n, deadline, jobs):
# max number of jobs you can schedule is the max deadline available.
filledJobs = ['dummy']*max(deadline);
i = 0
# start assigning the jobs in a greeedy way
while i < n:
job = jobs[i]
j = deadline[i]
# assign the job from the last deadline
while j > 0:
if(filledJobs[j-1] == 'dummy'):
filledJobs[j-1] = job
break
j = j - 1
i = i + 1
return filledJobs
def main():
sins = Scheduling()
n = 4
deadline = [1,1,2,2]
# assuming jobs are sorted w.r.t profits
# I represented the jobs with string to be clear
jobs = ['a', 'b', 'c', 'd']
sjobs = sins.schedule(n, deadline, jobs)
print (sjobs)
if __name__ == "__main__":
main()
Given the following Graph class which takes a graph object and outputs it's adjacency list:
class Graph:
def __init__(self, graph_str):
self.graph_str = []
graph_str = graph_str.splitlines()
for i in graph_str:
i = (i.split())
self.graph_str.append(i)
directed_helper = self.graph_str[0]
directed_score = directed_helper[0]
weighted_helper = self.graph_str[0]
weighted_score = weighted_helper[1]
self.weighted = weighted_score
self.directed = directed_score
self.graph_str.pop(0)
if self.directed == ("D"):
self.directed = True
elif self.directed == ("U"):
self.directed = False
if self.weighted == ("W"):
self.weighted = True
elif self.weighted != ("W"):
self.weighted = False
if self.weighted == False:
self.edge_number = graph_str[0]
self.edge_number = list(self.edge_number)
self.edge_number = self.edge_number[2]
self.edge_number = int(self.edge_number)
self.adjacency_list = [[] for _ in range(self.edge_number)]
elif self.weighted == True:
self.edge_number = graph_str[0]
self.edge_number = list(self.edge_number)
self.edge_number = self.edge_number[4]
self.edge_number = int(self.edge_number)
self.adjacency_list = [[] for _ in range(self.edge_number)]
if self.weighted == False:
if self.directed == True:
for s in self.graph_str:
self.first_element = s[0]
self.first_element = int(self.first_element)
self.second_element = s[1]
self.second_element = int(self.second_element)
self.adjacency_list[self.first_element].append((self.second_element, None))
elif self.directed == False:
for t in self.graph_str:
self.first_element = t[0]
self.first_element = int(self.first_element)
self.second_element = t[1]
self.second_element = int(self.second_element)
self.adjacency_list[self.first_element].append((self.second_element, None))
self.adjacency_list[self.second_element].append((self.first_element, None))
elif self.weighted == True:
if self.directed == True:
for t in self.graph_str:
self.first_element = t[0]
self.first_element = int(self.first_element)
self.second_element = t[1]
self.second_element = int(self.second_element)
self.third_element = t[2]
self.third_element = int(self.third_element)
self.adjacency_list[self.first_element].append((self.second_element, self.third_element))
if self.directed == False:
for t in self.graph_str:
self.first_element = t[0]
self.first_element = int(self.first_element)
self.second_element = t[1]
self.second_element = int(self.second_element)
self.third_element = t[2]
self.third_element = int(self.third_element)
self.adjacency_list[self.first_element].append((self.second_element, self.third_element))
self.adjacency_list[self.second_element].append((self.first_element, self.third_element))
(self.adjacency_list)
And the following function which performs a breadth first search on the same graph object:
def bfs_tree(graph, start):
graph = Graph(graph_str)
parent_array = []
found_parent = []
empty_list = []
for _ in graph.adjacency_list:
parent_array.append(None)
found_parent.append(False)
empty_list. append(start)
found_parent[start] = True
while len(empty_list) != 0:
element = empty_list.pop()
for i in graph.adjacency_list[element]:
if found_parent[i[0]] == False:
found_parent[i[0]] = True
parent_array[i[0]] = element
empty_list.insert(0, i[0])
return parent_array
I wish to Use backtracking to write a function allpaths(graph, source, destination) that takes a graph object, a source vertex (integer), and a destination vertex (integer), and returns a list of all possible paths from the source vertex to the destination vertex that do not contain any cycle. A path is list of vertices where the first element is the source vertex, the last element is the destination vertex, and the elements in between (if any) are vertices along the path.
For example:
# triangle graph
graph_str = """\
U 3
0 1
1 2
2 0
"""
print(sorted(allpaths(Graph(graph_str), 0, 2)))
which should output:
[[0, 1, 2], [0, 2]]
and this:
graph_str = """\
U 5
0 2
1 2
3 2
4 2
1 4
"""
print(sorted(allpaths(Graph(graph_str), 0, 1)))
which should output:
[[0, 2, 1], [0, 2, 4, 1]]
So far I have:
def allpaths (graph, source, destination):
graph = Graph(graph_str)
parents = bfs_tree(graph, 0)
counter = 0
array_a = []
array_a_2 = []
for i in parents:
if len(parents) >= counter:
if parents[counter] == source:
if len(parents) > counter+1:
a = counter
else:
a = (counter)
else:
counter+=1
for a in parents[counter:]:
array_a.append(a)
for c in parents:
if destination not in parents and parents[0] != None:
if c not in array_a:
array_a.append(c)
array_a.append(destination)
if None in array_a:
array_a.remove(None)
for b in array_a:
if b not in array_a_2:
array_a_2.append(b)
return (array_a_2)
Which only outputs one path, not all, and I am unsure how to continue with this.
Any help with this problem would be much appreciated :)
Do you want to write this algorithm yourself? I am only asking because it looks like what you are looking for is the all_simple_paths function in the wonderful graph library networkx.
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?