Function changing the value of argument in Python - python

I am trying to write a program for Conway's Game of Life, and I came across some really weird problems, so I'm going step by step and trying to debug it. There is some weird stuff going on.
If you are not familiar with Conway's game of Life, the rules for determining the next stage is simply:
Any live cell with fewer than two live neighbours dies, as if caused
by under-population.
Any live cell with two or three live neighbours lives on to the next
generation.
Any live cell with more than three live neighbours dies, as if by
overcrowding.
Any dead cell with exactly three live neighbours becomes a live cell,
as if by reproduction.
I'm keeping a list called squareList that has N_ROWS rows and N_COL columns. I reference each element as squareList[i][j].
My get_next(squareList) function returns another list that counts the number of 'neighbors' in each square, and returns another list with the next stage.
Now, onto my problem. Here is a test case that highlights that a function is changing values it is not supposed to:
squareList = init_list(NUM_ROWS, NUM_COL) #sets all values in squareList to zero.
#here, NUM_ROWS = 12 and NUM_COL = 18
squareList[11][17] = 1
squareList[5][7] = 1
squareList[6][7] = 1
squareList[7][7] = 1
squareList[9][2] = 1
squareList[9][3] = 1
squareList[9][4] = 1
print_list(squareList) #prints squareList
nextList = get_next(squareList) #does NOT change squareList
print '\n--------------------------------------\n'
print_list(squareList) #prints squareList again
sys.exit()
What I get when I use my print_list function is:
As you can see, everything touched by the get_next function is set to zero. This shouldn't happen in my mind for two reasons:
It's not what should happen according to the Conway logic in my get_next function (and I really cannot find why it would not work)
My get_next function is setting a nextList variable, it's not supposed to do ANYTHING to squareList !! What am I missing??
Here's the code for my get_next function:
def get_next(squareList): #gets the next list for the game of life
nextList = squareList
for i in range(1,NUM_ROWS - 1):
for j in range(1,NUM_COL-1):
#num of neighbors:
counter = sum( [squareList[i+x][j+y] for x in range(-1,2) for y in range(-1,2) if not (x==y and x == 0)])
#logic for changing:
if squareList[i][j] == 1 and counter < 2: nextList[i][j] = 0
elif squareList[i][j] == 1 and counter > 3: nextList[i][j] = 0
elif squareList[i][j] == 0 and counter == 3: nextList[i][j] = 1
return nextList
My original thought is that it's changing a global variable, but it's not the case. First, python needs to declare global variables it uses within the function, and second, I tried changing the names of the lists, and got the same results.
EDIT: reply to lanzz suggestion:

The first thing you do in your get_next function is to make nextList a reference to the same list that squareList points to. Assignment does not imply copying — nextList = squareList makes both names point to the same actual structure in memory, so any change to nextList will affect squareList as well.
You should use copy.deepcopy to obtain an actual copy of your squareList list.

Related

Range value changing when indexing a list backwards

I'm creating a chess game inside of Python (pygame) and in my validating moves function, I access a list of all possible moves.
However, as I'm removing from that list, I index it backwards. However, when I implement that change, the amount of repetitions undergone by the for loop encompassing the index changes from 20 to 1.
Here's the full function code:
def valid_move_2():
global white_to_move
possible_moves = generate_possible_moves()
print(range(len(possible_moves)))
for i in range(len(possible_moves)-1, -1, -1):
print("possible moves range")
print(str(possible_moves[i][0]))
move(possible_moves[i][0], possible_moves[i][1], possible_moves[i][2], possible_moves[i][3])
white_to_move = not white_to_move
check_check()
if check_check():
possible_moves.remove(possible_moves[i])
white_to_move = not white_to_move
undo_move()
if len(possible_moves) == 0:
if check_check():
checkmate = True
else:
stalement = True
else:
checkmate = False
stalemate = False
return possible_moves
To be specific:
print(range(len(possible_moves)))
This line returns 20.
However this line:
print("possible moves range")
only returns once, meaning the for loop repeats only once.
Where have I gone wrong?
THE ISSUE
The issue lies with this function, where my program gets stuck:
def square_under_attack(posx, posy):
print("SQUARE UNDER ATTACK FUNCTION CALLED")
global white_to_move
white_to_move = not white_to_move
enemy_moves = generate_possible_moves()
white_to_move = not white_to_move
x=0
for opponent_move in enemy_moves:
print(x)
if opponent_move[3] == posx and opponent_move[4] == posy: # if the opponent can move to the square being tested
print("returned true")
return True
x+=1
print("returned false")
return False
In this function, it gets stuck in the for loop. The x value was for troubleshooting to find out how many times the loop is iterated before not returning anything.
x is printed to have a value of 2 without either of the other 2 print functions being called at all.
What's wrong?
print(range(len(possible_moves)))
This shouldn't return 20.
It should return something like
range(0,20)
If so then it's problem of your for loop not range.
possible_moves.remove(possible_moves[i])
This line in particular is problematic.
If you are trying to remove i th element in possible_moves, use del
del possible_moves[i]
Also there are some dangerous bits in your code, one is global variable. Try to find another way to do that without using a global variable.
Another is your loop is iterated by initial length of possible_moves, and in the loop you are possibly removing something from possible_moves. This may cause index error. Easy fix would be to create a new list to keep track of what is deleted and what is not deleted.
If this doesn't solve error then there must be some issues in called method/functions
Try refactoring it with recursion since I think your iteration is very unorderly and possibly has repeated codes all over your code base.
If possible, use list comprehension instead of iteration to make things more concise.
If you want to do something like "Do until list L is empty" then use following pattern
while L:
do_something()
For your problem, chess, I would design it like this:
First define a function for each chess pieces that returns a set of grids they can go on next turn.
For check and checkmate I would look for a union of all the sets which represents all their possible next moves.
I think you are trying to implement your program by trying out every single enemy moves and then undoing it which is incredibly inefficient approach
You should make all the methods very concise and atomic. You shouldn't make them convoluted and do many implicit things under the hood. Keep them simple and logically concise as much as possible

How does Recursive Backtracking work? Computerphile sodoku solver

I'm so confused by backtracking because when the recursive call returns, won't you replace the solution found by replacing the grid back to zero. So even if you find the solution would it not be erased because after calling the solve function you are canceling what you did by replacing the value back to zero. I get the fact that you are backtracking but on the final recursive call that contains all the correct values are you not just replacing everything to 0?
# grid = ..... # defined as a global value,
# a list of 9 lists, 9-long each
def solve():
global grid
for y in range (0, 9):
for x in range (0, 9):
if grid[y][x] == 0:
for n in range(1,10):
if possible(y, x, n):
grid[y][x] = n
solve()
grid[y][x] = 0
return
# edit: missed this final line:
print (np.matrix(grid))
This was the code on Computerphile video by Prof. Thorsten Altenkirch.
This is weird code, but should work with some adaptations:
def solve():
global grid
for y in range(0, 9):
for x in range(0, 9):
if grid[y][x] == 0:
for n in range(1,10):
if possible(y, x, n):
grid[y][x] = n
if solve():
return True # return without reset
grid[y][x] = 0
return False # exhausted all options
return True # this is the deepest and last call with no more zeroes
Here is part of my code:
vlist = PossibleValueAtPosition(row,col) # find possible value at location (row, col)
for v in vlist: # try each possible value
puzzle[row][col] = v
if SolvePuzzle(n+1)==True: # n=81 means all filled then end loop
return True # if get a solution, you return True
puzzle[row][col] = 0 # if above return true, this line will never run
return False # return False for each fail attemp
Main program should like this
if SolvePuzzle(0)==True:
print(puzzle)
else:
print('No solution!')
It's not the final recursive call that contains all the correct values, but (each of) the deepest. Yes, this code enumerates all the solutions to the puzzle with the given board grid, not just the first solution.
For each (y,x) place, if it's empty, we try to place there each of the numbers from 1 through 9 in turn. If the placement was possible on the board as it is so far, we recurse with the changed grid board.
At the deepest level of recursion there were no empty (y,x) places on the board. Therefore we slide through to the print statement. (It could also be replaced by yield True for example, to turn it into a generator. On each next value we'd get from that generator, we'd have a complete solution -- in the changed grid. And when the generator would get exhausted, the grid would be again in its original state.)
When all the numbers from 1 through 9 have been tried, the current invocation has run its course. But the one above it in the recursion chain is waiting to continue its work trying to fill its (y,x) position. We must let it work on the same board it had before it invoked this invocation of solve(). And the only change on the board this invocation did was to change its (y,x) position's value from 0 to 1 through 9. So we must change it back to 0.
This means that the code could be restructured a little bit too, as
def solve():
global grid
for y in range (0, 9):
for x in range (0, 9): # for the first
if grid[y][x] == 0: # empty slot found:
for n in range(1,10): # try 1..9
if possible(y, x, n):
grid[y][x] = n
solve() # and recurse
# (move it here)
grid[y][x] = 0 # restore
return # and return
# no empty slots were found:
# we're at the deepest level of recursion and
# there are no more slots to fill:
yield True # was: print (np.matrix(grid))
Each invocation works only on one (y,x) location, the first empty position that it found by searching anew from the start on the changed board. This search is done by the first two nested loops on y and on x. That is a bit redundant; we know all the positions before this (y,x) are already filled. The code would be better restructured to pass the starting position (y,x) as a parameter to solve.
The paradigm of recursive backtracking is beautiful. Prolog is full of mystique, Haskell will dazzle you with cryptic monads talk (monads are actually just interpretable nestable data), but all it takes here are some nested loops, recursively created!
The paradigm is beautiful, but this code, not so much. A code's visual structure should reflect its true computational structure, but this code gives you an impression that the y-x- loops are the nested loops at work to create the backtracking structure, and they are not (they just implement a one-off linear search for the next empty space in the top-down left-to-right order).
That role is fulfilled by the n in range(1,10) loops. The y-x- loops should be stopped and exited explicitly when the empty space is found, to truly reflect in the code structure what is going on computationally, to make it apparent that the n in range(1,10) loop is not nested inside the y-x- loops, but comes in play after they finish their job.
Another problem is that it just assumes the validity of the numbers given to us in the grid before the very first call to solve(). That validity is never actually checked, only the validity of the numbers which we are placing in the empty cells is checked.
(note: previous versions of this answer were based on an erroneous reading of the code. there were some valid parts in them too. you can find them on the revisions list here).

How can I fix a function that does not take in what it returned?

have tried to use the return feature so when I call the Function it will use the last generated generation of solutions, but instead, it just uses the randomly generated ones that I used for the first generation and doesn't use the newest, this is an attempt of the Genetic algorithm in python on countdown to find a solution using "*,/,+,-" as operators and six numbers that are generated.
Forgive me if this was an overly simple mistake but I couldn't find anything online as to why it wouldn't use the latest Generation/return properly.
Mutate, SecondGen and CurrentPopFitness are both lists of lists, ([[]]) Mutate is ran through a cross over algorithm and mutation is done to it and appended to SecondGen, the Fitness scores are then calculated and appended to CurrentPopFitness along with the solution that gave that fitness, the 50 best solutions are then appended to SecondGen and Mutate is set to equal it which it does as I checked via the print at the end, however when it goes onto the next call of the function Mutate is back to being what it was at the start of the program.
Mutate is made to be 50 long just before the function ends.
I have checked that the second generation at the end is different from what it was for the first generation, However, Mutate (which the contents of the second generation are in at the end) still ends up being the first generation I generated outside of the function; when calling it for the 2nd time and every time after.
Target = random.randint(101,1000)
track2 = 0
Mutate = [[5/6*24-4+3+2][2/5+100*50-7-8]....]
def OffspringMutation(SecondGen,Mutate):
print(len(Mutate))
for x in range(50):
if track2 >= 1:
SecondGen = SecondGen + 1*[[]]
SecondGen[track2].append(CurrentPopFitness[x][1])
SecondGen[track2].append(CurrentPopFitness[x][2])
SecondGen[track2].append(CurrentPopFitness[x][3])
SecondGen[track2].append(CurrentPopFitness[x][4])
SecondGen[track2].append(CurrentPopFitness[x][5])
SecondGen[track2].append(CurrentPopFitness[x][6])
SecondGen[track2].append(CurrentPopFitness[x][7])
SecondGen[track2].append(CurrentPopFitness[x][8])
SecondGen[track2].append(CurrentPopFitness[x][9])
SecondGen[track2].append(CurrentPopFitness[x][10])
SecondGen[track2].append(CurrentPopFitness[x][11])
track2 += 1
Mutate = SecondGen
return(Mutate)
TruFal = True
while TruFal != False:
stop = input("Type X to stop the Genetic Algorithm, otherwise press enter")
if stop == "X":
TruFal = False
OffspringMutation(SecondGen,Mutate)
The expected result is for len(Mutate) to be roughly 60 long at the start (i made it roughly 60 long). Then on the second call of the function(the second generation) len(Mutate) should be 50 long and the contents different from what they were set to at the start.
You're not recording the return value of OffspringMutation. Although you pass the reference to Mutate to the function, this reference is stored in the local variable of the same name you gave it. Therefore any actions to its contents will change the original eg: append, slicing, delete, etc. But by doing Mutate = SecondGen you are not editing the contents of the reference, you are telling the local variable to point to a different reference, unaffecting the original. By this logic something like Mutate[:] = SecondGen (slicing) will change the original, but this is bad practice for a function.
Target = random.randint(101,1000)
track2 = 0
Mutate = [[5/6*24-4+3+2][2/5+100*50-7-8]....]
def OffspringMutation(SecondGen,Mutate):
print(len(Mutate))
for x in range(50):
if track2 >= 1:
SecondGen = SecondGen + 1*[[]]
SecondGen[track2].append(CurrentPopFitness[x][1])
SecondGen[track2].append(CurrentPopFitness[x][2])
SecondGen[track2].append(CurrentPopFitness[x][3])
SecondGen[track2].append(CurrentPopFitness[x][4])
SecondGen[track2].append(CurrentPopFitness[x][5])
SecondGen[track2].append(CurrentPopFitness[x][6])
SecondGen[track2].append(CurrentPopFitness[x][7])
SecondGen[track2].append(CurrentPopFitness[x][8])
SecondGen[track2].append(CurrentPopFitness[x][9])
SecondGen[track2].append(CurrentPopFitness[x][10])
SecondGen[track2].append(CurrentPopFitness[x][11])
track2 += 1
Mutate = SecondGen
return Mutate
TruFal = True
while TruFal != False:
stop = input("Type X to stop the Genetic Algorithm, otherwise press enter")
if stop == "X":
TruFal = False
Mutate = OffspringMutation(SecondGen,Mutate)
also a cleaner method with the same logic as the last loop would be:
while True:
stop = input("Type X to stop the Genetic Algorithm, otherwise press enter")
Mutate = OffspringMutation(SecondGen,Mutate)
if stop == "X":
break

Difficulty with Fizz Buzz

I'm a high highschool student taking an online course for python, and one of the assignments is to create a function similar to fizz buzz, except instead of "fizz" and "buzz", they simply use "three" and "five". I wrote a function but unfortunately it completely failed, and doesn't do its job at all. I've been having a hard time to figure out what to input instead of "for x in range(101):", I think that's where my problem is. Any pointers/guidance would be very helpful, I'm completely lost.
The instructions are here and here is my code:
def student_func(x):
for x in range(101):
if x % 3 == 0 and x % 5 == 0:
return('threefive')
elif x % 3 == 0:
return('three')
elif x % 5 == 0:
return('five')
else:
return x
Edit:A couple of people were recommending me to delete the loop, since it was starting off the function with 0(I should've realized that). However when I took it out the output became None. Am I doing something else wrong, or am I misunderstanding? Thanks for the speedy answers however, I'm still very inexperienced
Code with new error:
def student_func(x):
if x % 3 == 0 and x % 5 == 0:
return('threefive')
elif x % 3 == 0:
return('three')
elif x % 5 == 0:
return('five')
else:
print(x)
So, a few things to note. First, the range(n) function returns an iterator that will go through 0, 1, 2, ..., n-1. Thus, the first value you will iterate over in your code will be 0. Think about how your for loop will handle that.
Second, because you're assigning x to be the values iterated over from the range function, you're never actually testing the x being passed into the function.
Third, note that every branch of the if-elif-else tree inside the loop has a return statement, thus the loop is only going to go around once before returning a value. That sort of defeats the purpose of using a loop.
Lastly, make sure you are aware of how 0 % n evaluates. Think about things and see if you can reach a conclusion about why your program is returning student_func(1) = 'threefive'.
The issue is that the "tester" which tests your function gives the function the number/argument - x. However, what you are doing is testing the function from 0 - 100. The question does not ask you to test your function with numbers from 0 - 100. Therefore, you should remove the for loop.
Why is it giving the output "threefive"? Well, the function first tests in the loop the number 0. It satisfies the first if statement, and returns (so it gets out of the function).
Right now your code is looping through every value from 0 to 100 and returning if the first value (0) is divisible by both three and five, which of course it is. The function is only asking you to check if a single value is divisible by three, or five, or both. Why would you need a loop?
Your function isn't supposed to loop; it's supposed to examine only the single value passed in as the argument. The teacher's function will call your function with a variety of inputs.
Take out the for loop statement and it should work.

Python programming function call scope changes

I am not a newbie to python. But recently I encountered an error, due to some misconception. Someone please help me to clarify it. Entire program is here : http://www.codeskulptor.org/#user39_cFs3Z8mAtf_0.py
I am having a function
def mc_trial(board, player):
"""
Plays a game starting with the given player by making random
moves and alternating between players.
"""
while board.check_win() == None:
# Get a random empty square
empty_squares = random.choice(board.get_empty_squares())
# Move the player in a random position
board.move(empty_squares[0], empty_squares[1], player)
# Switch the player
player = provided.switch_player(player)
# Game has ended
return
scores = [[0 for dummy in range(board.get_dim())] \
for dummy in range(board.get_dim())]
board_clone = board.clone()
for dummy in range(trials):
print board_clone ## Empty board
mc_trial(board_clone, player)
print board_clone #### Here value is changing after function call. How ??
My doubt is "board_clone" is passing to a function mc_trial(). the return statement there is not providing anything relating to return a value except None. But after the call, when I am printing "board_clone" the value is changing. I tried to clarify it with ipython through a sample program. But, there value remains unchanged as in the local scope . For clarification, i run a sample program, there it behaves as i expected.
def func1(var):
x = 0
while x < 1:
var[1:5]
x = x+1
return
var1 = [1,2,3,4,5,6,7,8,9]
print "B F", var1
func1(var1)
print "A F", var1
This is normal Python behaviour. Lists are passed by reference, so if you change the items in a list in a function, the changes will persist:
def f(mylist):
mylist[0] = 1
>>> l = [0, 0, 0]
>>> mylist(l)
>>> l
[1, 0, 0]
The reason your second example isn't changing the list is because the var[1:5] statement doesn't do anything. It just creates a temporary variable which is a slice of the var list, and then throws it away. It's just like saying a + 1; this would create a temporary variable but would not modify a even inside the function, let alone outside of it.
I'd guess your problem is that your "clone" has references to datastructs in the board object. The clone code doesn't appear in the link you posted?.. and as far as I can recall it's not an in-built python thing (I may be wrong there).?
Read this How to clone or copy a list?
and/or this
https://docs.python.org/2/library/copy.html

Categories

Resources