Python variable not seen by nested function - python

I am building a program that will solve sudoku puzzles. Here is the code:
def set_table(boxes_v, boxes_h, tiles_v, tiles_h):
""" boxes_v = boxes on vertical side of the whole table;
boxes_h = boxes on horixontal side of the whole table;
tiles_v = tiles on vertical line in each box;
tiles_h = tiles on horizontal line in each box.
"""
total_boxes = boxes_v * boxes_h
tiles_in_box = tiles_v * tiles_h
return [[{None : [False, 0]} for x in range(1, tiles_in_box + 1)] for a in range(total_boxes)]
def insert_numbers(table, numbers_and_positions):
""" table = sudoku table set up in "set_table";
numbers_and_postions = dictionary containing already given numbers and their positions in the table.
"""
noPos = numbers_and_positions
for number in noPos:
box = noPos[number][0]
tile = noPos[number][1]
table[box][tile] = {None : [True, number]}
return table
def test(table, index, number):
"Tests if number is suitable in a tile on vertical and horizontal lines"
box_index, tile_index = index
lines = {
0 : [(0,1,2), (0,3,6)],
1 : [(0,1,2), (1,4,7)],
2 : [(0,1,2), (2,5,8)],
3 : [(3,4,5), (0,3,6)],
4 : [(3,4,5), (1,4,7)],
5 : [(3,4,5), (2,5,8)],
6 : [(6,7,8), (0,3,6)],
7 : [(6,7,8), (1,4,7)],
8 : [(6,7,8), (2,5,8)]
}
box_line_h, box_line_v = lines[index[0]]
tile_line_h, tile_line_v = lines[index[1]]
################################### horizontal line tester
taken_numbers_h = []
for box_index in box_line_h:
index_counter = 0
for tile in table[box_index]:
if index_counter in tile_line_h:
taken_numbers_h.append(tile[None][1])
index_counter += 1
################################### vertical line tester
taken_numbers_v = []
for box_index in box_line_v:
index_counter = 0
for tile in table[box_index]:
if index_counter in tile_line_v:
taken_numbers_v.append(tile[None][1])
index_counter += 1
################################### box tester
taken_numbers_b = []
for tile in table[box_index]:
taken_numbers_b.append(tile[None][1])
###################################
taken_numbers = taken_numbers_h + taken_numbers_v + taken_numbers_b
if number in taken_numbers:
return True
elif number not in taken_numbers:
return False
def reset_key(dictionary, old_key, new_key, value):
"Resets a key of a dictionary to a different name"
dictionary[new_key] = dictionary[old_key]
del dictionary[old_key]
return dictionary
def solve(table):
""" Solves the sudoku puzzle
"""
box_index = 0
tile_index = 0
safe = True
def repeat():
tile[None][1] += 1
if tile[None][1] > 9:
tile[None][1] = 0
safe = False
tile_index -= 1
elif tile[None][1] <= 9:
if test(table, [box_index, tile_index], tile[None][1]) is True:
repeat()
elif test(table, [box_index, tile_index], tile[None][1]) is False:
tile_index += 1
safe = True
valid = False
while valid is not True:
box = table[box_index]
tile = box[tile_index]
if tile[None][0] is True:
if safe is True:
tile_index += 1
safe = True
elif safe is False:
tile_index -= 1
elif tile[None][0] is False:
repeat()
What I do in Python shell is:
>>>table = set_table(3,3,3,3)
>>>table = insert_numbers(table, {0 : [0,4]})
>>>solve(table)
What I expect to happen is the program changing the value of every dictionary on table[0] (box 1) to {None : [False, 1]}, {None : [False, 2]}, {None : [False, 3]} and so on until it reaches 9 at the last small box within the first big box of the whole grid because the code should cause an index error. This is because when it reaches the last small box in the first big box, an negative test result for violating the rules should come up for numbers 1-8 because they are obviously already in the box. I do not see this error, however, but this one instead:
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
solve(t)
File "C:\Users\cdobr\Desktop\Sudoku Solver.py", line 153, in solve
repeat()
File "C:\Users\cdobr\Desktop\Sudoku Solver.py", line 129, in repeat
if test(table, [box_index, tile_index], tile[None][1]) is True:
UnboundLocalError: local variable 'tile_index' referenced before assignment
What am I supposed to do to fix this? It is as if the function inside the function could not see the parent's variables.

tile_index is a non-local variable, but you do not specify it to be so. You can do this explicitly using nonlocal:
def repeat():
nonlocal tile_index
Why is nonlocal to be specified for this and not for any of the other non-local variables? Because of this line:
tile_index -= 1
A recursive call causes this to be executed. However, when doing an assignment, python assumes the variable is local unless otherwise mentioned. None of the other non-local variables are assigned to/updated, so this does not apply.

Related

Why am I getting the variable not defined error?

The following is my code
n = int(input("please enter a value: "))
board = []
def make_board(n):
global board
max = n * n #the number of tiles in the board
count = 1 #a counter to change the value assigned to each tile
for i in range(n):
board.append([]) #appends a list inside the list of board. Essentially creates a row which is of type list.
for j in range(n):
num = max - count
if num == 0: #the 0 tile will display like a blank space
tile = ' '
elif num < 10: #adds a space to tile values less than 10 for formatting.
tile = ' ' + str(num)
else:
tile = str(num)
board[i].append(tile) #appends a tile value to each row, n number of times.
count += 1
if n%2 == 0:
tempvara = board[n-1][n-2]
tempvarb = board[n-1][n-3]
board[n-1][n-2]=tempvarb
board[n-1][n-3]=tempvara
#TODO
for row in board:
print(' '.join(row))
def find_blank(board):
global counterUNO
global counterDOS
global counterTRES
counterTRES = 0
#TODO
for i in range(n):
tempvari = board[i]
if ' ' in tempvari:
counterUNO = i
for z in board[counterUNO]:
counterTRES = counterTRES + 1
if ' ' in z:
counterDOS = counterTRES-1
break
tupleone = (counterUNO,counterDOS)
return(tupleone)
def find_tile(f):
counterfour = 0
tiles = str(input("tile BUBBER"))
if int(tiles)<10:
tiles = " "+tiles
counterfive = 0
countersixe = 0
countersixe = 0
for i in range(n):
chopstick = board[i]
if tiles in chopstick:
counterfour = i
for z in board[counterfour]:
countersixe = countersixe + 1
if tiles in z:
counterfive = countersixe-1
break
tupleDOS = (counterfour,counterfive)
return(tupleDOS)
def find_next_to_blank(board):
#print("here is the shit")
#print(find_tile(board))
vara = find_tile(board) #sets tile numb tuple to vara
varb = find_blank(board) #yeah
varc = int(tiles)
varaa = int(tiles[0])
varab = int(tiles[1])
varba = board[varaa+1][varab]
varbb = board[varaa][varab+1]
varbc = board[varaa-1][varab]
varbd = board[varaa][varab-1]
varbe = board[varaa+1][varab+1]
varbf = board[varaa-1][varab-1]
make_board(n)
#find_blank(board)
#find_tile(board)
find_next_to_blank(board)
Problem:
Right now I am trying to make a python Tile game where I can shift the numbers. the make board function obviously creates a board, i did it so that there are three lists in a big list and in the three list there are elements
and the find blank function identifies the coordinate of the non existing section of the board
and the find tile function is the function that the user inputs a value and the code identifies what is the coordinate of the tile that we want
So currently I am getting an error because when i am running the find next to blank function (the function is supposed to identify whether or not there is a blank spot next to the value which the user wants to input) i get the following error
Traceback (most recent call last):
File "python", line 149, in <module>
File "python", line 122, in find_next_to_blank
NameError: name 'tiles' is not defined
and i tried making "tiles" variable into a global one but it didn't work at all
The variable tiles is not defined in find_next_to_blank(board). I'd be remiss if I don't say this: consider restructuring your program to avoid using global variables.
Now that's out of the way, if you make tiles global, it ought to work.

How to implement overwriting in my current "Redo" function in Python?

I have (along with the help of my buddy #Nuclearman who answered one of my other questions) partly implemented a redo function in my program, which is a turtle graphics program that draws any letter pressed on the keyboard pressed by the user onto the turtle graphics canvas. I say "partly" because although it does redo, it does not do what it was meant for, which is overwrite the last undone letter with a new one if one is called by the user. For example, suppose you wrote "HELLO" on the canvas and undid up to "H". You then wanted to replace the "E", with, lets say, an "A". So you draw an "A". When you press redo, it is supposed to now show "HAL" right? Well, that is not the case with my redo. My redo instead, following the example I gave, outputs this:
It draws the E over the A! Well, that is not what the redo is meant for! So, how could I make so that the turtle would draw "HAL" instead of whatever is happening in the image above and implement this based on my current redo function shown below? Any help is greatly appreciated! :)
My Redo function:
def Clear():
# Clear the canvas
clear()
speed(0)
tracer(0,0)
def redoHandler():
if undoHandler.handling == True and draw.drawing == True and len(newerdeq) > 0:
# "newerdeq" is my 'forward' stack for the redo to be possible
redoHandler.handling = True
if not hasattr(redoHandler, "counter"):
redoHandler.counter = 0
redoHandler.counter += 1
draw.counter += 1
print("`draw` has been called {} times.".format(draw.counter))
Clear()
ui = newerdeq.pop() #<-- Pop redone items...
function.append(ui) #<-- back to the "undo" function stack.
penup()
try:
goto(o,p)
except:
goto(-200, 100)
pendown()
try:
# Now execute all the items (from an earlier queue that all letter functions go to first) if defined as a Point class or as a function
for i in function:
k = i.getXY()
penup()
goto(k)
pendown()
hk = i.getletterheight()
global letter_height
letter_height = hk
rk = i.getletterwidth()
global letter_width
letter_width = rk
hw = i.getwidth()
width(hw)
op = i.getcolor()
try:
color(op)
except:
for g in colors:
cp = g.getcolor2()
colormode(255)
color(cp)
j = i.getfunction()
j()
except:
i()
update()
Well, I solved it. It was a very simple fix. All I had to do was in the queue that each letter is executed, just put:
if len(newerdeq) > 0:
newerdeq.pop()
Like this:
def draw(x):
draw.drawing = True
if not hasattr(draw, 'counter'):
draw.counter = 0
global drawing
q.put(x)
process = False
drawingLock.acquire()
if not drawing:
process = True
drawing = True
drawingLock.release()
if process:
if not q.empty():
v = xcor()
y = ycor()
c = pencolor()
w = width()
ph = letter_height
pw = letter_width
x()
po = Point(v,y,c,w,isdown(),x,ph,pw)
function.append(po)
if len(newerdeq) > 0:
newerdeq.pop() #<-- Here it is... fixed.
draw.counter += 1
print("`draw` has been called {} times.".format(draw.counter))
if x == draw_W:
draw_W.drawing = True
draw_W.counter += 1
draw.counter -= 1
elif x == draw_Y:
draw_Y.drawing = True
draw_Y.counter += 1
draw.counter -= 1
global h
h = (x)
drawingLock.acquire()
drawing = False
drawingLock.release()

Python - how to distinguish between 2 objects of the same name in a list

So i'm making a genetic algorithm in python. The goal is for the "organism" to get to the top of the screen (x,20). I have a loop that creates the population which is a list of objects of type "Organism". Here is the code for the Organism class:
class Organism(object):
def __init__(self, genes,ID):
self.genes = genes
self.position = [0,0]
self.thisTime=str()
self.geneTranslation = []
self.ID=ID
def move(self,d):
if d == "f" or d == "forward":
self.position[1] += 1
elif d == "b" or d == "back":
self.position[1] -= 1
elif d == "r" or d == "right":
self.position[0] += 1
elif d == "l" or d == "left":
self.position[0] -= 1
print(self.position)
def isInContactWith(self,point):
point = list(point)
if self.position == point:
return True
else:
return False
def run(self):
for i in range(0,4):
if i == 0:
self.geneTranslation.extend(["f"] * self.genes[0])
elif i == 1:
self.geneTranslation.extend(["b"] * self.genes[1])
elif i == 2:
self.geneTranslation.extend(["r"] * self.genes[2])
elif i == 3:
self.geneTranslation.extend(["l"] * self.genes[3])
r.shuffle(self.geneTranslation)
for x in range(1,20):
self.thisTime = r.choice(self.geneTranslation)
self.move(self.thisTime)
As you can see, the genes specify the chance of a particular move. The problem is in the for loop that creates the population and runs it(ive added prints for debugging):
population = []
yValues={}
running = True
BestOrganism=Organism([25,25,25,25],0)
SecondOrganism=Organism([25,25,25,25],1)
for count in range(5):
for x in range(10):
a = lambda: r.randint(0, 3)
c = lambda: r.randint(-1, 1)
b = BestOrganism.genes
anOrganism = Organism(b,x)
anOrganism.genes[a()]+=c()
population.append(anOrganism)
for j in range(len(population)):
print("Organism " + str(population[j].ID) + str(population[j].genes))
population[j].run()
yValues[population[j].ID]=population[j].position[1]
if population[j].position[1]>=20:
print(population[j].genes)
running = False
break
BestOrganism=max(yValues)
for k in range(len(population)):
if population[k].ID==BestOrganism:
BestOrganism=population[k]
print(yValues[max(yValues)])
print(str(population)+"\n"+str(yValues)+"\n"+str(BestOrganism.genes))
population=[]
yValues={}
No errors pop up but, whenever I try to "mutate" a particular part of the organism's genes with anOrganism.genes[a()]=c(), it ends up mutating every organism in the list, even the ones not yet created, so that all of their genes are the exact same at the end (as seen in the final product). Why is python doing this?
It's because of this line: anOrganism = Organism(b,popPos).
So on the first iteration of the loop, you create an Organism and set the self.genes to b. Then you modify the .genes of that organism you just created.
When you iterate through the loop again, you are creating a new organism but with the same exact list, so it has the changes from the previous one.
Basically, there is only one b, and you are setting the .genes attribute of every organism to point to that single list. So any and everytime you change it, it changes for all of the organisms.
What you need to do is be sending in a copy of b to the constructor of Organism.
And then change the line anOrganism = Organism(b,popPos) to be
anOrganism = Organism(list(b), popPos)
The key is these lines:
b = BestOrganism.genes
anOrganism = Organism(b,popPos)
The list of genes will be shared by every organism created by that line. That is, anOrganism.genes is exactly the same object as BestOrganism.genes. So a change to BestOrganism.genes is seen in anOrganism.genes.
Python has an easy way to make a copy of a list, using "slicing". Change the line to this:
anOrganism = Organism(b[:],popPos)
Now anOrganism has its own genes, starting off identical to but still distinct from BestOrganism's genes.
Postscript:
Here is a simplified form, using literal ints instead of the random functions:
>>> class Organism(object):
... def __init__(self, genes, ID):
... self.genes = genes
... self.ID = ID
...
>>> zero = Organism([25,25,25,25],0)
>>> one = Organism(zero.genes, 1)
>>> two = Organism(zero.genes[:], 2)
>>> one.genes[2] += 5
>>> two.genes[3] += 7
>>> zero.genes
[25, 25, 30, 25]
>>> one.genes
[25, 25, 30, 25]
>>> two.genes
[25, 25, 25, 32]
>>>

Solving a maze using recursion in python

So, I have an assignment which asks me to solve a maze using recursion. I will post the assignment guidelines so you can see what I am talking about. The professor didn't explain recursion that much, he gave us examples of recursion, which I will post, but I was hoping someone might be able to give me a more in depth explanation of the recursion, and how I would apply this to solving a maze. I'm not asking for anyone to write the code, I'm just hoping some explanations would put me on the right path. Thank you to anyone who answers.
Here are the examples I have:
def foo():
print("Before")
bar()
print("After")
def bar():
print("During")
def factorial(n):
"""n!"""
product = 1
for i in range(n,0,-1):
product *= i
return product
def recFac(n):
"""n! = n * (n-1)!"""
if(n == 1):
return 1
return n * recFac(n-1)
def hello():
"""Stack overflow!"""
hello()
def fib(n):
"""f(n) = f(n-1) + f(n-2)
f(0) = 0
f(1) = 1"""
if n == 0 or n == 1: #base case
return n
return fib(n-1) + fib(n-2) #recursive case
def mult(a,b):
"""a*b = a + a + a + a ..."""
#base case
if (b == 1):
return a
#recursive case
prod = mult(a,b-1)
prod *= a
return prod
def exp(a,b):
"""a ** b = a* a * a * a * a *.... 'b times'"""
#base case
if (b==0):
return 1
if (b == 1):
return a
#recursive case
return exp(a,b-1)*a
def pallindrome(word):
"""Returns True if word is a pallindrome, False otherwise"""
#base case
if word == "" or len(word)==1:
return True
#recursive case
if word[0] == word[len(word)-1]:
word = word[1:len(word)-1]
return pallindrome(word)
else:
return False
Here are the guidelines:
You are going to create a maze crawler capable of solving any maze you give it with the power of recursion!
Question 1 - Loading the maze
Before you can solve a maze you will have to load it. For this assignment you will use a simple text format for the maze. You may use this sample maze or create your own.
Your objective for this question is to load any given maze file, and read it into a 2-dimensional list.
E.g.: loadMaze("somemaze.maze") should load the somemaze.maze file and create a list like the following...
[['#','#','#','#','#','#','#','#','#'],
['#','S','#',' ',' ',' ','#','E','#'],
['#',' ','#',' ','#',' ',' ',' ','#'],
['#',' ',' ',' ','#',' ','#',' ','#'],
['#', #','#','#','#','#','#','#','#']]
Note that the lists have been stripped of all '\r' and '\n' characters. In order to make the next question simpler you may make this list a global variable.
Next write a function that prints out the maze in a much nicer format:
E.g.,
####################################
#S# ## ######## # # # # #
# # # # # # #
# # ##### ## ###### # ####### # #
### # ## ## # # # #### #
# # # ####### # ### #E#
####################################
Test your code with different mazes before proceeding.
Question 2 - Preparing to solve the maze
Before you can solve the maze you need to find the starting point! Add a function to your code called findStart() that will search the maze (character-by-character) and return the x and y coordinate of the 'S' character. You may assume that at most one such character exists in the maze. If no 'S' is found in the maze return -1 as both the x and y coordinates.
Test your code with the 'S' in multiple locations (including no location) before proceeding.
Question 3 - Solving the maze!
Finally, you are ready to solve the maze recursively! Your solution should only require a single method: solve(y,x)
A single instance of the solve method should solve a single location in your maze. The parameters y and x are the current coordinates to be solved. There are a few things your solve method should accomplish. It should check if it is currently solving the location of the 'E'. In that case your solve method has completed successfully. Otherwise it should try to recursively solve the space to the right. Note, your method should only try to solve spaces, not walls ('#'). If that recursion doesn't lead to the end, then try down, then left, and up. If all that fails, your code should backtrack a step, and try another direction.
Lastly, while solving the maze, your code should leave indicators of its progress. If it is searching to the right, the current location should have a '>' in place of the empty space. If searching down put a 'v'. If searching left '<', and if searching up '^'. If your code has to backtrack remove the direction arrow, and set the location back to a ' '.
Once your maze is solved print out the maze again. You should a see step-by-step guide to walking the maze.
E.g.,
main("somemaze.maze")
#########
#S# #E#
# # # #
# # # #
#########
S is at (1,1)
#########
#S#>>v#E#
#v#^#>>^#
#>>^# # #
#########
Test your code with different different start and end locations, and optionally over a variety of mazes.
Here is the code I have so far:
But the code is not actually printing the track in the maze, and I'm not sure why.
def loadMaze():
readIt = open('Maze.txt', 'r')
readLines = readIt.readlines()
global mazeList
mazeList = [list(i.strip()) for i in readLines]
def showMaze():
for i in mazeList:
mazeprint = ''
for j in i:
mazeprint = mazeprint + j
print(mazeprint)
print('\n')
def solve(x,y, mazeList):
mazeList[x][y] = "o"
#Base case
if y > len(mazeList) or x > len(mazeList[y]):
return False
if mazeList[y][x] == "E":
return True
if mazeList[y][x] != " ":
return False
#marking
if solve(x+1,y) == True: #right
mazeList[x][y]= '>'
elif solve(x,y+1) == True: #down
mazeList[x][y]= 'v'
elif solve(x-1,y) == True: #left
mazeList[x][y]= '<'
elif solve(x,y-1) == True: #up
mazeList[x][y]= '^'
else:
mazeList[x][y]= ' '
return (mazeList[x][y]!= ' ')
(Dating myself, I actually did this problem in COBOL, in high-school.)
You can think of solving the maze as taking steps.
When you take a step, the same rules apply every time. Because the same rules apply every time, you can use the exact same set of instructions for each step. When you take a step, you just call the same routine again, changing the parameters to indicate the new step. That's recursion. You break the problem down by taking it one step at a time.
Note: Some recursion solutions break the problem in half, solving each half independent of the other, that works when the two solutions are actually independent. It doesn't work here because each step (solution) depends on the previous steps.
If you hit a dead end, you back out of the dead end, until you find a step where there are still viable squares to check.
Helpful Hint: You don't mark the correct path on the way to the exit, because you don't know that the step you're taking right now is part of the path to the exit. You mark the path on the way back, when you know that each step is indeed part of the path. You can do this because each step remembers which square it was in before it took the next step.
Instead, you put a mark in each square you've tried that only says: I've been here, no need to check this one again. Clean those up before you print the solution.
Here is my solution of CodeEval's The Labirynth challenge:
import sys
sys.setrecursionlimit(5000)
class Maze(object):
FLOOR = ' '
WALLS = '*'
PATH = '+'
def __init__(self):
self.cols = 0
self.rows = 0
self.maze = []
def walk_forward(self, current_k, r, c):
self.maze[r][c] = current_k
next_k = current_k + 1
# up
if r > 1:
up = self.maze[r - 1][c]
if up != self.WALLS:
if up == self.FLOOR or int(up) > current_k:
self.walk_forward(next_k, r - 1, c)
# down
if r < self.rows - 1:
down = self.maze[r + 1][c]
if down != self.WALLS:
if down == self.FLOOR or int(down) > current_k:
self.walk_forward(next_k, r + 1, c)
# left
if c > 1:
left = self.maze[r][c - 1]
if left != self.WALLS:
if left == self.FLOOR or int(left) > current_k:
self.walk_forward(next_k, r, c - 1)
# right
if c < self.cols - 1:
right = self.maze[r][c + 1]
if right != self.WALLS:
if right == self.FLOOR or int(right) > current_k:
self.walk_forward(next_k, r, c + 1)
def walk_backward(self, r, c):
current_k = self.maze[r][c]
if not isinstance(current_k, int):
return False
self.maze[r][c] = self.PATH
up = self.maze[r - 1][c] if r > 0 else None
down = self.maze[r + 1][c] if r < self.rows - 1 else None
left = self.maze[r][c - 1] if c > 1 else None
right = self.maze[r][c + 1] if c < self.cols else None
passed = False
if up and isinstance(up, int) and up == current_k - 1:
self.walk_backward(r - 1, c)
passed = True
if down and isinstance(down, int) and down == current_k - 1:
self.walk_backward(r + 1, c)
passed = True
if left and isinstance(left, int) and left == current_k - 1:
self.walk_backward(r, c - 1)
passed = True
if right and isinstance(right, int) and right == current_k - 1:
self.walk_backward(r, c + 1)
def cleanup(self, cleanup_path=False):
for r in range(0, self.rows):
for c in range(0, self.cols):
if isinstance(self.maze[r][c], int):
self.maze[r][c] = self.FLOOR
if cleanup_path and self.maze[r][c] == self.PATH:
self.maze[r][c] = self.FLOOR
def solve(self, start='up', show_path=True):
# finding start and finish points
upper = lower = None
for c in range(0, self.cols):
if self.maze[0][c] == self.FLOOR:
upper = (0, c)
break
for c in range(0, self.cols):
if self.maze[self.rows - 1][c] == self.FLOOR:
lower = (self.rows - 1, c)
break
if start == 'up':
start = upper
finish = lower
else:
start = lower
finish = upper
self.cleanup(cleanup_path=True)
self.walk_forward(1, start[0], start[1])
length = self.maze[finish[0]][finish[1]]
if not isinstance(length, int):
length = 0
if show_path:
self.walk_backward(finish[0], finish[1])
self.cleanup(cleanup_path=False)
else:
self.cleanup(cleanup_path=True)
return length
def save_to_file(self, filename):
with open(filename, 'w') as f:
f.writelines(str(self))
def load_from_file(self, filename):
self.maze = []
with open(filename, 'r') as f:
lines = f.readlines()
for line in lines:
row = []
for c in line.strip():
row.append(c)
self.maze.append(row)
self.rows = len(self.maze)
self.cols = len(self.maze[0]) if self.rows > 0 else 0
def get_maze(self):
return copy.copy(self.maze)
def __str__(self):
as_string = u''
for row in self.maze:
as_string += u''.join([str(s)[-1] for s in row]) + "\n"
return as_string
maze = Maze()
maze.load_from_file(sys.argv[1])
maze.solve(show_path=True)
print str(maze)
import os
class Maze_Crawler:
def __init__(self):
self.maze = []
def load_maze(self, path):
rows = []
with open(path, 'r') as f:
rows = f.readlines()
for i in range(len(rows)):
self.maze.append([])
for j in range(len(rows[i])-1):
self.maze[i].append(rows[i][j])
return self.maze
def get_start_coor(self):
for i in range(len(self.maze)):
for j in range(len(self.maze[i])):
if self.maze[i][j] == 'S':
return i, j
return -1, -1
def solve_maze(self, coor):
x, y = coor
if self.maze[x][y] == '#' or self.maze[x][y] == 'X':
return False
if self.maze[x][y] == 'E':
return True
if self.maze[x][y] != 'S':
self.maze[x][y] = 'X'
if self.solve_maze((x+1, y)):
if self.maze[x][y] != 'S':
self.maze[x][y] = 'v'
elif self.solve_maze((x-1, y)):
if self.maze[x][y] != 'S':
self.maze[x][y] = '^'
elif self.solve_maze((x, y+1)):
if self.maze[x][y] != 'S':
self.maze[x][y] = '>'
elif self.solve_maze((x, y-1)):
if self.maze[x][y] != 'S':
self.maze[x][y] = '<'
else:
return False
return True
def show_solution(self):
for i in range(len(self.maze)):
r = ''
for j in range(len(self.maze[i])):
if self.maze[i][j] == 'X':
r += ' '
else:
r += self.maze[i][j]
print(r)
Maze solving with python shows my answer. However, if you want to do the code yourself the steps are.
1. Start at the entrance.
2. Call the function solve(x,y) with the entrance co-ordinates
3. in solve, return false if the input point has already been handled or is a wall.
4. Mark the current point as handled (tag = 'o')
5. go to the right and call solve on that point. If it returns true, set tag to '>'
6 elif do the same for left and '<'
7 elif do the same for up and '^'
8 elif do the same for down and 'v'
9 else this is a false path, set tag = ' '
10 set the current maze point to tag
11 return (tag != ' ')
Alternatively leave step 9 out and make step 11
return(tag != 'o')
Then search through the maze and replace every 'o' with ' '
You can display the maze both ways so that it will show how you tried to solve it as well as the final answer. This has been used as a Solaris screensaver with the potential paths showing in one color and the actual path in a different color so that you can see it trying and then succeeding.
Recursion is actually a simple idea: to solve a problem, you shrink the problem by one step, then solve the reduced problem. This continues until you reach a "base problem" that you know how to solve completely. You return the base solution, then add to the solution returned at each step until you have the full solution.
So to solve n!, we remember n and solve for (n-1)!. The base case is 1!, for which we return 1; then at each return step we multiply by the remembered number (2 * 1! is 2, 3 * 2! is 6, 4 * 3! is 24, 5 * 4! is 120) until we multiply by n and have the full solution. This is actually a pretty pale and anemic sort of recursion; there is only one possible decision at each step. Known as "tail recursion", this is very easy to turn inside-out and convert to an iterative solution (start at 1 and multiply by each number up to n).
A more interesting sort of recursion is where you split the problem in half, solve each half, then combine the two half-solutions; for example quicksort sorts a list by picking one item, dividing the list into "everything smaller than item" and "everything bigger than item", quicksorting each half, then returning quicksorted(smaller) + item + quicksorted(larger). The base case is "when my list is only one item, it is sorted".
For the maze, we are going to split the problem four ways - all solutions possible if I go right, left, up, and down from my current location - with the special feature that only one of the recursive searches will actually find a solution. The base case is "I am standing on E", and a failure is "I am in a wall" or "I am on a space I have already visited".
Edit: for interest's sake, here is an OO solution (compatible with both Python 2.x and 3.x):
from collections import namedtuple
Dir = namedtuple("Dir", ["char", "dy", "dx"])
class Maze:
START = "S"
END = "E"
WALL = "#"
PATH = " "
OPEN = {PATH, END} # map locations you can move to (not WALL or already explored)
RIGHT = Dir(">", 0, 1)
DOWN = Dir("v", 1, 0)
LEFT = Dir("<", 0, -1)
UP = Dir("^", -1, 0)
DIRS = [RIGHT, DOWN, LEFT, UP]
#classmethod
def load_maze(cls, fname):
with open(fname) as inf:
lines = (line.rstrip("\r\n") for line in inf)
maze = [list(line) for line in lines]
return cls(maze)
def __init__(self, maze):
self.maze = maze
def __str__(self):
return "\n".join(''.join(line) for line in self.maze)
def find_start(self):
for y,line in enumerate(self.maze):
try:
x = line.index("S")
return y, x
except ValueError:
pass
# not found!
raise ValueError("Start location not found")
def solve(self, y, x):
if self.maze[y][x] == Maze.END:
# base case - endpoint has been found
return True
else:
# search recursively in each direction from here
for dir in Maze.DIRS:
ny, nx = y + dir.dy, x + dir.dx
if self.maze[ny][nx] in Maze.OPEN: # can I go this way?
if self.maze[y][x] != Maze.START: # don't overwrite Maze.START
self.maze[y][x] = dir.char # mark direction chosen
if self.solve(ny, nx): # recurse...
return True # solution found!
# no solution found from this location
if self.maze[y][x] != Maze.START: # don't overwrite Maze.START
self.maze[y][x] = Maze.PATH # clear failed search from map
return False
def main():
maze = Maze.load_maze("somemaze.txt")
print("Maze loaded:")
print(maze)
try:
sy, sx = maze.find_start()
print("solving...")
if maze.solve(sy, sx):
print(maze)
else:
print(" no solution found")
except ValueError:
print("No start point found.")
if __name__=="__main__":
main()
and when run produces:
Maze loaded:
####################################
#S# ## ######## # # # # #
# # # # # # #
# # ##### ## ###### # ####### # #
### # ## ## # # # #### #
# # # ####### # ### #E#
####################################
solving...
####################################
#S# ## ######## # #>>>>>v# >>v# #
#v#>>v# >>>v #^# >>>>^#>>v#
#>>^#v#####^##v######^# ####### #v#
### #v##>>>^##>>>>>v#^# # ####v#
# #>>>^# #######>>^# ### #E#
####################################
Note that the assignment as given has a few unPythonic elements:
it asks for camelCase function names rather than underscore_separated
it suggests using a global variable rather than passing data explicitly
it asks for find_start to return flag values on failure rather than raising an exception

Conway's Game of Life using Sparse Matrix (Python)

I am writing the game of life in python using a Sparse Matrix. My program takes coordinates from user input and sets the cells at the chosen coordinates to be alive. I have it to where it will print the first generation, but I can't seem to figure out why it wont print any subsequent generations. Any help would be appreciated.
class SparseLifeGrid:
generations = list()
def __init__(self):
"""
"pass" just allows this to run w/o crashing.
Replace it with your own code in each method.
"""
self._mat = list()
self.col = []
self.row = []
def minRange(self):
"""
Return the minimum row & column as a tuple.
"""
for i in range(len(self.row)):
if self.row[i] < self.minRow:
self.minRow = self.row[i]
if self.col[i] < self.minCol:
self.minCol = self.col[i]
min_Range = [self.minRow,self.minCol]
return min_Range
def maxRange(self):
"""
Returns the maximum row & column as a tuple.
"""
for i in range(len(self.row)):
if self.row[i] > self.maxRow:
self.maxRow = self.row[i]
if self.col[i] > self.maxCol:
self.maxCol = self.col[i]
max_Range = [self.maxRow,self.maxCol]
return max_Range
def configure(self,coordList):
"""
Set up the initial board position.
"coordlist" is a list of coordinates to make alive.
"""
# for i in coordList:
# self.setCell(i[0],i[1])
self._mat = list()
self.coordList = coordList
for i in range(len(self.coordList)):
spot = self.coordList[i]
self.row += [spot[0]]
self.col += [spot[1]]
self._mat += [[self.row[i],self.col[i]]]
self.maxRow = self.minRow = self.row[0]
self.maxCol = self.minCol = self.col[0]
def clearCell(self,row, col):
"""
Set the cell to "dead" (False)
"""
self[row,col] = 0
def setCell(self,row, col):
"""
Set the cell to "live" (True") and if necessary, expand the
minimum or maximum range.
"""
self[row,col] = 1
def isLiveCell(self,row,col):
n = len(self.coordList)
for i in range(n):
if (self._mat[i] == [row,col]):
return True
return False
def numLiveNeighbors(self, row,col):
"""
Returns the number of live neighbors a cell has.
"""
neighbors = 0
if self.isLiveCell(row+1,col): #checks below the current cell
neighbors += 1
if self.isLiveCell(row-1,col): #checks above the current cell
neighbors += 1
if self.isLiveCell(row,col+1): #checks to the right of the current cell
neighbors += 1
if self.isLiveCell(row,col-1): #checks to the left of the current cell
neighbors += 1
if self.isLiveCell(row+1,col+1): #checks downwards diagonally to the right of the current cell
neighbors += 1
if self.isLiveCell(row+1,col-1): #checks downwards diagonally to the left of the current cell
neighbors += 1
if self.isLiveCell(row-1,col+1): #checks upwards diagonally to the right of the current cell
neighbors += 1
if self.isLiveCell(row-1,col-1): #checks upawards diagonally to the left of the current cell
neighbors += 1
return neighbors
def __getitem__(self,ndxTuple):
row = ndxTuple[0]
col = ndxTuple[1]
if(self.isLiveCell(row,col)==1):
return 1
else:
return 0
def __setitem__(self,ndxTuple, life):
"""
The possible values are only true or false:
True says alive, False for dead.
Also, check to see if this cell is outside of the maximum row and/or
column. If it is, modify the maximum row and/or maximum column.
"""
ndx = self._findPosition(ndxTuple[0],ndxTuple[1])
if ndx != None:
if life != True:
self._mat[ndx].value = life
else:
self._mat.pop[ndx]
else:
if life != True:
element = _GoLMatrixElement(ndxTuple[0],ndxTuple[1],life)
self._mat.append(element)
def _findPosition(self,row,col):
''' Does a search through the matrix when given the row&col and
returns the index of the element if found
'''
n = len(self._mat)
for i in range(n):
if (row == self._mat[i]) and (col == self._mat[i]):
return i
return None
def __str__(self):
"""
Print a column before and after the live cells
"""
s=""
maxRange=self.maxRange()
minRange=self.minRange()
for i in range(minRange[0]-1,maxRange[0]+2):
for j in range(minRange[1]-1,maxRange[1]+2):
s+=" "+str(self[i,j])
s+="\n"
return s
def getCopy(self):
"""
Return a copy of the current board object, including the max and min
values, etc.
"""
return SparseLifeGrid()
def evolve(self):
"""
Save the current state to the "generations" list.
Based on the current generation, return the next generation state.
"""
self.generations.append(self._mat)
for row in range(len(self.row)):
for col in range(len(self.col)):
if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) == 2)):
self.setCell(row,col)
if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) == 3)):
self.setCell(row,col)
if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) < 2)):
self.clearCell(row,col)
if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) > 3)):
self.clearCell(row,col)
if ((self[row,col] == False) and (self.numLiveNeighbors(row,col) == 3)):
self.setCell(row,col)
self.generations.append(self._mat)
return self._mat
def hasOccurred(self):
"""
Check whether this current state has already occured.
If not, return False. If true, return which generation number (1-10).
"""
for i in range(len(self.generations)):
if len(self.generations) > 0:
print("This is generation",len(self.generations))
return self.generations[i]
else:
print("No Generations")
return False
def __eq__(self,other):
"""
This is good method if we want to compare two sparse matrices.
You can just use "sparseMatrixA == sparseMatrixB" once this method
is working.
"""
pass
class _GoLMatrixElement:
"""
Storage class for one cell
"""
def __init__(self,row,col):
self.row = row
self.col = col
self.next = None #
# Since this node exists, this cell is now alive!
# To kill it, we just delete this node from the lists.
from SparseLifeGrid import SparseLifeGrid
import sys
def readPoints(lifeGrid):
"""
Reads the locations of life and set to the SparseMatrix
"""
print("1. Enter positions of life with row,col format (e.g., 2,3).")
print("2. Enter empty line to stop.")
life=input()
coordList=[]
while life:
points=life.split(",")
try:
coord=[int(points[0]),int(points[1])]
coordList.append(coord)
except ValueError:
print("Ignored input:" + life+ ", row, col not valid numbers")
except:
print("Unexpected error:", sys.exc_info()[0])
print("added, keep entering or enter empty line to stop.")
life=input()
print("Thanks, finished entering live cells")
lifeGrid.configure(coordList)
def main():
"""
Runs for ten generations if a stable (repeating) state is not found.
"""
lifeGrid= SparseLifeGrid()
readPoints(lifeGrid)
patterns=0
i=0
while i <10 :
"""
Evolve to the next generation
"""
lifeGrid.evolve()
print(lifeGrid)
"""
Check whether this generation is a repetition of any of the
previous states.
If yes return the previous matching generation (1-10).
"""
patterns=lifeGrid.hasOccurred()
if patterns != -1:
break
i+=1
if i==10:
print("No pattern found")
else:
print("Pattern found at: " + str(i)+ " of type: " + str(patterns))
main()
The loop only executes once because of this:
patterns=lifeGrid.hasOccurred()
if patterns != -1:
break
patterns will never equal -1 under any circumstance, since hasOccurred can only return False or a member of self.generations or None. As a result, the loop will always break in the first iteration, and no subsequent generations will be printed.
Incidentally, your hasOccurred logic is strange. if len(self.generations) equals zero, then the loop will not get executed at all, and none of your return statements will be evaluated, so the result will be None. If the length is greater than zero, then the condition within the loop is always True, so self.generations[i] is always returned in the first iteration. Perhaps you meant to do:
#changed the name from `hasOcurrence`, since that implies the method can only return True or False.
def get_last_occurrence(self):
for i in range(len(self.generations)):
#todo: somehow compare self.generations[i] to the current generation
if the generations match:
return i
#the loop ended without finding a match!
return False
Then, within main:
patterns=lifeGrid.hasOccurred()
if patterns != False:
break

Categories

Resources