├── README.md └── sudoku.py /README.md: -------------------------------------------------------------------------------- 1 | # sudoku 2 | This is a simple 4x4 Sudoku game I created in Python for my class. 3 | -------------------------------------------------------------------------------- /sudoku.py: -------------------------------------------------------------------------------- 1 | import turtle 2 | import random 3 | 4 | 5 | # --------------- Made this function to set our coordinates and environment whenever we play ------------- # 6 | def setWorld(): 7 | turtle.setworldcoordinates(-100, -100, 100, 100) 8 | turtle.hideturtle() 9 | turtle.pensize(10) 10 | turtle.speed(0) 11 | turtle.pu() 12 | turtle.goto(-75, -75) 13 | 14 | 15 | # ------------ A function to draw the outer large square ------------- # 16 | def drawGrid(): 17 | for i in range(2): 18 | turtle.fd(150) 19 | turtle.lt(90) 20 | turtle.fd(150) 21 | turtle.lt(90) 22 | 23 | 24 | # ------------ A function to draw squares within the grid ---------# 25 | def drawSquare(): 26 | for i in range(2): 27 | turtle.fd(37.5) 28 | turtle.lt(90) 29 | turtle.fd(37.5) 30 | turtle.lt(90) 31 | 32 | 33 | # ------------- A function to keep drawing squares and moving forward until it reaches a specific x-coordinate --------# 34 | def move_turtle(): 35 | turtle.pd() 36 | while not (turtle.xcor() >= 75): 37 | drawSquare() 38 | turtle.setx(turtle.xcor() + 37.5) 39 | 40 | 41 | # ---------------- 'move_turtle()' draws squares in one row. ---------------------- # 42 | def make_box(): 43 | row_boxes = 0 44 | turtle.sety(-37.5) 45 | while row_boxes != 4: 46 | move_turtle() 47 | turtle.setx(-75) # Start making boxes from the left for each row 48 | turtle.sety( 49 | turtle.ycor() + 37.5) # Once boxes are filled in one row, move y coordinate up. E.g (Makes boxes along row 'D', now move up 50 | row_boxes += 1 # Do this until boxes are made in all four rows. 51 | turtle.pu() 52 | 53 | 54 | # ------------- A function to label the grid -------------- # 55 | def labelGrid(): 56 | rows = ['A', 'B', 'C', 'D'] 57 | cols = ['1', '2', '3', '4'] 58 | 59 | for row_name in rows: 60 | turtle.write(row_name, move=False, align='left', font=('Georgia', 40, 'normal')) 61 | turtle.sety(turtle.ycor() - 40) 62 | 63 | turtle.goto(-65, 75) 64 | for col_name in cols: 65 | turtle.write(col_name, move=False, align='left', font=('Georgia', 40, 'normal')) 66 | turtle.setx(turtle.xcor() + 40) 67 | 68 | 69 | # --------------- A function that uses the turtle class as an eraser ---------- # 70 | def eraseEntry(puzzle): 71 | ''' 72 | Takes in the puzzle 73 | Asks the user if they want to erase the entry before hand 74 | ''' 75 | eraser = turtle.Turtle() 76 | eraser.hideturtle() 77 | eraser.speed(0) 78 | eraser.pu() 79 | 80 | ask_user = turtle.textinput('', 'Do you want to erase the previous entry? (y/n)') 81 | if ask_user in ['y', 'YES', 'yes']: 82 | eraser.goto(turtle.xcor() + 5, 83 | turtle.ycor()) # I had to adjust where the eraser goes accordingly. By trial and error I just had to move it a bit 84 | eraser.pd() # to the right. 85 | eraser.color('white', 'white') 86 | eraser.begin_fill() 87 | eraser.circle(10) # Using a white circle to be my eraser 88 | eraser.end_fill() 89 | 90 | 91 | def take_input(puzzle): 92 | grid_rows = ['A', 'B', 'C', 'D'] 93 | grid_cols = ['1', '2', '3', '4'] 94 | 95 | col_increment_size = [-60, -30, 10, 50] 96 | row_increment_size = [50, 10, -30, -60] 97 | 98 | user_input = turtle.textinput('', 'Enter Row and Column (e.g B4)') 99 | 100 | x_cor = 0 101 | y_cor = 0 102 | 103 | for r in range(0, len(grid_rows)): 104 | if grid_rows[r] == user_input[0]: # First 'element' of the user's input will be the row. E.g B1 105 | y_cor = row_increment_size[r] 106 | 107 | for c in range(0, len(grid_cols)): 108 | if grid_cols[c] == user_input[1]: # Second 'element' of the user's input will be the column. 109 | x_cor = col_increment_size[c] 110 | 111 | turtle.goto(x_cor, y_cor) 112 | 113 | # Appending the puzzle (which is a nested list) 114 | 115 | get_input = turtle.textinput('', 'Enter a number and make your move!') 116 | 117 | for i in range(0, len(grid_rows)): 118 | if user_input[0] == grid_rows[i]: 119 | for j in range(0, len(grid_cols)): 120 | if user_input[1] == grid_cols[j]: 121 | if puzzle[i][j] != 0: # Call the eraseEntry function and ask 122 | eraseEntry(puzzle) # Ask the user if they want to erase the entry where there isn't a zero 123 | puzzle[i][j] = int(get_input) 124 | make_move = turtle.write(get_input, move=False, align='left', font=('Arial', 35, 'normal')) 125 | 126 | return puzzle 127 | 128 | 129 | def populatePuzzle(puzzle): 130 | ''' 131 | Takes in a puzzle (which is a nested list) 132 | Returns the grid with the initial numbers inside 133 | ''' 134 | 135 | col_increment_size = [-60, -30, 10, 50] # Starts at A1 (-60,50), A2 (-30,50), A3 (10,50) .... D4 (50,-60) 136 | row_increment_size = [50, 10, -30, -60] 137 | 138 | for row in range(0, len(puzzle)): # Enter the row 139 | for col in range(0, len(puzzle[row])): # Enter the the row and which column the element belongs to 140 | if puzzle[row][col] != 0: # If the initial base is not 0, populate the grid 141 | turtle.goto(col_increment_size[col], row_increment_size[row]) 142 | turtle.write(puzzle[row][col], move=False, align='left', font=('Arial', 35, 'normal')) 143 | 144 | 145 | def row_check(puzzle): 146 | ''' 147 | Assumes that puzzle is a nested list 148 | Returns True if every element in the row is unique 149 | ''' 150 | 151 | check = 0 152 | for row in range(0, len(puzzle)): 153 | if len(puzzle[row]) == len(set(puzzle[row])) and 0 not in puzzle[ 154 | row]: # Using the notion of sets, we look at the length of each row 155 | check += 1 # Check keeps track of how many rows (same for columns and box) 156 | # are unique. If all 4 are unique, return True. 157 | if check == 4: 158 | return True 159 | 160 | else: 161 | return 'Keep trying!' 162 | 163 | 164 | def col_check(puzzle): 165 | ''' 166 | Assumes that puzzle is a nested list 167 | Returns True if every element in the column is unique 168 | ''' 169 | 170 | col_1 = [s[0] for s in puzzle] # Turns each 'row' of the puzzle into a column. Kind of how you invert a matrice 171 | col_2 = [s[1] for s in puzzle] # so for example. col_1 = [A1, B1, C1, D1], col_2 = [A2, B2, C2, D2] and so on. 172 | col_3 = [s[2] for s in puzzle] # After the transformation is done, it applies the checker. The checker looks 173 | col_4 = [s[3] for s in puzzle] # at the length of each column. If every number is unique len(set(col_1)) = 4 and 174 | all_col = [col_1, col_2, col_3, col_4] # if that matches with our actual column return True. 175 | 176 | check = 0 177 | for col in range(0, len(all_col)): 178 | if len(all_col[col]) == len(set(puzzle[col])) and 0 not in all_col: 179 | check += 1 180 | 181 | if check == 4: # 4 because 4 columns 182 | return True 183 | 184 | else: 185 | return 'Keep trying!' 186 | 187 | 188 | def box_check(puzzle): 189 | ''' 190 | Assumes that puzzle is a nested list 191 | Rearragnes the puzzle so that each 'row' is a box. 192 | ''' 193 | 194 | box_1 = [s[0:2] for s in 195 | puzzle[0:2]] # Transforming each row of the puzzle so that all elements are rearranged as boxes 196 | box_2 = [s[2:] for s in puzzle[0:2]] # so for example, box_1 = [A1,A2,B1,B2], box_2 = [A3,A4,B3,B4] and so on 197 | box_3 = [s[0:2] for s in puzzle[2:]] 198 | box_4 = [s[2:] for s in puzzle[2:]] 199 | 200 | all_box = [box_1, box_2, box_3, box_4] 201 | 202 | check = 0 203 | 204 | for box in all_box: 205 | if len(box[0] + box[1]) == len(set(box[0] + box[1])) and 0 not in box[0] + box[1]: 206 | check += 1 207 | 208 | if check == 4: 209 | return True 210 | 211 | 212 | else: 213 | return 'Keep trying!' 214 | 215 | 216 | # ----------------- A function to see if the board is filled ----------------- # 217 | def isboardfilled(puzzle): 218 | ''' 219 | Assumes puzzle is a nested list 220 | Returns True if the number of 'non_zero' elements match the puzzle 221 | ''' 222 | 223 | non_zero = [] 224 | for row in range(len(puzzle)): 225 | for el in range(len(puzzle[row])): 226 | if puzzle[row][el] != 0: 227 | non_zero.append(puzzle[row][el]) 228 | if len(non_zero) == len(puzzle[row]) * len( 229 | puzzle): # len(puzzle[row]) -> Number of elements in each row 230 | return True # len(puzzle) -> Number of rows. 231 | # Therefore total numbers = len(puzzle[row])* len(puzzle) 232 | 233 | 234 | def replay(): 235 | return turtle.textinput('', "Do you want to play again? (y/n)") in ['yes', 'y', "YES"] 236 | 237 | 238 | # ------------------- Pick one of these random puzzles to start the game --------------------------- # 239 | 240 | puzzle_1 = [[3, 4, 1, 2], [0, 0, 0, 0], [0, 0, 0, 0], 241 | [4, 2, 3, 1]] # Solution: [[3,4,1,2],[2,1,4,3],[1,3,2,4],[4,2,3,1]] 242 | puzzle_2 = [[4, 0, 0, 1], [0, 1, 3, 0], [0, 4, 1, 0], 243 | [1, 0, 0, 3]] # Solution: [[4,3,2,1],[2,1,3,4],[3,4,1,2],[1,2,4,3]] 244 | puzzle_3 = [[0, 0, 0, 0], [2, 3, 4, 1], [3, 4, 1, 2], 245 | [0, 0, 0, 0]] # Solution: [[4,1,2,3],[2,3,4,1],[3,4,1,2],[1,2,3,4]] 246 | puzzle_4 = [[0, 2, 4, 0], [1, 0, 0, 3], [4, 0, 0, 2], 247 | [0, 1, 3, 0]] # Solution: [[3,2,4,1],[1,4,2,3],[4,3,1,2],[2,1,3,4]] 248 | puzzle_5 = [[0, 4, 2, 0], [2, 0, 0, 0], [0, 0, 0, 3], 249 | [0, 3, 1, 0]] # Solution: [[3,4,2,1],[2,1,3,4],[1,2,4,3],[4,3,1,2]] 250 | 251 | all_puzzles = [puzzle_1, puzzle_2, puzzle_3, puzzle_4, puzzle_5] 252 | 253 | # --------------------------------------------- Start the game ---------------------------------------------------------- # 254 | 255 | while True: 256 | turtle.reset() # Start with a clean canvas in case user wants to replay 257 | setWorld() 258 | game_won = False 259 | turtle.pd() 260 | drawGrid() 261 | 262 | # --------- Draw the lines in bold, across and down -------- # 263 | turtle.pu() 264 | turtle.goto(-75, 0) 265 | turtle.pd() 266 | turtle.fd(150) 267 | turtle.pu() 268 | turtle.goto(0, 75) 269 | turtle.lt(270) 270 | turtle.pd() 271 | turtle.fd(150) 272 | turtle.pu() 273 | 274 | # -------- Make the turtle go to the left ------------- # 275 | turtle.goto(-75, -75) 276 | 277 | turtle.pensize(2) 278 | make_box() 279 | turtle.goto(-95, 45) # Start by going to the left top of the grid (Labeling starts with 'A', 'B'... etc) 280 | labelGrid() 281 | game_puzzle = all_puzzles[random.randint(0, 4)] # Pick a random puzzle to start the game 282 | populatePuzzle(game_puzzle) 283 | 284 | while game_won != True: 285 | take_input(game_puzzle) 286 | 287 | if isboardfilled(game_puzzle): 288 | confirm_sol = turtle.textinput('', 'Are you sure you want to submit this solution? (y/n)') 289 | if confirm_sol in ['yes', 'y', 'YES']: 290 | if row_check(game_puzzle) == col_check(game_puzzle) == box_check(game_puzzle) == True: 291 | turtle.textinput('', 'Congratulations you have won! (Press any key to continue)') 292 | game_won = True 293 | else: 294 | turtle.textinput('', "Your solution is incorrect. Keep trying! (Press any key to continue)") 295 | 296 | if not replay(): 297 | break 298 | 299 | turtle.done() 300 | --------------------------------------------------------------------------------