可以选关,记录最高分,可以提示错误的格子或别的乱七八糟的
"""
@author: Bre Athy
@contact: https://www.zhihu.com/people/you-yi-shi-de-hu-xi
@productware: PyCharm
@file: SodokuMaker.py
@time: 2020/4/8 16:06
"""
import pygame, sys, time, copy, SodukuUtil, json
class Color():
black = (0, 0, 0)
white = (255, 255, 255)
red = (200, 0, 0)
green = (0, 200, 0)
bright_red = (255, 0, 0)
bright_green = (0, 255, 0)
grey = (88, 88, 88)
mWhite = (255,251,240)
blue = (0, 0, 255)
class SodokuGame():
running = True # 控制退出
fontFamily = "comicsansms" # 默认字体
difficulty = 1 # 难度控制,三个难度:简单1 普通2 困难3
level = 1 # 关卡控制,1-10关
title = "Soduku Game" # 游戏标题
isPaused = False # 暂停模式
isAnswer = False # 答案模式
errorCount = 0 # 错误次数
recordFile = "record.json" # 记录文件
score = -1 # 得分
def __init__(self):
# 游戏页面初始化
pygame.init()
self.clock = pygame.time.Clock()
pygame.display.set_caption('数独游戏') # 窗口标题
display_size = self.display_width, self.display_height = 800, 600 # 设置窗口大小
self.gameScreen = pygame.display.set_mode(display_size)
self.timeStart = int(time.time()) # 时间记录器
# 其他游戏细节
self.timeColor = Color.grey
self.pausedText = "Pause"
self.pausedColor = Color.black
# 数独初始化
self.Sodoku = [[0] * 9 for i in range(9)] # 数独数组
self.Game = [[0] * 9 for i in range(9)] # 游戏数组
self.Answer = [[0] * 9 for i in range(9)] # 答案数组
self.position = (-1, -1) # 选中位置
self.errorCursor = (-1, -1) # 错误指示
try:
with open(self.recordFile, "r") as f:
self.record = json.load(f)
assert len(self.record) == 30
except:
self.record = [-1] * 30 # 读取记录
def start(self):
# 游戏运行界面
self.draw = self.Index
# 开启游戏
while self.running:
for event in pygame.event.get():
# 退出
if event.type == pygame.QUIT:
self.exit()
# 检测输入
if (event.type == pygame.KEYDOWN) and (pygame.K_1 <= event.key <= pygame.K_9):
if (self.position != (-1, -1)) and (self.Sodoku[self.position[0]][self.position[1]] == 0):
self.Game[self.position[0]][self.position[1]] = event.key - 48
# 填充背景
self.gameScreen.fill(Color.white)
# 绘制游戏页面
self.draw()
# 更新界面
pygame.display.flip()
self.clock.tick(120)
def exit(self):
# 退出游戏
# self.file.close()
pygame.quit()
sys.exit()
def fontRender(self, text, color, size, position, ttf = "comicsansms"):
# 字体渲染
font = pygame.font.SysFont(ttf, size)
surf = font.render(text, True, color)
surfRect = surf.get_rect()
surfRect.center = position
self.gameScreen.blit(surf, surfRect)
def buttonRender(self, text, rectColor, floatingColor, fontColor, position, rectSize, fontSize, ttf = "comicsansms", border=0, action=None, actionArgs=None, timeDelay = True):
# 按钮渲染
# 绘制浮动矩形 rect(surface, color, rect, width)
mousePositon = pygame.mouse.get_pos() # 鼠标位置
click = pygame.mouse.get_pressed() # 鼠标按键
if (position[0]+rectSize[0] > mousePositon[0] > position[0]) and (position[1]+rectSize[1] > mousePositon[1] > position[1]):
pygame.draw.rect(self.gameScreen, floatingColor, (position, rectSize), border)
if click[0] == 1 and action != None:
if timeDelay:time.sleep(0.2)
if actionArgs != None:action(actionArgs)
else:action()
else:
pygame.draw.rect(self.gameScreen, rectColor, (position, rectSize), border)
# 绘制字体
fontPosition = position[0] + rectSize[0] / 2, position[1] + rectSize[1] / 2
self.fontRender(text, fontColor, fontSize, fontPosition, ttf=ttf)
def Index(self):
# 绘制主页
y = self.display_height / 1.5
rectSize = self.display_width / 8, self.display_height / 12
fontSize = 20
def drawIndex():
self.fontRender("Soduku Game", Color.black, 115,(self.display_width / 2, self.display_height / 3)) # 主页字体
self.buttonRender("Start", Color.green, Color.bright_green, Color.black, (150, y), rectSize, fontSize, action=self.Difficulty_select) # 开始按钮
self.buttonRender("Quit", Color.red, Color.bright_red, Color.black, (550, y), rectSize, fontSize, action=self.exit) # 关闭按钮
self.draw = drawIndex
def Difficulty_select(self):
# 绘制选择难度的页面
x = 350
rectSize = self.display_width / 7, self.display_height / 12
fontSize = 20
def drawGameSelect():
self.fontRender("Select difficulty", Color.black, 35, (self.display_width/2, self.display_height/6))
self.buttonRender("back", Color.red, Color.bright_red, Color.black, (50, 550), rectSize, fontSize, action=self.Index)
self.buttonRender("easy", Color.green, Color.bright_green, Color.black, (x, 200), rectSize, fontSize, action=self.Stage_select, actionArgs=1)
self.buttonRender("general", Color.red, Color.bright_red, Color.black, (x, 300), rectSize, fontSize, action=self.Stage_select, actionArgs=2)
self.buttonRender("difficult", Color.bright_green, Color.green, Color.black, (x, 400), rectSize, fontSize, action=self.Stage_select, actionArgs=3)
self.draw = drawGameSelect
def generateSoduku(self):
# 调用接口生成数独数据
mkr = SodukuUtil.SudoKuMaker(self.getStage())
self.Sodoku = mkr.getArr() # 数独数据
self.Answer = mkr.getAnswer() # 数独答案
self.Game = copy.deepcopy(self.Sodoku) # 游戏数据
self.uploader = mkr # 数据游标
# 关闭模式
self.isAnswer = False
self.isPaused = False
# 重置时间记录器
self.timeStart = int(time.time())
# 重置游标
self.errorCursor = self.position = (-1, -1)
# 重置错误次数
self.errorCount = 0
def enterGame(self, level_num):
# 为游戏界面做准备,布置细节
self.level = level_num
if self.difficulty == 1:
title = "Easy"
elif self.difficulty == 2:
title = "Normal"
else:
title = "Difficult"
self.title = title + " Mode : level " + str(self.level)
# 生成一组数独数据
self.generateSoduku()
# 正式开始游戏
self.draw = self.LevelStart
def Stage_select(self, difficulty):
# 绘制关卡选择页面
self.difficulty = difficulty
def drawStageSelect():
x1 = 250
x2 = 450
rectSize = self.display_width / 7, self.display_height / 12
fontSize = 20
self.fontRender("Select stage", Color.black, 35, (self.display_width / 2, self.display_height / 6))
self.buttonRender("back", Color.red, Color.bright_red, Color.black, (50, 550), rectSize, fontSize, action=self.Difficulty_select)
self.buttonRender("1 level", Color.green, Color.bright_green, Color.black, (x1, 200), rectSize, fontSize, action=self.enterGame, actionArgs=1)
self.buttonRender("2 level", Color.red, Color.bright_red, Color.black, (x1, 270), rectSize, fontSize, action=self.enterGame, actionArgs=2)
self.buttonRender("3 level", Color.green, Color.bright_green, Color.black, (x1, 340), rectSize, fontSize, action=self.enterGame, actionArgs=3)
self.buttonRender("4 level", Color.red, Color.bright_red, Color.black, (x1, 410), rectSize, fontSize, action=self.enterGame, actionArgs=4)
self.buttonRender("5 level", Color.green, Color.bright_green, Color.black, (x1, 480), rectSize, fontSize, action=self.enterGame, actionArgs=5)
self.buttonRender("6 level", Color.green, Color.bright_green, Color.black, (x2, 200), rectSize, fontSize, action=self.enterGame, actionArgs=6)
self.buttonRender("7 level", Color.red, Color.bright_red, Color.black, (x2, 270), rectSize, fontSize, action=self.enterGame, actionArgs=7)
self.buttonRender("8 level", Color.green, Color.bright_green, Color.black, (x2, 340), rectSize, fontSize, action=self.enterGame, actionArgs=8)
self.buttonRender("9 level", Color.red, Color.bright_red, Color.black, (x2, 410), rectSize, fontSize, action=self.enterGame, actionArgs=9)
self.buttonRender("10 level", Color.green, Color.bright_green, Color.black, (x2, 480), rectSize, fontSize, action=self.enterGame, actionArgs=10)
self.draw = drawStageSelect
def LevelStart(self):
# 游戏开始
def renderGrid(position):
# 渲染选中网格
self.position = position
def toPause():
# 暂停
if self.isPaused:
self.isPaused = False
self.pausedText = "Pause"
self.pausedColor = Color.black
self.timeColor = Color.grey
else:
self.isPaused = True
self.pausedText = "Paused"
self.pausedColor = self.timeColor = Color.red
def getAnswer():
# 查看答案
self.isAnswer = True
self.errorCursor = (-1, -1)
self.Game = self.Answer
def submitSoduku():
# 提交答案
if self.isAnswer:return # 答案模式
result = self.uploader.getWrong(self.Game)
if not result:
self.score = int(self.getBackTime())
if self.record[self.level-1] == -1 or self.record[self.level-1] > int(self.getBackTime()):
self.record[self.level-1] = self.score
with open(self.recordFile, "w")as f:
json.dump(self.record, f)
self.draw = self.ShowWin # 成功
else:
self.errorCursor = result
self.errorCount += 1
if self.errorCount > 5:
self.draw=self.ShowLose # 失败
# 绘制标题与时间
self.fontRender(self.title, Color.black, 40, (280 , 30))
self.fontRender("Time: " + self.getBackTime(), self.timeColor, 30, (self.display_width - 130, 40))
# 绘制操作按钮
rectSize = 140, 60
fontSize = 35
self.buttonRender(self.pausedText, Color.green, Color.bright_green, self.pausedColor, (600, 200), rectSize, fontSize, action=toPause)
if self.isAnswer: self.buttonRender("Submit", Color.grey, Color.grey, Color.black, (600, 300), rectSize, fontSize, action=submitSoduku)
else: self.buttonRender("Submit", Color.green, Color.bright_green, Color.black, (600, 300), rectSize, fontSize, action=submitSoduku)
rectSize = 100, 50
fontSize = 20
self.buttonRender("restart", Color.green, Color.bright_green, Color.black, (625, 410), rectSize, fontSize, action=self.generateSoduku)
self.buttonRender("answer", Color.green, Color.bright_green, Color.black, (570, 480), rectSize, fontSize, action=getAnswer)
self.buttonRender("back", Color.green, Color.bright_green, Color.black, (680, 480), rectSize, fontSize, action=self.Stage_select, actionArgs=self.difficulty)
# 绘制九宫格
x,y = 40,70 # 基础位置
length = 500 / 9 # 网格边长
borderWidth = 1 # 边框宽度
a,b = x+self.position[1]*length, y+self.position[0]*length
e,f = x+self.errorCursor[1]*length, y+self.errorCursor[0]*length
points = [(a, b), (a+length, b), (a+length, b+length), (a, b+length)]
## 绘制九宫格内容
if not self.isPaused:
for i in range(9):
for j in range(9):
text = str(self.Game[i][j])
if text == '0':text = ""
position = x+j*length, y+i*length
if self.Game[i][j] == self.Sodoku[i][j]: fontColor = Color.black
else:
if self.isAnswer: fontColor = Color.green
else: fontColor = Color.blue
self.buttonRender(text, Color.white, Color.mWhite, fontColor, position, (length, length), 20, action=renderGrid, actionArgs=(i, j), timeDelay=False)
## 绘制九宫格边线
for i in range(10):
pygame.draw.line(self.gameScreen, Color.black, (x, y+i*length), (x+500, y+i*length), borderWidth)
pygame.draw.line(self.gameScreen, Color.black, (x+i*length, y), (x+i*length, y+500), borderWidth)
## 绘制选中的格子
if self.position != (-1, -1):
pygame.draw.lines(self.gameScreen, Color.bright_red, 1, points, 4*borderWidth)
## 绘制错误游标
if self.errorCursor != (-1, -1):
pygame.draw.line(self.gameScreen, Color.red, (e,f), (e+length, f+length), 2*borderWidth)
pygame.draw.line(self.gameScreen, Color.red, (e+length,f), (e, f+length), 2*borderWidth)
def ShowWin(self):
# 胜利的页面
self.fontRender("You Win",Color.black, 115,(self.display_width / 2, self.display_height / 3))
self.fontRender("Your:"+str(self.score), Color.blue, 50, (self.display_width / 2, 340))
self.fontRender("Best:"+str(self.record[self.level-1]), Color.blue, 30, (self.display_width / 2, 420))
def nextLevel():
if self.level == 10:return
else:
self.level += 1
self.generateSoduku()
self.enterGame(self.level)
self.buttonRender("Next Level", Color.green, Color.bright_green, Color.black, (480, 500), (150, 50), 20, action=nextLevel)
self.buttonRender("Back", Color.red, Color.bright_red, Color.black, (180, 500), (150, 50), 20, action=self.Stage_select, actionArgs=self.difficulty)
def ShowLose(self):
# 失败的页面
self.fontRender("You Lose",Color.black, 115,(self.display_width / 2, self.display_height / 3))
self.buttonRender("Quit", Color.red, Color.bright_red, Color.black, (200, 400), (150, 50), 20, action=self.exit)
self.buttonRender("Restart", Color.green, Color.bright_green, Color.black, (500, 400), (150, 50), 20, action=self.enterGame, actionArgs=self.level)
def getStage(self):
# 获取真实关卡数
return int((self.difficulty - 1) * 10 + self.level)
def getBackTime(self):
# 获取剩余时间
if not self.isPaused:
self.backTime = int(time.time()) - self.timeStart
return str(self.backTime)
else:
self.timeStart = int(time.time()) - self.backTime
return str(self.backTime)
if __name__ == "__main__":
Game = SodokuGame()
Game.start()