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:
- Traverse each space and fill in the legal numbers so far;
- If there are numbers to fill in, continue to the next space;
- If there are no numbers to fill in, it means there is a problem with the previous numbers, and the recursion ends;
- When recursing to the next one of the last grid, it means that the generation has been completed and just return;
- 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