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


Posted in Python onApril 04, 2020

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

功能列表:

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

 游戏界面

初始界面

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

过程中界面

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

运行方式

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~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

到此这篇关于150行Python代码实现带界面的数独游戏的文章就介绍到这了,更多相关Python 数独游戏内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

作者:Ho Loong
Github:https://github.com/NemoHoHaloAi
Kaggle:https://www.kaggle.com/holoong9291

Python 相关文章推荐
Django中URLconf和include()的协同工作方法
Jul 20 Python
python嵌套函数使用外部函数变量的方法(Python2和Python3)
Jan 31 Python
解决Python中定时任务线程无法自动退出的问题
Feb 18 Python
Python玩转PDF的各种骚操作
May 06 Python
numpy数组广播的机制
Jul 12 Python
tensorflow实现测试时读取任意指定的check point的网络参数
Jan 21 Python
Python 中如何写注释
Aug 28 Python
Python利用Pillow(PIL)库实现验证码图片的全过程
Oct 04 Python
python 使用三引号时容易犯的小错误
Oct 21 Python
Python批量删除mysql中千万级大量数据的脚本分享
Dec 03 Python
python 命令行传参方法总结
May 25 Python
Python万能模板案例之matplotlib绘制甘特图
Apr 13 Python
Python decorator拦截器代码实例解析
Apr 04 #Python
浅谈Python中os模块及shutil模块的常规操作
Apr 03 #Python
Python实现多线程下载脚本的示例代码
Apr 03 #Python
python实现将两个文件夹合并至另一个文件夹(制作数据集)
Apr 03 #Python
pycharm安装及如何导入numpy
Apr 03 #Python
解决pyPdf和pyPdf2在合并pdf时出现异常的问题
Apr 03 #Python
Python利用PyPDF2库获取PDF文件总页码实例
Apr 03 #Python
You might like
详解WordPress中简码格式标签编写的基本方法
2015/12/22 PHP
yii实现model添加默认值的方法(2种方法)
2016/01/06 PHP
session 加入redis的实现代码
2016/07/15 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
jquery模拟按下回车实现代码
2011/09/20 Javascript
javascript版2048小游戏
2015/03/18 Javascript
解决jquery实现的radio重新选中的问题
2015/07/03 Javascript
Angularjs注入拦截器实现Loading效果
2015/12/28 Javascript
深入理解JS函数的参数(arguments)的使用
2016/05/28 Javascript
JS中使用变量保存arguments对象的方法
2016/06/03 Javascript
详解Angular2中的编程对象Observable
2016/09/17 Javascript
bootstrap中添加额外的图标实例代码
2017/02/15 Javascript
详解AngularJs ui-router 路由的简单介绍
2017/04/26 Javascript
Angular directive递归实现目录树结构代码实例
2017/05/05 Javascript
JS中的数组转变成JSON格式字符串的方法
2017/05/09 Javascript
layer.open组件获取弹出层页面变量、函数的实例
2019/09/25 Javascript
浅谈Vue使用Elementui修改默认的最快方法
2020/12/05 Vue.js
mapboxgl实现带箭头轨迹线的代码
2021/01/04 Javascript
python使用7z解压软件备份文件脚本分享
2014/02/21 Python
在Python中操作字符串之replace()方法的使用
2015/05/19 Python
分享给Python新手们的几道简单练习题
2017/09/21 Python
Python Nose框架编写测试用例方法
2017/10/26 Python
Python中pip更新和三方插件安装说明
2018/07/08 Python
Django实现跨域的2种方法
2019/07/31 Python
Python学习笔记之For循环用法详解
2019/08/14 Python
深入浅析Python代码规范性检测
2020/07/31 Python
python删除文件、清空目录的实现方法
2020/09/23 Python
财务管理专业自荐信范文
2013/12/24 职场文书
查摆问题对照检查材料
2014/08/28 职场文书
2014年小学生迎国庆65周年演讲稿
2014/09/27 职场文书
2015年世界急救日宣传活动方案
2015/05/06 职场文书
婚礼必备主持词范本!
2019/07/23 职场文书
pygame面向对象的飞行小鸟实现(Flappy bird)
2021/04/01 Python
详解Redis瘦身指南
2021/05/26 Redis
python 实现图片特效处理
2022/04/03 Python
python APScheduler执行定时任务介绍
2022/04/19 Python