150+ lines of Python code to implement Sudoku game with interface

150 lines of code to implement a graphical Sudoku game

Github address, everyone is welcome to fork, star, etc. Thank you;

I have nothing to do today. I have made an HTML + js version of Sudoku before. This time I will make a Python version. The interface is completed by pygame. The Sudoku generation is implemented by a recursive algorithm. Shuffle ensures that each game is different. , have fun;

function list:

  • Graphical Sudoku game;
  • Python implementation, relying on pygame library;
  • Randomly generated games, different every time you run them;
  • Correctness judgment and color prompts after numbers are filled in;
  • Displays the remaining spaces to be filled in and the number of operations that have been performed;
  • The difficulty is optional by modifying the number of blanks that need to be filled in;

Game Interface

Initial interface

Process Interface

Run mode

python main.py 15

The 15 here means that the number of spaces that need to be filled in is 15. Theoretically, the greater the value, the higher the difficulty. You can adjust it randomly, or set different values such as easy, simple, difficult, hell, etc., which are easy to modify. ;

Program Analysis

Interface part

This part is very simple to implement through pygame. It mainly uses the main loop, mouse and keyboard monitoring, drawing rectangular lines, fonts, color control, etc. It is easy to understand. For students who are not familiar with this part, it is good to understand it this way. :On the one hand, the main loop of pygame is responsible for receiving user input, usually the mouse and keyboard, and on the other hand, it is responsible for updating the interface display content in real time;

Function package for drawing each part of the content on the interface
# Draw the background part, this is the 9*9 nine-square grid
def draw_background():
    # white background
    screen.fill(COLORS['white'])

    # draw game board
    pygame.draw.rect(screen,COLORS['black'],(0,0,300,900),5)
    pygame.draw.rect(screen,COLORS['black'],(300,0,300,900),5)
    pygame.draw.rect(screen,COLORS['black'],(600,0,300,900),5)

    pygame.draw.rect(screen,COLORS['black'],(0,0,900,300),5)
    pygame.draw.rect(screen,COLORS['black'],(0,300,900,300),5)
    pygame.draw.rect(screen,COLORS['black'],(0,600,900,300),5)

# Change the respective backgrounds selected by the user to blue blocks to indicate selection
def draw_choose():
    pygame.draw.rect(screen,COLORS['blue'],(cur_j*100 + 5,cur_i*100 + 5,100-10,100-10),0)

# Draw the numbers in the nine-square grid, including those that already exist and those filled in by the user. Those that are already there are gray. If the numbers filled in by the user are legal, they will be green. Otherwise, they will be red. This is a reminder.
def draw_number():
    for i in range(len(MATRIX)):
        for j in range(len(MATRIX[0])):
            _color = check_color(MATRIX,i,j) if (i,j) in BLANK_IJ else COLORS['gray']
            txt = font80.render(str(MATRIX[i][j] if MATRIX[i][j] not in [0,'0'] else ''),True,_color)
            x,y = j*100 + 30,i*100 + 10
            screen.blit(txt,(x,y))

# Draw the current number of empty cells at the bottom and the number of user operations
def draw_context():
    txt = font100.render('Blank:' + str(cur_blank_size) + ' Change:' + str(cur_change_size),True,COLORS['black'])
    x,y = 10,900
    screen.blit(txt,(x,y))
Calls to the above functions in the main loop and processing of mouse and keyboard events
# Main loop, responsible for monitoring mouse and keyboard time, refreshing interface content, and checking whether the game has been won.
running=True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running=False
            break
        elif event.type == pygame.MOUSEBUTTONDOWN:
            cur_j,cur_i = int(event.pos[0]/100),int(event.pos[1]/100)
        elif event.type == event.type == pygame.KEYUP:
            if chr(event.key) in ['1','2','3','4','5','6','7' ,'8','9'] and (cur_i,cur_j) in BLANK_IJ:
                MATRIX[cur_i][cur_j] = int(chr(event.key))
                cur_blank_size = sum([1 if col==0 or col=='0' else 0 for row in MATRIX for col in row])
                cur_change_size + =1
    # background
    draw_background()
    #choose item
    draw_choose()
    # numbers
    draw_number()
    #point
    draw_context()
    # flip
    pygame.display.flip()

    # check win or not
    if check_win(MATRIX_ANSWER,MATRIX):
        print('You win, smarty ass!!!')
        break

pygame.quit()

Generate a two-dimensional array representing Sudoku

Compared with the interface part, this part is logically more difficult. The idea is based on recursion, supplemented by randomness, to get a Sudoku game that is inconsistent every time it is generated. The generation idea is briefly described as follows:

  1. Traverse each space and fill in the legal numbers so far;
  2. If there are numbers to fill in, continue to the next space;
  3. If there are no numbers to fill in, it means there is a problem with the previous numbers, and the recursion ends;
  4. When recursing to the next one of the last grid, it means that the generation has been completed and just return;
  5. In this process, the traversal numbers of the nine numbers 1 to 9 will be shuffled to ensure randomness instead of getting the same legal Sudoku array every time;
Generate process code

One advantage of recursion is that the code is usually very short, and of course it is not very readable. Bosses are welcome to change it to loops;

def shuffle_number(_list):
    random.shuffle(_list)
    return_list

def check(matrix,i,j,number):
    if number in matrix[i]:
        return False
    if number in [row[j] for row in matrix]:
        return False
    group_i,group_j = int(i/3),int(j/3)
    if number in [matrix[i][j] for i in range(group_i*3,(group_i + 1)*3) for j in range(group_j*3,(group_j + 1)*3)]:
        return False
    return True

def build_game(matrix,i,j,number):
    if i>8 or j>8:
        return matrix
    if check(matrix,i,j,number):
        _matrix = [[col for col in row] for row in matrix]
        _matrix[i][j] = number
        next_i,next_j = (i + 1,0) if j==8 else (i,j + 1)
        for _number in shuffle_number(number_list):
            __matrix = build_game(_matrix,next_i,next_j,_number)
            if __matrix and sum([sum(row) for row in __matrix])==(sum(range(1,10))*9):
                return __matrix
    return None
Randomly cover N positions in the Sudoku array
  • matrix_all represents the entire Sudoku array
  • matrix_blank represents an array for display with parts replaced by 0
  • blank_ij represents the i and j of the covered position
def give_me_a_game(blank_size=9):
    matrix_all = build_game(matrix,0,0,random.choice(number_list))
    set_ij = set()
    while len(list(set_ij))<blank_size:
        set_ij.add(str(random.choice([0,1,2,3,4,5,6,7,8])) + ',' + str(random.choice([0,1,2 ,3,4,5,6,7,8])))
    matrix_blank = [[col for col in row] for row in matrix_all]
    blank_ij = []
    for ij in list(set_ij):
        i,j = int(ij.split(',')[0]),int(ij.split(',')[1])
        blank_ij.append((i,j))
        matrix_blank[i][j] = 0
    return matrix_all,matrix_blank,blank_ij

Attach all codes at the end

You can also fork it directly from my Github repository and run it directly;

main.py: main process + interface + execution

import sys

import pygame
from pygame.color import THECOLORS as COLORS

from build import print_matrix,give_me_a_game,check

def draw_background():
    # white background
    screen.fill(COLORS['white'])

    # draw game board
    pygame.draw.rect(screen,COLORS['black'],(0,0,300,900),5)
    pygame.draw.rect(screen,COLORS['black'],(300,0,300,900),5)
    pygame.draw.rect(screen,COLORS['black'],(600,0,300,900),5)

    pygame.draw.rect(screen,COLORS['black'],(0,0,900,300),5)
    pygame.draw.rect(screen,COLORS['black'],(0,300,900,300),5)
    pygame.draw.rect(screen,COLORS['black'],(0,600,900,300),5)

def draw_choose():
    pygame.draw.rect(screen,COLORS['blue'],(cur_j*100 + 5,cur_i*100 + 5,100-10,100-10),0)

def check_win(matrix_all,matrix):
    if matrix_all == matrix:
        return True
    return False

def check_color(matrix,i,j):
    _matrix = [[col for col in row]for row in matrix]
    _matrix[i][j] = 0
    if check(_matrix,i,j,matrix[i][j]):
        return COLORS['green']
    return COLORS['red']

def draw_number():
    for i in range(len(MATRIX)):
        for j in range(len(MATRIX[0])):
            _color = check_color(MATRIX,i,j) if (i,j) in BLANK_IJ else COLORS['gray']
            txt = font80.render(str(MATRIX[i][j] if MATRIX[i][j] not in [0,'0'] else ''),True,_color)
            x,y = j*100 + 30,i*100 + 10
            screen.blit(txt,(x,y))

def draw_context():
    txt = font100.render('Blank:' + str(cur_blank_size) + ' Change:' + str(cur_change_size),True,COLORS['black'])
    x,y = 10,900
    screen.blit(txt,(x,y))

if __name__ == "__main__":
    # init pygame
    pygame.init()
    
    #contant
    SIZE = [900,1000]
    font80 = pygame.font.SysFont('Times', 80)
    font100 = pygame.font.SysFont('Times', 90)
    
    # create screen 500*500
    screen = pygame.display.set_mode(SIZE)
    
    # variable parameter
    cur_i, cur_j = 0,0
    cur_blank_size = int(sys.argv[1])
    cur_change_size = 0
    
    # matrix abount
    MATRIX_ANSWER,MATRIX,BLANK_IJ = give_me_a_game(blank_size=cur_blank_size)
    print(BLANK_IJ)
    print_matrix(MATRIX)
    
    # main loop
    running=True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running=False
                break
            elif event.type == pygame.MOUSEBUTTONDOWN:
                cur_j,cur_i = int(event.pos[0]/100),int(event.pos[1]/100)
            elif event.type == event.type == pygame.KEYUP:
                if chr(event.key) in ['1','2','3','4','5','6','7' ,'8','9'] and (cur_i,cur_j) in BLANK_IJ:
                    MATRIX[cur_i][cur_j] = int(chr(event.key))
                    cur_blank_size = sum([1 if col==0 or col=='0' else 0 for row in MATRIX for col in row])
                    cur_change_size + =1
        # background
        draw_background()
        #choose item
        draw_choose()
        # numbers
        draw_number()
        #point
        draw_context()
        # flip
        pygame.display.flip()
    
        # check win or not
        if check_win(MATRIX_ANSWER,MATRIX):
            print('You win, smarty ass!!!')
            break
    
    pygame.quit()

build.py: generates the Sudoku array part

import random

def print_matrix(matrix):
    print('-'*19)
    for row in matrix:
        print('|' + ' '.join([str(col) for col in row]) + '|')
    print('-'*19)

def shuffle_number(_list):
    random.shuffle(_list)
    return_list

def check(matrix,i,j,number):
    if number in matrix[i]:
        return False
    if number in [row[j] for row in matrix]:
        return False
    group_i,group_j = int(i/3),int(j/3)
    if number in [matrix[i][j] for i in range(group_i*3,(group_i + 1)*3) for j in range(group_j*3,(group_j + 1)*3)]:
        return False
    return True

def build_game(matrix,i,j,number):
    if i>8 or j>8:
        return matrix
    if check(matrix,i,j,number):
        _matrix = [[col for col in row] for row in matrix]
        _matrix[i][j] = number
        next_i,next_j = (i + 1,0) if j==8 else (i,j + 1)
        for _number in shuffle_number(number_list):
            #_matrixs.append(build_game(_matrix,next_i,next_j,_number))
            __matrix = build_game(_matrix,next_i,next_j,_number)
            if __matrix and sum([sum(row) for row in __matrix])==(sum(range(1,10))*9):
                return __matrix
    #return _matrices
    return None

def give_me_a_game(blank_size=9):
    matrix_all = build_game(matrix,0,0,random.choice(number_list))
    set_ij = set()
    while len(list(set_ij))<blank_size:
        set_ij.add(str(random.choice([0,1,2,3,4,5,6,7,8])) + ',' + str(random.choice([0,1,2 ,3,4,5,6,7,8])))
    matrix_blank = [[col for col in row] for row in matrix_all]
    blank_ij = []
    for ij in list(set_ij):
        i,j = int(ij.split(',')[0]),int(ij.split(',')[1])
        blank_ij.append((i,j))
        matrix_blank[i][j] = 0
    return matrix_all,matrix_blank,blank_ij

number_list = [1,2,3,4,5,6,7,8,9]
matrix = [([0]*9) for i in range(9)]
if __name__ == "__main__":
    print_matrix(build_game(matrix,0,0,random.choice(number_list)))

Summary

If you deliberately reduce the code, you can actually control it to less than 100 lines, but it is not necessary. However, this also fully demonstrates the power of Python. It can complete some seemingly complex work in a short time and space. This example provides some Personally, I think it’s pretty good for students to get started with python. It’s not too complicated to use. If you have a little understanding of interface development and some understanding of recursion, you can basically master this code. I hope you all have fun and challenge 50 spaces. Haha, I failed anyway, it was too difficult. . . .

Last

You can go to my Github to see if there is anything else you need. Currently, it is mainly my own machine learning projects, various Python scripting tools, interesting small projects, and Follow bosses, Fork projects, etc.: https:/ /github.com/NemoHoHaloAi