前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >150+行Python代码实现带界面的数独游戏

150+行Python代码实现带界面的数独游戏

作者头像
HoLoong
发布2020-09-21 18:50:20
1.5K0
发布2020-09-21 18:50:20
举报
文章被收录于专栏:尼莫的AI小站尼莫的AI小站

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个空格呗,哈哈,反正我没通过,太难了。。。。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 150行代码实现图形化数独游戏
    • 游戏界面
      • 初始界面
      • 过程中界面
    • 运行方式
      • 程序分析
        • 界面部分
        • 生成表示数独的二维数组
      • 最后附上全部代码
        • main.py:主流程+界面+执行
        • build.py:生成数独数组部分
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档