I am making a sudoku solver for school and one part of it, is that it needs to be able to solve multiple sudokus in a file. Currently it can solve one at a time, but how would I code it to solve multiple?
board = []
#open the file with puzzles in it
with open('project.txt','r') as file:
for line in file:
board.append(list(map(int,line.strip())))
#check puzzle for empty spaces
def solve(puzzle):
find = find_empty(puzzle)
if not find:
return True
else:
row, col = find
#go through 1 to 9 for each empty space
for vertical in range(1,10):
if valid(puzzle, vertical, (row, col)):
puzzle[row][col] = vertical
#continue to next empty space if number works
if solve(puzzle):
return True
#backtrack until correct number is found
puzzle[row][col] = 0
return False
def valid(puzzle, num, pos):
# Check each vertical row for correct numbers
for vertical in range(len(puzzle[0])):
if puzzle[pos[0]][vertical] == num and pos[1] != vertical:
return False
# Check each horizontal column for correct numbers
for vertical in range(len(puzzle)):
if puzzle[vertical][pos[1]] == num and pos[0] != vertical:
return False
# Check each 3x3 box for correct numbers
box_x = pos[1] // 3
box_y = pos[0] // 3
for vertical in range(box_y*3, box_y*3 + 3):
for horizontal in range(box_x * 3, box_x*3 + 3):
#if any wrong numbers appear try again
if puzzle[vertical][horizontal] == num and (vertical,horizontal) != pos:
return False
#if all numbers are correct solve puzzle
return True
#formatting for how the final board should look
def print_board(puzzle):
#open a new file for the answers to go into
with open('files.txt', 'w') as file:
#each row should have this formatting
for vertical in range(len(puzzle)):
if vertical % 3 == 0 and vertical != 0:
print("- - - - - - - - - - - - - ", file=file)
#each column should have this formatting
for horizontal in range(len(puzzle[0])):
if horizontal % 3 == 0 and horizontal != 0:
print(" | ", end="", file=file)
if horizontal == 8:
print(puzzle[vertical][horizontal], file=file)
else:
print(str(puzzle[vertical][horizontal]) + " ", end="", file=file)
def find_empty(puzzle):
for vertical in range(len(puzzle)):
for horizontal in range(len(puzzle[0])):
if puzzle[vertical][horizontal] == 0:
return (vertical, horizontal) # row, col
return None
#print the final solved puzzles
solve(board)
print_board(board)
the file would be set up like this:
003020600
900305001
001806400
008102900
700000008
006708200
002609500
800203009
005010300
000900800
128006400
070800060
800430007
500000009
600079008
090004010
003600284
001007000
and we don't know how many puzzles there will be in the end. Thanks for any help!
The problem was that you were putting all the puzzles into a single board.
Simply use the empty line (\n) between the puzzles as a delimiter to separate the board.
Besides that, you should use if __name__ == "__main__": to
protects users from accidentally invoking the script when they didn't intend to.
if __name__ == "__main__":
board_list = []
board = []
# open the file with puzzles in it
with open('project.txt', 'r') as file:
for line in file:
# print(line)
if line == "\n":
print("\\n detected")
board_list.append(board)
print(board_list)
board = []
else:
#print("constructing board")
board.append(list(map(int, line.strip())))
board_list.append(board)
for i in range(len(board_list)):
# print the final solved puzzles
solve(board_list[i])
print_board(board_list[i], i)
I have also modified print_board() so that it write the puzzles into different files.
def print_board(puzzle, index):
# take the index of the board to write it to different files
with open(f'files{index}.txt', 'w') as file:
# your code
Related
I'm currently working on a program that allows you to select the size of a game board and then allow players to choose S or O in the goal of making SOS. I've done this with a list of buttons in the gui. Now the issue I'm stuck on is checking whether an SOS has been made while also staying within the index of my lists. The print statements in the code were to help me determine what method was being called, which was another issue that I had with depending on which button was clicked only certain if statements were being called.
def checksos(i, j):
for i in range (len(board[j])):
for j in range(len(board[j])):
if board[i][j]["text"]=='S':
if not i >= len(board[j]):
print("h")
if board[j][i-1]["text"] == 'O' and board[j][i-2]["text"]== 'S':
this will execute if there is a horizontal sos
print("found horizontal sos")
#check if it will go out of boundaries horizontally and vertically
if (not i >= len(board[j])-2) and (not j >= len(board)-2):
print("d")
if board[j+1][i+1]["text"] == 'O' and board[j+2][i+2]["text"] == 'S':
#this will execute if there is a diagonal sos
print("found diagonal sos")
#check if it will go out of boundaries vertically
if not j >= len(board)-2:
print("v")
if board[j+1][i]["text"] == 'O' and board[j+2][i]["text"] == 'S':
#this will execute if there is a vertical sos
print("found vertical sos")
if not j >= len(board)-1:
print("v")
if board[j+1][i]["text"] == 'O' and board[j+2][i]["text"] == 'S':
#this will execute if there is a vertical sos
print("found vertical sos")
if not j >= len(board):
print("v")
if board[j+1][i]["text"] == 'O' and board[j+2][i]["text"] == 'S':
#this will execute if there is a vertical sos
print("found vertical sos")
elif board[i][j]["text"]=='O':
if not i >= len(board[j])-2:
print("not out of bounds")
if board[j][i-1]["text"] == 'S' and board[j][i+1]["text"]== 'S':
#this will execute if there is a horizontal sos
print("found horizontal sos")
#check if it will go out of boundaries horizontally and vertically
if (not i >= len(board[j])-2) and (not j >= len(board)-2):
if board[j+1][i+1]["text"] == 'S' and board[j-1][i-1]["text"] == 'S':
#this will execute if there is a diagonal sos
print("found diagonal sos")
#check if it will go out of boundaries vertically
if not j >= len(board)-2:
if board[j+1][i]["text"] == 'S' and board[j-1][i]["text"] == 'S':
#this will execute if there is a vertical sos
print("found vertical sos")
You're making this harder than it needs to be. In particular, I'd ask why you have each board cell be a dictionary? Why not just store the character itself in board[j][i]? That would make this a lot easier.
What I've done here is, for each row and for each column, convert the whole row or columns into a single string, then search the string for "SOS".
I also do the same for diagonals, but they're a little trickier. If you think about the 5x5 grid I'm using here as an example, the first two strings gather up the NW-to-SE diagonals starting at (2,0), (1,0), (0,0), (0,1), and (0,2). The second two strings gather up the NE-to-SW diagonals starting at (2,0), (3,0), (4,0), (4,1), and (4,2). That should be all diagonals.
def checksos(board):
w = len(board[0])
h = len(board)
# Check for horizontals.
for row in board:
s = ''.join([cell['text'] for cell in row])
if 'SOS' in s:
print("found horizontal sos")
# Check for verticals.
for col in range(w):
s = ''.join([board[i][col]['text'] for i in range(h)])
if 'SOS' in s:
print("found vertical sos")
# Check for diagonals. There are N-2 diagonals in each direction;
# the outermost 2 are too short to hold SOS.
for offset in range(0,w-2):
# Start from the top and go SE. If offset is 1, the
# first string gets 1,0 then 2,1 then 3,2; the other
# string gets 0,1 then 1,2 then 2,3.
s1 = []
s2 = []
s3 = []
s4 = []
for i in range(0,w-offset):
s1.append( board[i+offset][i]['text'] )
s2.append( board[i][i+offset]['text'] )
s3.append( board[i+offset][w-i-1]['text'] )
s4.append( board[h-i-1][i+offset]['text'] )
if 'SOS' in ''.join(s1) or 'SOS' in ''.join(s2) or \
'SOS' in ''.join(s3) or 'SOS' in ''.join(s4):
print("found diagonal sos")
board = []
for i in range(5):
board.append( [{'text':' '} for _ in range(5)] )
board[1][1]['text'] = 'S'
board[1][2]['text'] = 'O'
board[1][3]['text'] = 'S'
board[1][4]['text'] = 'S'
board[2][4]['text'] = 'O'
board[3][4]['text'] = 'S'
board[2][2]['text'] = 'S'
board[3][3]['text'] = 'O'
board[4][4]['text'] = 'S'
checksos(board)
I'm looking to create a function that takes a word and print it in a specified direction:
when n = 0, print vertically, when n = 1 print diagonally.
def word_direction(text, direction=n):
print(word)
use if statments to check what the direction is then write code to print the text in that direction
def word_direction(text,direction):
if direction==0:
for char in text:
print(char)
elif direction==1:
for spaces,value in enumerate(text):
print(" "*spaces,value)
elif direction==2:
print(text)
elif direction==3:
for pos,value in enumerate(text[::-1]):
spaces=len(text)-pos-1
print(" "*spaces,value)
You could try this approach, which uses enumerate() to get the index for each character of the text and then to create a string with a number of spaces equal to the index of the character in text to create the diagonal effect. After the spaces the character itself is concatenated, and finally the string is printed.
When direction = 0 this operation is short-circuited and only char is printed.
By default print() prints a newline character at then end, which is useful for both modes.
def word_direction(text, direction = 1):
for i, char in enumerate(text):
print(''.join((' ' * i * direction, char)))
word_direction('Hello World!', direction = 0) # Prints vertically
word_direction('Hello World!', direction = 1) # Prints diagonally
Output (direction = 0):
H
e
l
l
o
W
o
r
l
d
!
Output (direction = 1):
H
e
l
l
o
W
o
r
l
d
!
This code might help you:
def word_direction(text, direction):
if direction==0:
for ch in text:
print(ch)
elif direction==1:
for i in range(len(text)):
print(' '*i,text[i])
word_direction('Hello World!',0) #prints vertically
word_direction('Hello World!',1) #prints diagonally
I'm trying to make a 'smart' opponent in my Tic Tac Toe program. To do this I've created a 'possible win' function which will decide if there is a possible win in the next turn. My problem when running this code is that on every iteration of the for loop the variable board seems to be changed.
I want to reset potential board to the original board at the start of every iteration which is why I included potential_board = board[:] at the start of the loop. I then edit potential_board but every time the loop repeats this variable is not reset, and board is in fact changed as well. Why is this?
Many thanks!
import random,copy
board = [['o','o',' '],[' ',' ',' '],[' ',' ',' ']]
cols = [['o',' ',' '],['o','',''],['o','','']]
def possible_win(board,player):
""" This function should predict whether a winning move is possible in
the next turn. This could be done by simulating every possible next move
and running check_win() on those positions.
:param board,player: checks a win for the specified player
:return:
"""
spaces = empty_spaces(board)
print('Spaces',spaces)
winning_moves = []
for space in spaces:
potential_board = board[:]
print('PBoard',potential_board)
print(space[0],space[1])
potential_board[space[0]][space[1]] = 'o'
if check_win(potential_board,'o'):
winning_moves.append(space)
return winning_moves
def choose_space(board):
a = True
while a:
col = int(input('Choose your column of 1,2,3: ')) - 1
row = int(input('Choose your row of 1,2,3: ')) - 1
if board[row][col] == ' ':
board[row][col] = 'o'
a = False
else: print('Sorry, try again')
return board
def empty_spaces(board):
empty_spaces = []
ind = 0
for row in board:
ind1 = 0
for space in row:
if space == ' ':
empty_spaces.append((ind, ind1))
ind1 += 1
ind += 1
return empty_spaces
def comp_choose_space(board):
choice = random.choice(empty_spaces(board))
board[choice[0]][choice[1]] = 'x'
return board
def check_win(board,player):
rows = board
columns = construct_cols(board)
for row in board:
# if player fills row win = True
a = ind = 0
for space in row:
if rows[board.index(row)][ind] != player: break
else: a += 1
ind += 1
if a == 3:
return True
for col in columns:
a = ind = 0
for space in col:
if rows[columns.index(col)][ind] != player:
break
else:
a += 1
ind += 1
if a == 3:
return True
if rows[0][0] == player and rows[1][1] == player and rows[2][2] == player \
or rows[0][2] == player and rows[1][1] == player and rows[2][0] == player:
return True
return False
def construct_cols(board):
cols = [['','',''],['','',''],['','','']]
for row in range(len(board)):
for col in range(row):
cols[col][row] = board[row][col] # sounds like this should work
return cols
def print_board(board):
for row in board:
print('| {} {} {} |'.format(row[0],row[1],row[2]))
def main():
turns = 0
board = [[' ',' ',' '],[' ',' ',' '],[' ',' ',' ']]
print_board(board)
win = False
while win == False and turns < 9:
turns += 1
board = choose_space(board)
if check_win(board,'o'): win,winner = True,'won'
board = comp_choose_space(board)
if check_win(board,'x'): win,winner = True,'lost'
print_board(board)
if turns == 9: print('You drew!')
else:
print('{}, you {}'.format('Congratulations' if winner == 'won' else 'Sorry',winner))
print(possible_win(board,'o'))
# print(empty_spaces(board))
# print(check_win(board,'o'))
# print_board(board)
# print(comp_choose_space(board))
# main()
# Future project - make the computer smarter than just randomly choosing a space
# ie seeing how close i am to winning
EDIT:
By using copy.deepcopy() I managed to fix this, but I dont understand why this works and copy.copy() and board[:] did not work? Could somebody explain this?
This is what copy.deepcopy is for. It will traverse the structure, creating copies of each mutable object within. Using a slice [:] or shallow copy duplicated only the top level, leaving the list for each row shared.
Basically, if we start out with a list:
l = [a, b, c]
shallow = l[:]
shallow2 = copy(l)
deep = deepcopy(l)
The two shallow copies have operated only on l, not on a, b or c. They both have the value [a, b, c] but are distinct lists. All of them refer to the same a, b and c objects (the only change from their perspective is that there are more references).
The deep copy has gone deeper and copied each element; it is a new list with the shape [deepcopy(a), deepcopy(b), deepcopy(c)], whatever those values turned into.
I would appreciate if someone helps me understand how to change the Breadth First Search to Depth First Search, or which steps I need to follow.
The algorithm is basically in the next functions:
canPlace
getpositions
loopBoard
CODE:
import sys
import copy
from os import system
#Set the console title
system("title Michael Fiford - Breadth First N-Queen Solver")
class QueenSolver:
#Store for the amount of queens we're placing, or table size
tableSize = 0
#The alphabet, for nice cell referencing on the output
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
#The queue of possible moves that we will create and loop through
queue = []
#Whether or not the solver can be ran
canRun = False
def setup(self, queenNumber):
#Set the number of queens/table size
self.tableSize = queenNumber
#Can run, so long as there are no errors
self.canRun = True
#Show error if there is no solution, or would take too long
if queenNumber < 4:
print "ERROR: A solution is not available for this few number of queens"
self.canRun = False
elif queenNumber > 13:
print "ERROR: This solution would take too long to calculate, and your computer would probably run out of memory first!"
self.canRun = False
#Create an empty table
def blankTable(self):
table = []
for row in xrange(self.tableSize):
new = []
for col in xrange(self.tableSize):
new.append(0);
table.append(new)
return table
#Place a queen in a table
def placeQueen(self, table, row, col):
#Copy the table, as python is annoying and will change both
t2 = copy.deepcopy(table)
t2[row][col] = 1
return t2
#The main program loop
def loopBoard(self):
#The column we are currently looking at
col = 1
#Loop while the queue isn't empty, and there are still move possibilities to explore
while len(self.queue):
#Create a new empty queue
queue2 = []
#Update status
print col, "Queens Placed"
#Loop the queue, looking for positions from each status
#s is an array containing the current queen positions for this status
for s in self.queue:
#Get what moves are available from this status
availableMoves = self.getPositions(s, col)
#If we are placing the last queen, and there are solutions available, finish
if col == self.tableSize -1 and len(availableMoves):
#Clear queue
self.queue = []
#Get the solution (or one of them, we only care about the first one)
s = availableMoves[0]
break;
#Add the possible moves to the new queue
#This a table containing all queens now placed
if len(availableMoves):
queue2 += availableMoves
#Replace the old queue with the new one
self.queue = queue2
#Increase Queen/col counter
col += 1
self.finish(s, col)
#Get an array of moves that are available, given info about the current table, and the current column
def getPositions(self, table, col):
#Create a row counter, and array to contain our position info
row = 0
possiblePositions = []
#Loop all rows on the board
while row < self.tableSize:
#If we can place in this space
if self.canPlace(table, row, col):
#Add the table with the newly added queen to the list of possible moves
possiblePositions.append(self.placeQueen(table, row, col))
row += 1
return possiblePositions
#Check whether or not we can place a queen in a position, given the table and the row and col of the desired position
#Return True if space is available
def canPlace(self, table, row, col):
# - Direction
# Check left/right
x = 0
#Loop across the table
while x < self.tableSize:
if table[x][col]:
return False
x += 1
# | Direction
#Check up/down
y = 0
#Loop down the table
while y < self.tableSize:
if table[row][y]:
return False
y += 1
# / Direction
#Check up right Diagonal
#We can start in the cell 1 up and 1 right of the cell in question, as we have already checked the actual cell in the above 2 checks
x = row + 1
y = col + 1
#Loop up/right through the table
while x < self.tableSize and y < self.tableSize:
if table[x][y]:
return False
x += 1
y += 1
#Check down left Diagonal
#Again, not starting in the cell specified
x = row - 1
y = col - 1
#Loop down/left through the table
while x >= 0 and y >= 0:
if table[x][y]:
return False
x -= 1
y -= 1
# \ Direction
#Check up left diagonal
#Again, not starting in the cell specified
x = row - 1
y = col + 1
#Loop up left through the table
while x >= 0 and y < self.tableSize:
if table[x][y]:
return False
x -= 1
y += 1
#Check down right diagonal
#Again, not starting in the cell specified
x = row + 1
y = col - 1
#Loop down right through the table
while x < self.tableSize and y >= 0:
if table[x][y]:
return False
x += 1
y -= 1
return True
#Output a table to a user, looking all pretty
def display(self, table):
#Max Number Length, so we can indent our table nicely later
mnl = len(str(len(table)))
#New Line
print ""
#Top of the table, E.g " A B C D"
print " "*mnl, " ",
for x in range(self.tableSize):
print self.alphabet[x],
#New Line
print ""
#Row spacer, E.g " * - - - - *
print " " * mnl, " *",
for x in range(self.tableSize):
print "-",
print "*"
#Row Counter
#Print the actual table, with the Queens as 1's, empty space as 0
#Also prefixed by the row number, E.g " 3 | 0 1 0 0 |
x = 1
for row in table:
#If numbers are shorter than the largest number, give them extra spaces so the rows still line up
extraPadding = mnl - len(str(x))
#Show the number prefix, spaces, and | symbol, E.g " 6 | "
print "", x, " "*int(extraPadding) + "|",
#Show the value of the cell (1 or 0)
for col in row:
print col,
#End of the row
print "|"
#Next Row
x += 1
#Show the same row spacer as at the top of the table, E.g " * - - - - *
print " " * mnl, " *",
for x in range(self.tableSize):
print "-",
print "*"
#We're done! Show output to the user
def finish(self, table, col):
#If we found the right number of queens
if col == self.tableSize:
print ""
print "Total of", self.tableSize, "Queens placed!"
print "Solution:"
self.display(table)
else:
print ""
print "ERROR: Could not place all queens for some unknown reason =["
#Run the script
def run(self):
if not self.canRun:
print "ERROR: Can not run"
else:
print ""
print "Working..."
print ""
self.queue = self.getPositions(self.blankTable(), 0)
self.loopBoard()
#Ask the user how many Queens they want to use
def ask():
while True:
print ""
print "How many Queens would you like use? [8]"
input = raw_input()
#Check if the input given is an integer
if input.isdigit():
return int(input)
#If no input is given, use the standard 8
elif input == "":
return 8;
print "ERROR: Invalid Input"
#Run the program
def run():
#Instantiate the solver
qs = QueenSolver()
#While ask hasn't given a valid input
while(not qs.canRun):
qs.setup(ask())
print ""
#GO!
qs.run()
#Prompt the user if they want to run the program again
def prompt():
#Has valid input been received?
while True:
print ""
print "Would you like to run the script again? Please enter Y/N [N]"
input = raw_input()
#Check if the input given is Y or N
if input == "Y" or input == "y":
return True
#Also accept an empty string in place of N
elif input == "N" or input == "n" or input == "":
return False
print "ERROR: Invalid Input"
if __name__ == "__main__":
print ""
print ""
print " #######################################"
print " ## Breadth First Search Queen Solver ##"
print " ## By: Michael Fiford - COMF3 ##"
print " ## Date: 03/12/2013 ##"
print " #######################################"
#Run the script, and prompt them after if they want to run it again
shouldRun = True
while(shouldRun):
run()
shouldRun = prompt()
The difference between DFS and BFS is in the way you are exploring the nodes of the graph.
In the DFS algorithm, you are first exploring the last node added into the stack (last in first out) whereas in the BFS algorithm, you are exploring it on the basis of first in first out (queue).
The resulting change is the code shoud be quite small: in the BFS algorithm, you could use a list to implement the stack, appending new node at the top of the stack and then exploring them:
l=[]
while len(l)>0:
new_node=l.pop()
l.extend(new_node.get_neighbors())
The change is quite small to switch to the BFS algorithm: you switch from a stack to queue. This is implemented in Python in the collections module as a deque (with an efficient implementation of popleft (getting the first item of the list and remove it) and append (appending an item at the end of the queue):
import collections
l=collections.deque()
while len(l)>0:
new_node=l.popleft()
l.extend(new_node.get_neighbors())
Your code can be rewritten to fit with the previous description:
while len(self.queue):
#Create a new empty queue
#queue2 = []
#Update status
print col, "Queens Placed"
#Loop the queue, looking for positions from each status
#s is an array containing the current queen positions for this status
s=queue.pop()
#Get what moves are available from this status
availableMoves = self.getPositions(s, col)
#If we are placing the last queen, and there are solutions available, finish
if col == self.tableSize -1 and len(availableMoves):
#Clear queue
self.queue = []
#Get the solution (or one of them, we only care about the first one)
s = availableMoves[0]
break;
#Add the possible moves to the new queue
#This a table containing all queens now placed
#if len(availableMoves):
# queue2 += availableMoves
queue.extend(availableMoves)
#Replace the old queue with the new one
#self.queue = queue2
#Increase Queen/col counter
col += 1
Let me know if you need more explanations. Hope this helps.
I'm trying to get my code to go through and pop-out after some math has been done. The file being summed is just lists of numbers on separate lines. Could you give me some pointers to make this work because I am stumped.
EDIT:
I'm trying to get the transition from the main function to Checker function working correctly. I also need some help with slicing. The numbers being imported from the file are like this:
136895201785
155616717815
164615189165
100175288051
254871145153
So in my Checker funtion I want to add the odd numbers from left to right together. For example, for the first number, I would want to add 1, 6, 9, 2, 1, and 8.
Full Code:
def checker(line):
flag == False
odds = line[1 + 3+ 5+ 9+ 11]
part2 = odds * 3
evens = part2 + line[2 + 4 +6 +8 +10 +12]
part3 = evens * mod10
last = part3 - 10
if last == line[-1]:
return flag == True
def main():
iven = input("what is the file name ")
with open(iven) as f:
for line in f:
line = line.strip()
if len(line) > 60:
print("line is too long")
elif len(line) < 10:
print("line is too short")
elif not line.isdigit():
print("contains a non-digit")
elif check(line) == False:
print(line, "error")
To get the odd numbers:
odds = line[1::2]
and the evens:
evens = part2 + line[::2]
Unfortunately none of the parts of your checker function work. It seems you probably need something like this:
def check_sums(line):
numbers = [int(ch) for ch in line] # convert text string to a series of integers
odds = sum(numbers[1::2]) # sum of the odd-index numbers
evens = sum(numbers[::2]) # sum of the even-index numbers
if numbers[-1] == (odds * 3 + evens) % 10:
return True
else:
return False
numbers[1::2] says "get the slice of numbers from 1 until the end with step 2", while numbers[::2] says "get the slice of numbers from the beginning until the end with step 2". (See this question or the documentation for more explanation.)
Note that the operator for a modulus is x % 10. I assume that's what you're trying to do with evens * mod10. In your original code you also subtract 10 (last = part3 - 10), but that makes no sense so I have omitted that step.
This returns the following for the input lines you mention:
print(check_sums('136895201785')) # >>> False
print(check_sums('155616717815')) # >>> True
print(check_sums('164615189165')) # >>> True
print(check_sums('100175288051')) # >>> False
print(check_sums('254871145153')) # >>> False
Your main function is fine except that it refers to the function as check when you had named it checker.