The purpose of these code challenges is to help Python beginners gain experience and familiarity with lists, tuples, and sets by thinking through processes of using them to solve or otherwise involve the game Sudoku. A sample Sudoku puzzle is shown at the right. If you are not familiar with Sudoku, see this video and then come back: v=kvU9_MVAiE0″>https://www.youtube.com/watch?v=kvU9_MVAiE0
There are a handful of basic strategies for playing and solving Sudoku. For more insight into the strategies, you might consider Social Sudoku, an Excel program, downloadable from GitHub here: Social Sudoku – an Excel program on GitHub
Your challenges will involve setting up for and implementing these strategies. Most solutions will be written as functions. Note that pages one and two of Big Daddy’s Python Toolbox (free download from GitHub) might be a helpful reference. A solution (not THE solution as yours might be better) to each challenge is provided, as are test lines to make sure we are all on the same page as we go.
Not everything is done in the simplest or shortest way since an effort is made to make the process understandable to beginners.
Lets start with what is given so we can all solve the same problem with the same resources and data: you can copy and paste the following into a new IDE.
# SUDOKU CODE CHALLENGES - given to keep us all on the same page(s) # John Oakey ver: 120721a # VALUES AND CONTAINERS for these challenges - the following lines are given: # An Example New Sudoku Board - list of rows with each element # of a row holding integers for given, or guessed values. The # row elements are columns in order from zero to nine - r zero is not used # but is defined so that column numbers are logical to the programmer. r0=[0, 0,0,0,0,0,0,0,0,0] # There are a lot of ways to structure this data, the most # efficient being a dictionary holding 10 elements, r1=[0, 1,0,0,0,7,0,5,8,3] # with each element being a corresponding row with r2=[0, 0,2,0,9,0,0,6,0,0] # it's data being a list of values by ordered column. r3=[0, 0,8,0,0,3,1,0,2,0] # BUT... by doing it as at left it is VASTLY easier to r4=[0, 0,0,0,4,1,0,0,0,0] # enter, check and visualize a puzzle. r5=[0, 0,0,8,0,0,0,4,0,0] # We "throw away" r0 so when we want to access the first r6=[0, 0,0,0,0,8,6,0,0,0] # row we can slice [1] instead of [0] r7=[0, 0,7,0,5,9,0,0,6,0] # <-- so, this is an actual sudoku puzzle board r8=[0, 0,0,3,0,0,7,0,9,0] r9=[0, 9,1,5,0,6,0,0,0,7] pvr0=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # Note: we could create the # "pvr" (possible value row) lists of lists with: pvr1=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # bl = "=[[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]]" pvr2=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # for i in range(10): pvr3=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # exec("pvr" + str(i) + bl) pvr4=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # but by printing it out as at left a beginner pvr5=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # can better visualize the data structure pvr6=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] pvr7=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] pvr8=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] pvr9=[[0], [0],[0],[0],[0],[0],[0],[0],[0],[0]] # Create a tuple to hold given/guessed rows r0-r9 and one for our all possible value lists t_posted = (r0,r1,r2,r3,r4,r5,r6,r7,r8,r9) # a tuple whose elements are lists t_all_pcv = (pvr0,pvr1,pvr2,pvr3,pvr4,pvr5,pvr6,pvr7,pvr8,pvr9) # pvr = possible values row pvlist = [] # a reusable temporary list to hold possible values fs_all_values = frozenset([1,2,3,4,5,6,7,8,9]) # all valid sudoku values in frozen set, # now a tuple to hold 10 "sub-tuples" with stored box corners toft_box_corners=((0,0),(1,1),(1,4),(1,7),(4,1),(4,4),(4,7),(7,1),(7,4),(7,7)) # toft = tuple of tuples # __________FUNCTIONS______________________________
NOW THE FUN BEGINS! But first, perhaps a line or two about the common starting stuff. Skip this if you have looked at the data given and think you grok the gist.
——————————–
THE SHORT EXPLAINATION
Think of the “r” lists as values in each column for each row that you are either given or guessing. Compare the values across the rows with the playing board – they should be the same. So essentially, those first ten row lists hold a Sudoku puzzle. You can tell they are lists because the data in them is enclosed in brackets – []. We create an empty list called pvlist by assigning the name to an empty set of brackets.
The “pvr” lists will hold lists of possible values (we will get to this later) for each column by row.
It is important to remember that lists are ordered and mutable, so that make them a good container for our data since we will need the data in those lists to stay put in the place we assign them but to be changeable as the game progresses. It is also important to remember that the first element in any list, tuple or set is element # 0. By including a row of nulls for row zero and null (zero) values for column zero data in each row we pay a miniscule price in memory to be able to access rows and columns by their logical number one to nine.
But handling all those rows individually would be a nightmare and programmatically hard to access. So lets collect all those rows in a couple of tuples – a data container which is ordered, but immutable. We just list the rows by name inside a set of parentheses () and separate them by commas. We now come to a bit of a contradiction: tuples can not change once they are created, but if a tuple holds a list, that list CAN change. That is good for us because we can now easily SLICE our way to any specific piece of data we need and retrieve or change it.
For example, if we want to access the data value in 6th row element of t_posted and 4th column element of r5, we use the t_posted tuple like this: t_posted[5][3]. The 6th element of t_posted, which is logically row #5 as we look at a Sudoku board, is our row 5 or [5]. The row lists hold, in column order, the data for the cell identified by row and column and work the same way. In other words, t_posted[row][column]. In our data that value would be 8.
——————————–
THE CHALLENGES
As of 12/5/21 a total of ten challenges have been created:
Function Brief Objective
print_board() Produce a text version of the guessed and given values on the Sudoku board
box_number(row, col) Calculate a box number given tghe row and column of a cell
nums_in_a_row(row): Produce a list of the numbers used (given/guessed) in a row
nums_in_a_col(col): Produce a list of the numbers used (given/guessed) in a column
nums_in_a_box(row, col): Produce a list of the numbers used (given/guessed) in a box
cell_pv(row, col): Produce a list of the values possible for a cell given row and column
board_pv(): Fill the lists in tuple t_all_pcv of possible values for each cell
print_pv_board() Produce a text version of the board showing possible values for all cells
post(row, col, guess): Add a guessed value to a list in the t_posted tuple
mpair_cols(col) Create a function to find and remove “impossible numbers” by finding matched pairs in a column
CHALLENGE #1: Create a function called print_board() to display the given and guessed values.
The print_board() function finds a way to take the data in t_posted and produce a text display that looks something like this:
In addition to exercising your nested “for” skills this is a great opportunity to learn or refresh your use of f-strings. In some Pythonistas’ opinions, f-stings in 3.6 are what finally made Python a complete language. For some pretty good examples, see Big Daddy’s Toolbox Number 3: Python Format Options. It is free to download from GitHub, of course.
You can do this with lots of different variations but DON’T PEEK until you’ve got something that will work with these test lines:
# test1 printing board print_board()
This is an easy one I think. Now DON’T PEEK!
Here is one possible solution:
# function to print given/guessed Sudoku board data def print_board(): print("Columns",end="") # first print the columns title for col in range(1,10): # then the header for each column # for more on f string formatting see Big Daddy's # Formatting Options toolbox print((f"***---{ str(col):}---***").center(15),end="") print() for row in range(1,10): # for every row print(f"Row{str(row)}: ", end="") # we print the title and then for col in range(1,10): # for every column in the row x = str(t_posted[row][col]) # we print the t_posted value print(f" | {x:^12}",end="") print("|") print(" "*7 + "-"*135)
12/6/21 Today we are adding CHALLENGE #2: box_number(row, col) – but not the solution
box_number(row, col) should calculate the box number of a random cell given the row and column position. As an added Hi-Q challenge, try to do it in just 2 or 3 lines of code or as a genius challenge, do it in just ONE line of code! A possible 3 line format would be:
def box_number(row, col): # box = ... and your code goes here return(box)
A solution will be made available in a day or two.
12/7/21 Hey, watch your language! OK OK Challenge 2 was kind-of-but-not-really-a-trick. The solution is on the next page.