pygame寫個數獨

可以選關,記錄最高分,可以提示錯誤的格子或別的亂七八糟的

"""
@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()

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章