zoukankan      html  css  js  c++  java
  • 150+行Python代码实现带界面的数独游戏

    150行代码实现图形化数独游戏

    Github地址,欢迎各位大佬们fork、star啥的,感谢;

    今天闲着没事干,以前做过html+js版的数独,这次做个python版本的,界面由pygame完成,数独生成由递归算法实现,由shuffle保证每次游戏都是不一样的情况,have fun;

    功能列表:

    • 图形化的数独游戏;
    • python实现,依赖pygame库;
    • 随机生成游戏,每次运行都不一样;
    • 数字填入后的正确性判断以及颜色提示;
    • 显示剩余需填入的空格,已经操作的次数;
    • 难度可选,通过修改需要填入的空的数量;

    游戏界面

    初始界面

    过程中界面

    运行方式

    python main.py 15
    

    这里的15表示需要填入的空格数量为15,理论上这个值越大,难度就越高,大家可以随机调整,或者设置容易、简单、困难、地狱等对应不同的值即可,很方便修改;

    程序分析

    界面部分

    这部分很简单的通过pygame来实现,主要使用了其中的主循环、鼠标键盘监听、画矩形线条、字体、颜色控制等,理解起来很容易,对于这部分不太熟悉的同学,这样理解就好:pygame的主循环中一方面负责接收用户输入,一般就是鼠标和键盘,另一方面负责实时更新界面显示内容

    对于界面上各部分内容的绘制的函数封装

    # 绘制背景部分,这里就是9*9的九宫格
    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 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))
    

    主循环中对上述函数的调用以及鼠标键盘事件处理

    # 主循环,负责监听鼠标键盘时间,以及刷新界面内容,以及检查是否赢得了游戏
    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()
    

    生成表示数独的二维数组

    相对于界面部分,这部分在逻辑上要难一些,思路以递归为核心,辅以随机性,得到一个每次生成都不一致的数独游戏,生成思路简单描述如下:

    1. 遍历每个空格,填入目前为止合法的数字;
    2. 如果有数字可以填入,则继续向下一个空格;
    3. 如果没有数字可以填入,表示之前的数字有问题,则结束递归;
    4. 当递归到最后一个格子的下一个时,表示已经生成完毕,返回即可;
    5. 这个过程中对1~9这九个数字的遍历数字会经过shuffle处理,保证随机性而不是每次都得到同一个合法的数独数组;

    生成过程代码

    递归的一个优势是通常代码都很短,当然阅读性不强,欢迎大佬们改为循环;

    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
    

    随机覆盖数独数组中的N个位置

    • matrix_all表示整个数独数组
    • matrix_blank表示部分被替换为0的用于显示的数组
    • blank_ij表示被覆盖位置的i和j
    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
    

    最后附上全部代码

    大家也可以直接从我的Github仓库fork下来直接运行;

    main.py:主流程+界面+执行

    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:生成数独数组部分

    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 _matrixs
        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)))
    

    总结

    如果刻意减少代码的话,实际可以控制在100行以内,不过也没有这个必要,不过这也充分展示了python的强大,在很短的时间和空间上完成一些看似复杂的工作,这个例子供一些同学上手python个人觉得还是不错的,没有太复杂的用法,对界面开发有一点点了解,对递归有一些理解基本就能完全掌握这份代码,希望大家玩的开心,挑战一下50个空格呗,哈哈,反正我没通过,太难了。。。。

    最后

    大家可以到我的Github上看看有没有其他需要的东西,目前主要是自己做的机器学习项目、Python各种脚本工具、有意思的小项目以及Follow的大佬、Fork的项目等:
    https://github.com/NemoHoHaloAi

  • 相关阅读:
    Max History CodeForces
    Buy a Ticket CodeForces
    AC日记——字符串的展开 openjudge 1.7 35
    AC日记——回文子串 openjudge 1.7 34
    AC日记——判断字符串是否为回文 openjudge 1.7 33
    AC日记——行程长度编码 openjudge 1.7 32
    AC日记——字符串P型编码 openjudge 1.7 31
    AC日记——字符环 openjudge 1.7 30
    AC日记——ISBN号码 openjudge 1.7 29
    AC日记——单词倒排 1.7 28
  • 原文地址:https://www.cnblogs.com/helongBlog/p/12627863.html
Copyright © 2011-2022 走看看