Building a Sudoku Game in Python with Tkinter — Codes With Pankaj
Sudoku is a popular number puzzle game that challenges players to fill a 9x9 grid with digits so that each column, each row, and each of the nine 3x3 subgrids contain all of the digits from 1 to 9. In this blog post, we’ll walk through the process of creating a simple Sudoku game using Python and the Tkinter library.
Setting Up the Environment
Before diving into the code, make sure you have Python installed on your machine. Additionally, Tkinter, the standard GUI toolkit for Python, is required. You can install Tkinter using the following command:
pip install tk
The SudokuGame Class
The core of our Sudoku game is encapsulated in a class named SudokuGame
. This class manages the game logic and the Tkinter GUI. Let's break down some key aspects of the code:
Initialization and Board Generation
def __init__(self, master):
# ...
self.board = [[0]*9 for _ in range(9)]
self.initialize_board()
self.create_widgets()
The __init__
method initializes the game, creates the Tkinter window (master
), and generates the initial Sudoku board using the initialize_board
method.
Generating a Sudoku Board
def initialize_board(self):
# ...
The initialize_board
method generates a completed Sudoku board and then removes some numbers to create the puzzle. It uses a random shuffling technique to create a unique board each time a new game starts.
Creating Tkinter Widgets
def create_widgets(self):
# ...
The create_widgets
method sets up the Tkinter GUI. It creates an Entry widget for each cell in the Sudoku grid, allowing users to input their guesses directly. The "New Game" button triggers the new_game
method to reset the game.
Handling User Input
def button_click(self, row, col):
# ...
The button_click
method is called when a user clicks on a cell. If the cell is empty, the user can enter a number. The input is validated, and if it's a valid move, the board is updated. The check_win
method is called to check if the puzzle is complete.
Checking for a Win
def check_win(self):
# ...
The check_win
method verifies if the current board configuration satisfies the Sudoku rules, confirming whether the puzzle is solved. If true, a congratulations message is displayed.
Running the Application
if __name__ == "__main__":
root = tk.Tk()
app = SudokuGame(root)
root.mainloop()
The final block of code initializes the Tkinter root window and starts the main event loop to run the application.
Complete Code :
import tkinter as tk
from tkinter import messagebox
import random
class SudokuGame:
def __init__(self, master):
self.master = master
self.master.title("Sudoku Game - codeswithpankaj")
self.master.geometry("800x800")
self.board = [[0]*9 for _ in range(9)]
self.initialize_board()
self.create_widgets()
def initialize_board(self):
# Generate a completed Sudoku board
base = list(range(1, 10))
random.shuffle(base)
self.board[0] = base
for i in range(1, 9):
self.board[i] = self.shift_list(self.board[i - 1], 3)
# Create a puzzle by removing some numbers
for _ in range(40):
row, col = random.randint(0, 8), random.randint(0, 8)
self.board[row][col] = 0
def shift_list(self, lst, n):
return lst[n:] + lst[:n]
def create_widgets(self):
self.frame = tk.Frame(self.master)
self.frame.pack(padx=10, pady=10)
# Create Sudoku board
self.entries = [[None]*9 for _ in range(9)]
for i in range(9):
for j in range(9):
self.entries[i][j] = tk.Entry(self.frame, width=3, font=("Helvetica", 12))
self.entries[i][j].grid(row=i, column=j)
if self.board[i][j] != 0:
self.entries[i][j].insert(0, str(self.board[i][j]))
self.entries[i][j].config(state=tk.DISABLED)
# Bind Enter key to the grid
self.master.bind("<Return>", self.enter_pressed)
# Create New Game button
new_game_button = tk.Button(self.master, text="New Game", command=self.new_game)
new_game_button.pack(pady=10)
def enter_pressed(self, event):
# Handle Enter key press event
focused_widget = self.master.focus_get()
for i in range(9):
for j in range(9):
if self.entries[i][j] == focused_widget:
self.button_click(i, j)
def button_click(self, row, col):
# Handle cell click event
if self.board[row][col] == 0:
try:
entry_value = int(self.entries[row][col].get())
if 1 <= entry_value <= 9:
self.board[row][col] = entry_value
self.entries[row][col].config(state=tk.DISABLED, disabledforeground="black")
if self.check_win():
messagebox.showinfo("Congratulations!", "You've completed the Sudoku puzzle!")
else:
messagebox.showwarning("Invalid Value", "Please enter a number between 1 and 9.")
except ValueError:
messagebox.showwarning("Invalid Value", "Please enter a valid number.")
else:
messagebox.showinfo("Invalid Move", "This cell is fixed. You cannot change its value.")
def new_game(self):
# Reset the game with a new Sudoku board
for i in range(9):
for j in range(9):
self.entries[i][j].delete(0, tk.END)
self.entries[i][j].config(state=tk.NORMAL)
if self.board[i][j] != 0:
self.entries[i][j].insert(0, str(self.board[i][j]))
self.entries[i][j].config(state=tk.DISABLED)
self.initialize_board()
def check_win(self):
# Check if the current board is a winning configuration
for i in range(9):
if not self.is_valid(self.board[i]) or not self.is_valid([self.board[j][i] for j in range(9)]):
return False
for i in range(0, 9, 3):
for j in range(0, 9, 3):
if not self.is_valid(self.get_square(i, j)):
return False
return True
def is_valid(self, lst):
# Check if a list has unique non-zero values
lst = [x for x in lst if x != 0]
return len(lst) == len(set(lst))
def get_square(self, row, col):
# Get values in a 3x3 square
return [self.board[i][j] for i in range(row, row + 3) for j in range(col, col + 3)]
if __name__ == "__main__":
root = tk.Tk()
app = SudokuGame(root)
root.mainloop()
Conclusion
Building a Sudoku game with Python and Tkinter provides a fun and educational project for programming enthusiasts. The code presented in this blog post serves as a starting point for creating more advanced features and customizations. Feel free to explore additional improvements, such as difficulty levels, timer functionalities, or even integrating a solver algorithm.