I have designed a maze in python and i have implemented the A* algorithm in the code and i am having issues trying the show the shortest path in a different color for what it is now. For instance when you run the code in outputs red for walls and green for the path i am trying to have it show the shortest path in Blue.
If you need the entire code please let me know.
## Functions
def printMaze(maze):
for i in range(0, height):
for j in range(0, width):
if (maze[i][j] == 'u'):
print(Fore.WHITE + str(maze[i][j]), end=" ")
elif (maze[i][j] == 'c'):
print(Fore.GREEN + str(maze[i][j]), end=" ")
else:
print(Fore.RED + str(maze[i][j]), end=" ")
print('\n')
# Find number of surrounding cells
def surroundingCells(rand_wall):
s_cells = 0
if (maze[rand_wall[0]-1][rand_wall[1]] == 'c'):
s_cells += 1
if (maze[rand_wall[0]+1][rand_wall[1]] == 'c'):
s_cells += 1
if (maze[rand_wall[0]][rand_wall[1]-1] == 'c'):
s_cells +=1
if (maze[rand_wall[0]][rand_wall[1]+1] == 'c'):
s_cells += 1
return s_cells
def a_star(maze, start, end):
# A* algorithm to find the shortest path from start to end
heap = [(0, start)] # using a heap to store the nodes to visit
visited = set() # set to store the visited nodes
parent = {} # dictionary to store the parent of each node
g_score = {start: 0} # dictionary to store the cost from start to each node
f_score = {start: (end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2} # using euclidean distance as heuristic
while heap:
current = heapq.heappop(heap)[1]
if current == end: # end is reached
path = [end]
while path[-1] != start:
path.append(parent[path[-1]])
return path[::-1] # return the path in reverse order
visited.add(current)
for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]: # exploring the four neighboring cells
next_cell = (current[0] + dx, current[1] + dy)
if 0 <= next_cell[0] < height and 0 <= next_cell[1] < width and maze[next_cell[0]][next_cell[1]] == 'c':
if next_cell in visited:
continue
g = g_score[current] + 1 # the cost is always 1 for moving to a neighboring cell
if next_cell not in [i[1] for i in heap]:
heapq.heappush(heap, (f_score[current] + g, next_cell))
elif g >= g_score[next_cell]:
continue
if cell == 'c' in printMaze:
print(Fore.BLUE + str(maze[i][j]), end=" ")
#g_score[next_cell] = f_score[next_cell] = g + (end[0] - next_cell[0]) ** 2 + (end[1] - next_cell[1]) ** 2
parent[next_cell] = current
return None # if no path is found
`
this is the output you can when you run the entire code i am trying to show the shortest path in blue
enter image description here
Related
I am trying to create a tic tac toe game with an adjustable game size and a computer that uses the minimax algorithm. The game sizes can only be odd numbers, to make sure that diagonal wins are always possible. The game runs with no errors, but I know that the minimax algorithm isn't working 100% correct because I can still beat the computer. I've looked extensively over my code and cannot find where the algorithm is going wrong. Here is my code:
Main.py
import TicTacToe
import Minimax
if (__name__ == "__main__"):
t = TicTacToe.ttt(3)
m = Minimax.Minimax(3, t)
while (t.winner == None):
if (t.turn == 1):
playerInputI = int(input("Input row: "))
playerInputJ = int(input("Input column: "))
bestIndex = (playerInputI, playerInputJ)
else:
winner, bestIndex = m.minimax(t.grid, (-1, -1), 15, -1)
t.winner = None
t.findWinner(bestIndex, t.grid)
t.updateGameGrid(bestIndex)
print(t.grid)
print(t.grid)
Minimax.py
class Minimax:
def __init__(self, gs, t):
self.gridSize = gs
self.ttt = t
def minimax(self, state, currIndex, depth, turn):
if (currIndex[0] != -1 and currIndex[1] != -1):
winner = self.ttt.findWinner(currIndex, state)
if (winner == -1):
return winner - depth, currIndex
elif (winner == -1):
return winner + depth, currIndex
elif (winner == 0):
return 0, currIndex
if (depth==0 and winner==None):
return 0, currIndex
evalLimit = -turn * 1000
bestIndex = None
for i in range(self.gridSize):
for j in range(self.gridSize):
if (state[i][j] == 0):
state[i][j] = turn
eval, newIndex = self.minimax(state, (i, j), depth-1, -turn)
state[i][j] = 0
if (turn > 0 and eval > evalLimit):
bestIndex = newIndex
evalLimit = eval
elif (turn < 0 and eval < evalLimit):
bestIndex = newIndex
evalLimit = eval
return evalLimit, bestIndex
Tictactoe.py
from random import randint
class ttt:
def __init__(self, size):
self.gridSize = size
self.grid = self.createGrid()
# If using minimax algorithm, user is maximizer(1) and computer is minimizer(-1)
# If single player, then user is 1, computer is -1
# If multiplayer, user1 is 1, user2 = -1
self.turn = 1
self.winner = None
def createGrid(self):
grid = []
for i in range(self.gridSize):
grid.append([])
for j in range(self.gridSize):
grid[i].append(0)
# grid = [[-1, 1, 0], [0, -1, 0], [0, 0, 0]]
return grid
def updateGameGrid(self, index):
if (self.grid[index[0]][index[1]] != 0):
return
self.grid[index[0]][index[1]] = self.turn
winner = self.findWinner(index, self.grid)
self.turn = -self.turn
def randomIndex(self):
x = randint(0, self.gridSize-1)
y = randint(0, self.gridSize-1)
while (self.grid[x][y] != 0):
x = randint(0, self.gridSize-1)
y = randint(0, self.gridSize-1)
return (x, y)
def findWinner(self, index, grid):
# Row
found = True
for j in range(self.gridSize-1):
if (grid[index[0]][j] != grid[index[0]][j+1] or grid[index[0]][j] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Column
found = True
for i in range(self.gridSize-1):
if (grid[i][index[1]] != grid[i+1][index[1]] or grid[i][index[1]] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Top Left to Bottom Right Diagonal
if (index[0] == index[1]):
found = True
for i in range(self.gridSize-1):
if (grid[i][i] != grid[i+1][i+1] or grid[i][i] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Top Right to Bottom Left Diagonal
if (index[0] + index[1] == self.gridSize-1):
found = True
for i in range(self.gridSize-1):
if (grid[self.gridSize-i-1][i] != grid[self.gridSize-i-2][i+1] or grid[self.gridSize-i-1][i] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
tie = True
for i in range(self.gridSize):
for j in range(self.gridSize):
if (grid[i][j] == 0):
tie = False
if (tie):
self.winner = 0
return 0
return None
The grid is represented as a 2d array, with each element being either a -1 for O, 1 for X and 0 for nothing. The player is 1 and the computer is -1. Who's turn it is is represented as a -1 or 1, corresponding to O or X. If anyone is able to find where the error in my code is that would be a great.
I see two troubles :
a) the two following conditions are the same in minimax
if (winner == -1):
return winner - depth, currIndex
elif (winner == -1):
return winner + depth, currIndex
b) When you return bestIndex, you actually return the winning move, the one that completes a line or a row or a diagonal, at one of the leaves of the game tree. What you really want is the next move to play. Write instead bestIndex = (i, j) in the condition if eval > evalLimit
I didn't check for everything but that is a good start. Run your code at depth=1 or 2 with many printf inside the minimax function, and look at the different moves, with the corresponding score, if they look correct or not.
I have imported a text file with numbers as the following example:
3 0 0 0 0 1 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 0 3 3 3 0 3 0 0 0 0 2 3 3 3 0 3 0 0 0 0 3 3 3 3 0 3 0 0 0 0 3 3 3 3 0 3 2 2 0 0 3 3 3 3 3 3 3 3 2 0 3 3 3
The goal is to read in the text file, format it as a grid (i.e a 10 by 10 grid) which I am able to do, and then sort through the list of lists to reach the solution where the number 3 is an obstacle, number 1 is start point and number 2 is the solution, I am attempting to use a BFS algorithm where the agent can move UP, DOWN, LEFT, RIGHT.
I am trying to print the sequence of steps that is taken to reach the closest solution (i.e 2) from the beginning point(i.e 1). The numbers are formatted as strings/ text. The program I have written seems to be running but it never prints a solution or terminates. The move sequence that is to be printed as a solutions is in the format of:
'Move Down'
'Move UP'
ETC. where each move is on a newline
I am attaching my code below and any help that can be offered would be greatly appreciated
import queue
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
def read_to_grid():
file_name = read_user_input()
for nums in open(file_name):
line = list(nums.split())
result = []
for _ in range(0, len(line), 10):
result.append(line[_:_ + 10])
return result
file_name.close()
def print_grid(result, path=''):
for x, pos in enumerate(result[0]):
if pos == '0':
start = x
i = start
j = 0
pos = set()
for move in path:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
pos.add((j, i))
for j, row in enumerate(result):
for i, col in enumerate(row):
if (j, i) in pos:
print('#', end='')
else:
print(col + ' ', end='')
print()
def valid(result, moves):
for x, pos in enumerate(result[0]):
if pos == '0':
start = x
i = start
j = 0
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
if not (0 <= i < len(result[0]) and 0 <= j < len(result)):
return False
elif (result[i][j] == '3'):
return False
return True
def find_goal(result, moves):
for x, pos in enumerate(result[0]):
if pos == '0':
start = x
i = start
j = 0
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
if result[j][i] == '2':
print('Found: ' + moves)
print_grid(result, moves)
return True
return False
nums = queue.Queue()
nums.put('')
add = ''
result = read_to_grid()
while not find_goal(result, add):
add = nums.get()
for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
put = add + j
if valid(result, put):
nums.put(put)
Ok Ryan answer already says everything, here however is your code working though is not efficient in anyway, the only things I changed that is worth is that instead of using a list of list you can just use a list, and the valid function now check the traveled path so that it can know where it has been so it won't loop.
import queue
# Read name file from user
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
# Read file and return list of list[10]
def read_to_grid():
with open(read_user_input()) as file:
for nums in file:
line = list(nums.split())
return line
# Shows a text grid
def print_grid(result, path=[]):
for x, pos in enumerate(result):
if pos == '1':
start = x
i = start
#j = 0
pos = set()
for move in path:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
i -= 10
elif move == 'Move Down':
i += 10
pos.add(i)
for i, celd in enumerate(result):
if i % 10 == 0:
print()
if i in pos:
print('# ', end='')
else:
print(celd + ' ', end='')
# Validates coordinates and traveled path
def valid(result, moves):
for x, pos in enumerate(result):
if pos == '1':
start = x
i = start % 10
j = start // 10
# Where we start
travel = [(j,i)]
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
# Check if we have already been there
if (j, i) in travel:
return False
else:
travel += [(j,i)]
# Check coordinates
if i >= 10 or i < 0 or j >= len(result) // 10 or j < 0:
return False
elif result[i+j*10] == '3':
return False
return True
# Return true if 2 is reached
def find_goal(result, moves):
for x, pos in enumerate(result):
if pos == '1':
start = x
i = start
#j = 0
for move in moves:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
i -= 10
elif move == 'Move Down':
i += 10
if result[i] == '2':
print('Found: ',' '.join(moves))
print_grid(result, moves[0:-1])
return True
return False
nums = queue.Queue()
result = read_to_grid()
add = []
while not find_goal(result, add):
if not nums.empty():
add = nums.get()
for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
put = add + [j]
if valid(result, put):
nums.put(put)
EDIT:
I cleaned up a little:
import queue
# Read name file from user
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
# Read file and return list of list[10]
def read_to_grid():
with open(read_user_input()) as file:
for nums in file:
line = list(nums.split())
return line
# Shows a text grid
def print_grid(result, path=[]):
pos = set()
for (x,y), _ in path:
i = x + y*10
pos.add(i)
for i, celd in enumerate(result):
if i % 10 == 0:
print()
if i in pos:
print('# ', end='')
else:
print(celd + ' ', end='')
# Validates coordinates and traveled path
def valid(result, moves):
# Unpack
(i,j), _ = moves[-1]
# Check if already traveled
if any(x == i and y == j for (x,y), __ in moves[:-1]):
return False
# Check coordinates
if i >= 10 or i < 0 or j >= len(result) // 10 or j < 0:
return False
elif result[i+j*10] == '3':
return False
return True
# Return true if 2 is reached
def find_goal(result, moves):
# Unpack
(i,j), _ = moves[-1]
if result[i+j*10] == '2':
#Print moves
output = 'Found: '
for (x,y), _ in moves:
output += " "+_
print(output)
#Print grid
print_grid(result, moves[1:-1])
return True
return False
# Return new position and which movement was done.
def move(pos, dir):
(x, y), _ = pos
if dir == 'Move Left':
x -= 1
elif dir == 'Move Right':
x += 1
elif dir == 'Move Up':
y -= 1
elif dir == 'Move Down':
y += 1
return (x, y), dir
nums = queue.Queue()
result = read_to_grid()
# Find the starting position
for x, pos in enumerate(result):
if pos == '1':
start = x
add = [((start % 10, start // 10),'')]
while not find_goal(result, add):
if not nums.empty():
add = nums.get()
for j in ['Move Left', 'Move Right', 'Move Up', 'Move Down']:
put = add + [move(add[-1],j)]
if valid(result, put):
nums.put(put)
Whilst debugging your code I ran into some endless loops and other bugs when it came to your 'valid' and 'find_goal' functions.
In my experience with breadth first search its best to treat each point as a node (coordinates in this case) and to have your queue consist of lists of paths that are currently being tried. Where each path is a list of each node thats transversed. Typically you don't want to visit the same node more than once in a given path so you'll have to keep track of this information rather than soley 'left', 'right', etc...
All that said, I built-off your code and created a function that would return the valid adjacent nodes when given a node accounting for grid bound, not being a 3 and wether the node has been visited or not. Then for the BFS part the queue starts with a list containing the starting node (I made a function to find where the 1 was). Then while a queue exists the BFS will pop off the current path, get the last node in that path, find all valid adjacent nodes. With each valid adjacent node a new path entry will be added to the queue consisting of the old path + the adjacent node. If one of the adjacent nodes is a goal it will end the search and return the path. I've included the directional information in the path so that you can parse that out.
This should print off a path to the nearest 2 as such:
[((5, 0), ''), ((5, 1), 'Down'), ((6, 1), 'Right'), ((6, 2), 'Down'), ((7, 2), 'Right'), ((7, 3), 'Down'), ((7, 4), 'Down'), ((7, 5), 'Down')]
You'll see the ...sorted(path_queue, key=lambda... that line isn't needed but is a lazy way to prioritize the queue, always trying the shortest current path. If you remove it you'll see you still get a valid path but its much longer.
def read_user_input():
file_name = input('Enter the name of your file :\n')
return file_name
def read_to_grid():
file_name = read_user_input()
for nums in open(file_name):
line = list(nums.split())
result = []
for _ in range(0, len(line), 10):
result.append(line[_:_ + 10])
int_result = []
for i, row in enumerate(result):
int_result.append([])
for col in row:
int_result[i].append(int(col))
return int_result
def print_grid(result, path=''):
for x, pos in enumerate(result[0]):
if pos == 0:
start = x
i = start
j = 0
pos = set()
for move in path:
if move == 'Move Left':
i -= 1
elif move == 'Move Right':
i += 1
elif move == 'Move Up':
j -= 1
elif move == 'Move Down':
j += 1
pos.add((j, i))
for j, row in enumerate(result):
for i, col in enumerate(row):
if (j, i) in pos:
print('#', end='')
else:
print(str(col) + ' ', end='')
print()
def find_start_node(grid):
for i, row in enumerate(grid):
if 1 in row:
return ((row.index(1), i), '')
return (None, None)
def valid_adj(cur_node, grid, visited):
x = cur_node[0][0]
y = cur_node[0][1]
adj = []
if ((y + 1) < 10) and (grid[y + 1][x] != 3) and not (any((x, y + 1) in node for node in visited)):
adj.append(((x, y + 1), 'Down'))
if ((x + 1) < 10) and (grid[y][x + 1] != 3) and not (any((x + 1, y) in node for node in visited)):
adj.append(((x + 1, y), 'Right'))
if ((y - 1) >= 0) and (grid[y - 1][x] != 3) and not (any((x, y - 1) in node for node in visited)):
adj.append(((x, y - 1), 'Up'))
if ((x - 1) >= 0) and (grid[y][x - 1] != 3) and not (any((x - 1, y) in node for node in visited)):
adj.append(((x - 1, y), "Left"))
return adj
def BFS(grid):
start_node = find_start_node(grid)
path_queue = [[start_node]]
while path_queue:
path_queue = sorted(path_queue, key=lambda x: len(x), reverse=True) # More optimized to guarantee shortest path, not needed
cur_path = path_queue.pop()
cur_node = cur_path[-1]
if cur_node not in cur_path[:].pop():
adj = valid_adj(cur_node, grid, cur_path)
for node in adj:
new_path = list(cur_path)
new_path.append(node)
path_queue.append(new_path)
if grid[node[0][1]][node[0][0]] == 2:
print('path found')
return new_path
return -1
grid = read_to_grid()
print_grid(grid)
print(BFS(grid))
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 trying to make a program:
simulates a random walk
animates the walk using cs1graphics
starts on center block which is black
takes random steps, never been stepped on turns red, repeat step turns blue.
import random
from cs1graphics import *
from time import sleep
def animationWalk(walk):
print("Animation of Random Walk: ", end = "\n")
window = Canvas(250, 250)
window.setTitle('Random Walk in Manhattan')
for y in range(10) :
for x in range(10) :
cue = Square()
cue.setSize(50)
cue.moveTo(x*25, y*25)
window.add(cue)
(x,y)= (6,6)
squares = Square()
squares.setSize(25)
squares.moveTo((x*25)-14, (y*25)-13)
squares.setFillColor('black')
window.add(squares)
been = Square()
been.setSize(25)
been.moveTo((x*25)-14, (y*25)-13)
window.add(been)
for direction in range (len(walk)):
if walk[direction] == 'N':
#y -= 1
(x,y)=(x,y-1)
elif walk[direction] == 'E':
#x += 1
(x,y)=(x+1,y)
elif walk[direction] == 'S':
#y += 1
(x,y) =(x,y+1)
elif walk[direction] == 'W':
#x -= 1
(x,y) = (x-1,y)
been.setSize(25)
been.moveTo((x*25)-14, (y*25)-13)
been.setFillColor('red')
cue.setSize(25)
sleep(0.25)
cue.moveTo((x*25)-14, (y*25)-13)
cue.setFillColor('blue')
def randomWalk(x,y):
block = []
for i in range (x):
block.append([])
for i in block:
for j in range(y):
i.append(0)
position = (x//2, y//2)
h = position[0]
v = position[1]
walk = ''
block[h][v] += 1
while (h != -1) and (h != (x-1)) and (v != -1) and (v != (y-1)):
directions = random.randrange(1,5)
if directions == 1:
v += 1
walk = walk + 'E'
elif directions == 2:
h += 1
walk = walk + 'S'
elif directions == 3:
v -= 1
walk = walk + 'W'
elif directions == 4:
h -= 1
walk = walk + 'N'
block[h][v] += 1
print("Starting at Center (", x//2, ",", y//2, ")")
print("Walking Directions: ", walk)
print("Track of Random Walk:", end = "\n")
for entry in block:
print(entry)
animationWalk(walk)
def main(): ## define main program
x = 10
y = 10
randomWalk(x,y)
main()
The color of a square is supposed to change to red when it is visited, blue when it is revisited and is changed back to red when it is passed. I cant get the blocks to maintain the color after it has been stepped off of.
I'm working on an exercise where given a set of connections between two points (ie. 12 is a connection between 1 and 2 ect.). I decided to tackle the approach recursively in order to have it systematically check every path and return when it finds one that hits every node and starts and ends with one.
However upon debugging this it seems that as I pass down the adjMatrix further into the recursion it's also editing the upper levels and causing it not to search any further as it goes back up the tree. I think it has something to when I set newMatrix = adjMatrix, but I'm not exactly sure.
def checkio(teleports_string):
#return any route from 1 to 1 over all points
firstnode, secondnode, size = 0, 0, 8
#Makes the adjacency matrix
adjMatrix = [[0 for i in range(size)] for j in range(size)]
for x in teleports_string:
#Assigns Variables
if firstnode == 0 and x != ",":
#print("Node1:" + x)
firstnode = x
elif secondnode == 0 and x != ",":
#print("Node2:" + x)
secondnode = x
#Marks connections
if firstnode != 0 and secondnode != 0:
adjMatrix[int(firstnode) - 1][int(secondnode) - 1] = 1
adjMatrix[int(secondnode) - 1][int(firstnode) - 1] = 1
firstnode, secondnode = 0, 0
print(adjMatrix)
return findPath(adjMatrix, 1, "1")
def findPath(adjMatrix, currentnode, currentpath):
if isFinished(currentpath):
return currentpath
for x in range(0, 8):
if adjMatrix[currentnode - 1][x] == 1:
print(currentpath + "+" + str(x+1))
newMatrix = adjMatrix
newMatrix[currentnode - 1][x] = 0
newMatrix[x][currentnode - 1] = 0
temp = currentpath
temp += str(x+1)
newpath = findPath(newMatrix, x+1,temp)
print(newpath)
if isFinished(newpath):
print ("Returning: " + newpath)
return newpath
return ""
def isFinished(currentpath):
#Checks if node 1 is hit at least twice and each other node is hit at least once
if currentpath == "":
return False
for i in range(1, 9):
if i == 1 and currentpath.count(str(i)) < 2:
return False
elif currentpath.count(str(i)) < 1:
return False
#Checks if it starts and ends with 1
if not currentpath.startswith(str(1)) or not currentpath.endswith(str(1)):
return False
return True
#This part is using only for self-testing
if __name__ == "__main__":
def check_solution(func, teleports_str):
route = func(teleports_str)
teleports_map = [tuple(sorted([int(x), int(y)])) for x, y in teleports_str.split(",")]
if route[0] != '1' or route[-1] != '1':
print("The path must start and end at 1")
return False
ch_route = route[0]
for i in range(len(route) - 1):
teleport = tuple(sorted([int(route[i]), int(route[i + 1])]))
if not teleport in teleports_map:
print("No way from {0} to {1}".format(route[i], route[i + 1]))
return False
teleports_map.remove(teleport)
ch_route += route[i + 1]
for s in range(1, 9):
if not str(s) in ch_route:
print("You forgot about {0}".format(s))
return False
return True
assert check_solution(checkio, "13,14,23,25,34,35,47,56,58,76,68"), "Fourth"
The line
newMatrix = adjMatrix
merely creates another reference to your list. You'll need to actually create a new list object. As this is a matrix, do so for the contents:
newMatrix = [row[:] for row in adjMatrix]
This creates a new list of copies of your nested lists.