How does Recursive Backtracking work? Computerphile sodoku solver - python

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).

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 is this code is reachable in this recursion function?

I'm doing a sudoku solver and found this code online, which used a snimilar method to what I was doing, but I don't understand how grid[y][x] = 0 is able to be reached in this code since it calls solve(grid)in the line before.
I've noticed I get the same output if I put a else statement before it (and as well if it has on less indent)
def solve(grid):
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y, x, n, grid):
grid[y][x] = n
solve(grid)
grid[y][x] = 0 # How is the program able to reach this code if it calls solve(grid) before it's able to be reached?
return
print(np.matrix(grid))
It is a recursive solution. Recursive means that the solve function calls itself. It continues it's code when the program returns from calling.
grid[y][x] = n # current solve changes grid
solve(grid) # call recursively solve
# the function solve has now returned
# and can have changed grid
grid[y][x] = 0 # now the current solve continues
This works because a list like grid is a mutable object which means that the called function can change grid and the caller continues with the changed grid.
There is one condition: there must be some condition inside solve so that after calling itself multiple times the last called solve returns.
Compare it with the well known Fibonacci function:
def F(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return F(n - 1) + F(n - 2)
print(F(6))
It stops calling itself when n==0 or n==1. Before that it keeps calling itself with n-1 and n-2.
I don't understand what bugs you - it's just a plain ordinary function call. Obviously a function can call another function, else we couldn't do much.
NB : what the name solve points to is resolved at runtime - the only thing happening at compile time here is that the generated byte-code knows it has to resolve the name solve and apply the call op to whatever this name resolves to.

Checkers algorithm: how to reduce nested for loops

I’m trying to build a program that plays draughts/checkers. At the moment I’m trying to make the function, that allows the computer to make and evaluate moves. My idea is to have the computer look at all it’s own possible moves and for each of these moves, look at the possible opponents moves and then for each of these moves, again look at it’s own possible moves.
With each ply it will evaluate if the move is good or bad for the player and assign points, at the end it picks the moves with the highest points.
So far I have managed to get a version of this working, but involves a lot of nested for loops. The code is a mess and not very readable at the moment, but this is a simple model of the same concept. Instead of evaluating and producing more lists, it just multiplies by two for the new list.
counter = 0
for x in list:
counter += 1
list_2 = [x * 2 for x in list]
print 'list_2', list_2, counter
for x in list_2:
counter += 1
list_3 = [x * 2 for x in list_2]
print 'list_3',list_3, counter
for x in list_3:
counter += 1
list_4 = [x * 2 for x in list_3]
print 'list_4', list_4, counter
If I run this code, I get what I want, except that I can't easily control the depth of the search without copying in more for loops. I thought recursion might be a way of doing this, but I can’t figure out how to stop the recursion after x levels of search depth.
Is there a better way of getting the same output form the code above, while getting rid of all the for loops? If I can get that to work, I think I can do the rest myself.
Here's an equivalent function that uses recursion. It controls the recursion with two parameters which track the current depth and maximum depth. If current depth exceeds the maximum depth it will return immediately thus stopping the recursion:
def evaluate(l, max_depth, cur_depth=0, counter=0):
if cur_depth > max_depth:
return counter
for x in l:
counter += 1
l2 = [x * 2 for x in l]
print cur_depth, l2, counter
counter = evaluate(l2, max_depth, cur_depth + 1, counter)
return counter
If called with max_depth=2 it will produce the same output except that instead of variable name the current depth is printed.
I thought recursion might be a way of doing this, but I can’t figure out how to stop the recursion after x levels of search depth.
Your intuition is correct, and a simple a way of doing this would be to have an incrementing number passed to each level. When the recursion gets the maximum value then the recursion is completed. A trivial example is below to demonstrate.
def countup(i=0):
print(i)
if i==MAX_COUNT: return
countup(i+1)
For your algorithm, you need a value to represent the board evaluation. For instance in the range [-1,1]. Player A could be said to be winning if the evaluation is -1 and Player B is winning if the evaluation is 1 for example. A recursive algorithm could be as follows.
def evaulate(board, player, depth=0):
if depth==MAX_DEPTH: return hueristicEvaluation(board)
bestMove = None
if player==PLAYER_A:
val=999 # some large value
for move in get_moves():
newboard = board.makeMove(move)
eval, _ = evaluate(newboard, PLAYER_B, depth+1)
if eval < val:
bestMove = move
val = eval
elif player==PLAYER_B:
val=-999 # some large negative value
for move in get_moves():
newboard = board.makeMove(move)
eval, _ = evaluate(newboard, PLAYER_A, depth+1)
if eval > val:
bestMove = move
val = eval
return val, bestMove
This is abstract, but the idea is there. Adjust depending on how your are representing the board or the players. The function hueristicEvaluation could be something as simple as counting the pieces on the board for each player and how close they are to the other side. Remember that this function needs to return a number between [-1,1]
Edge cases to consider, which I didn't take into account:
If all moves are winning and/or losing
If the are NO moves in the position, for example if your pieces are all blocked by your opponent's pieces
Many improvements exist to a simple search like this. Read if you're interested :)
For checkers, perhaps Memoization would speed things up a lot. I'm not sure, but I'd think it would be especially in the beginning. See python's way of doing this.
Pruning
Alpha-beta pruning
Branch and bound

Algorithm for knight's tour on a 6x6 array only works on index [0, 0]

I must mention that this is my first time posting on this site, forgive me if I don't follow this site's guidelines to the tee.
My problem is probably simple but I can't understand it. My knight's tour algorithm recursively finds a path for a knight. It works on index [0,0], it iterates through the spaces of the array perfectly... however, on anything but index [0,0], the program hangs for what seems like an eternity. Here is my code:
# knightstour.py
#
# created by: M. Peele
# section: 01
#
# This program implements a brute-force solution for the Knight's tour problem
# using a recursive backtracking algorithm. The Knight's tour is a chessboard
# puzzle in which the objective is to find a sequence of moves by the knight in
# which it visits every square on the board exactly one. It uses a 6x6 array for
# the chessboard where each square is identified by a row and column index, the
# range of which both start at 0. Let the upper-left square of the board be the
# row 0 and column 0 square.
#
# Imports the necessary modules.
from arrays import *
# Initializes the chessboard as a 6x6 array.
chessBoard = Array2D(6, 6)
# Gets the input start position for the knight from the user.
row = int(input("Enter the row: "))
col = int(input("Enter the column: "))
# Main driver function which starts the recursion.
def main():
knightsTour(row, col, 1)
# Recursive function that solves the Knight's Tour problem.
def knightsTour(row, col, move):
# Checks if the given index is in range of the array and is legal.
if _inRange(row, col) and _isLegal(row, col):
chessBoard[row, col] = move # Sets a knight-marker at the given index.
# If the chessBoard is full, returns True and the solved board.
if _isFull(chessBoard):
return True, _draw(chessBoard)
# Checks to see if the knight can make another move. If so, makes that
# move by calling the function again.
possibleOffsets = ((-2, -1), (-2, 1), (-1, 2), (1, 2), \
(2, 1), (2, -1), (1, -2), (-1, -2))
for offset in possibleOffsets:
if knightsTour(row + offset[0], col + offset[1], move + 1):
return True
# If the loop terminates, no possible move can be made. Removes the
# knight-marker at the given index.
chessBoard[row, col] = None
return False
else:
return False
# Determines if the given row, col index is a legal move.
def _isLegal(row, col):
if _inRange(row, col) and chessBoard[row, col] == None:
return True
else:
return False
# Determines if the given row, col index is in range.
def _inRange(row, col):
try:
chessBoard[row, col]
return True
except AssertionError:
return False
# A solution was found if the array is full, meaning that every element in the
# array is filled with a number saying the knight has visited there.
def _isFull(chessBoard):
for row in range(chessBoard.numRows()):
for col in range(chessBoard.numCols()):
if chessBoard[row, col] == None:
return False
return True
# Draws a pictoral representation of the array.
def _draw(chessBoard):
for row in range(chessBoard.numRows()):
for col in range(chessBoard.numCols()):
print("%4s" % chessBoard[row, col], end = " ")
print()
# Calls the main function.
main()
There is nothing obviously wrong with your code. And, in fact, replacing chessBoard with a list of lists and changing the rest of the code appropriately, it works for all legal inputs.
See this pastebin for the adapted code. See this one for a modified version that just loops over all valid inputs. If you run it, it prints out exactly 36 completed boards.
So, if there is a problem either you're not running the same code you posted here, or there's a bug in your Array2D implementation.
There are a few weird things about your code.
First, you almost never want to check == None. If you really need to check whether something is None, use the is operator, not ==. If all of your "real" values are truthy, just use the value itself as a boolean (because None is falsey). See Programming Recommendations in PEP 8 for details.
Next, you have your global setup split between a main function, and global module scope. Usually you want to do it all in the same place.
Finally, having a recursive function that mutates a global variable is an odd thing to do. Not that it doesn't work in your case (but only because you can only ever run one test before exiting), but usually in a recursive function you want to either pass the value down as an "accumulator" argument, or do everything immutably (by passing copies down and back up).
The reason abarnert's code works is that his function, when rewritten accesses the array using a negative index. so, if you look at the results, they are incorrect (the knight jumps from the top to the bottom of the board).

Halt a recursively called function

I'm trying to halt the for loop below once values (x,y) or (z,2) have been returned so that the value i doesn't keep increasing, and simply halts when the if or elif condition is first
def maxPalindrome(theList):
# students need to put some logic here
maxcomplist = theList[:]
maxcomplist.reverse()
control = len(theList) - 1
# exit if maxPalindrome is True
for i in range(control):
if maxcomplist[:] == theList[:]:
x = 0
y = len(theList)
return (x, y)
break
elif maxcomplist[i:control] == theList[i:control]:
successList = theList[i:control]
z = i
w = len(theList) - z - 1
return (z, w)
How can I accomplish this?
As I wrote in a comment already: that function isn't a recursive one at all.
Recursion means, that a function calls itself to complete it's purpose. This call can be indirect, meaning that the function uses helper function that will call the first function again.
But your code doesn't cover both cases.
A recursive function always have a certain architecture:
the first thing after being called should be to test, if the primitive case (or one primitive case among options) has been reached. if so, it returns.
If not it will compute whatever is needed and pass this results to itself again,
untill the primitive case is reached, and the nested function calls will finish in one after the other.
One well-known usage of recursion is the quicksort algorithm:
def quicksort(alist):
if len(alist) < 2:
return alist # primitive case: a list of size one is ordered
pivotelement = alist.pop()
# compute the 2 lists for the next recursive call
left = [element for element in alist if element < pivotelement]#left = smaller than pivotelemet
right = [element for element in alist if element >= pivotelement]#left = greater than pivotelemet
# call function recursively
return quicksort(left) + [pivotelement] + quicksort(right)
So the "stop" must be the return of a primitive case. This is vital for recursion. You cannot just break out somehow.
I don't understand the question - if I get it right, that what you want already happens. If you return, the function stops running.
Some comments in addition to this answer:
As well, I cannot see where the function is called recursively, nor what
exit if maxPalindrome is True
means. (Is this a comment, maybe?)
Besides, the maxcomplist[:]==theList[:] does not make much sense to me, and seem to be a waste of time and memory, and to have this comparison in each iteration loop doesn't make it faster as well.

Categories

Resources