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 相关文章推荐
pyv8学习python和javascript变量进行交互
Dec 04 Python
浅析python中的分片与截断序列
Aug 09 Python
基于ID3决策树算法的实现(Python版)
May 31 Python
Python 模拟购物车的实例讲解
Sep 11 Python
利用python实现微信头像加红色数字功能
Mar 26 Python
Python实现决策树C4.5算法的示例
May 30 Python
python函数与方法的区别总结
Jun 23 Python
Django 多表关联 存储 使用方法详解 ManyToManyField save
Aug 09 Python
Python图像处理模块ndimage用法实例分析
Sep 05 Python
Python selenium如何打包静态网页并下载
Aug 12 Python
基于python实现复制文件并重命名
Sep 16 Python
如何利用python正则表达式匹配版本信息
Dec 09 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
php使用fputcsv实现大数据的导出操作详解
2020/02/27 PHP
Js 获取HTML DOM节点元素的方法小结
2009/04/24 Javascript
jqeury eval将字符串转换json的方法
2011/01/20 Javascript
JS获取整个页面文档的实现代码
2011/12/15 Javascript
JQuery入门—JQuery程序的代码风格详细介绍
2013/01/03 Javascript
jQuery实现的Div窗口震动特效
2014/06/09 Javascript
jQuery中eq()方法用法实例
2015/01/05 Javascript
jQuery实现炫酷的鼠标轨迹特效
2015/02/01 Javascript
JavaScript中的small()方法使用详解
2015/06/08 Javascript
Bootstrap每天必学之表单
2015/11/23 Javascript
不间断循环滚动效果的实例代码(必看篇)
2016/10/08 Javascript
微信小程序 删除项目工程实现步骤
2016/11/10 Javascript
教你用十行node.js代码读取docx的文本
2017/03/08 Javascript
vue实现移动端悬浮窗效果
2018/12/01 Javascript
vue 父组件给子组件传值子组件给父组件传值的实例代码
2019/04/15 Javascript
判断JavaScript中的两个变量是否相等的操作符
2019/12/21 Javascript
VUE实现Studio管理后台之鼠标拖放改变窗口大小
2020/03/04 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
2020/04/11 Javascript
js删除对象中的某一个字段的方法实现
2021/01/11 Javascript
[55:44]OG vs NAVI 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
[08:17]Ti9 现场cosplay
2019/09/10 DOTA
python网络编程之数据传输UDP实例分析
2015/05/20 Python
Python使用defaultdict读取文件各列的方法
2017/05/11 Python
python中WSGI是什么,Python应用WSGI详解
2017/11/24 Python
浅析Python数据处理
2018/05/02 Python
简单了解python中对象的取反运算符
2019/07/01 Python
python分别打包出32位和64位应用程序
2020/02/18 Python
python实现数字炸弹游戏
2020/07/17 Python
美国在线和移动免费会员制批发零售商:Boxed(移动端的Costco)
2020/01/02 全球购物
表彰先进集体通报
2014/01/12 职场文书
好员工观后感
2015/06/17 职场文书
职场领导同事生日简短祝福语
2019/08/06 职场文书
Vue详细的入门笔记
2021/05/10 Vue.js
Python django中如何使用restful框架
2021/06/23 Python
opencv深入浅出了解机器学习和深度学习
2022/03/17 Python
十大最强岩石系宝可梦,怪颚龙实力最强,第七破坏力很强
2022/03/18 日漫