Python//自制2048小遊戲 代碼分析與收穫

Python//2048小遊戲

這是一篇關於2048小遊戲自制記錄,用於個人總結與回看。如果能對你有幫助,我很榮幸。  

成品效果圖

伴隨音樂,音量可調。
同時伴隨音樂(音量可調)

0 仍存在的未解決問題

如果有大佬看見了這篇文章,能幫助我解決以下的一些問題,我十分感激。
1、在我電腦上,pygame(1.9.6)只能加載bmp圖片格式,但我從查找的資料來看,pygame應該可以讀取png jpg等圖片格式的。
2、在我用pyinstaller將代碼打包爲exe文件時,添加-w(使用Windows子系統執行.當程序啓動時侯不會打開命令行) 參數會出現錯誤,出現了我不太懂的一個名詞bootloader,報錯如下:在這裏插入圖片描述
3、在我用pyinstaller將代碼打包爲exe文件時,添加-i(修改exe默認圖片)參數,圖片十分模糊,且在打包好的文件夾中是正常的,移動到桌面(或者其他目錄下)exe圖標還是默認圖標,十分疑惑。
4、我希望可以監測鼠標的點擊位置(我希望是一個圖片),但是我在pygame中發現只能(實時監測鼠標位置+鼠標點擊事件)同時使用,以到達目的,但效果不好(因爲要涉及到整個圖片),我不太會操作。

一、寫在前面

身爲非計算機專業的大一小白,自知代碼風格不佳,函數封裝冗餘,但會努力學習,如果你看我的代碼覺得混亂,我十分抱歉。

1、出於Python課程大作業原因,藉此機會,完成了我一直想寫一個遊戲的想法。
2、代碼運用到了 numpy,random,pygame,sys庫

import numpy as np
import random
import pygame
import sys

二、代碼思路與分析

上一張粗略的思維導圖
在這裏插入圖片描述
1、初始化部分,就是簡單的生成兩個4*4矩陣,record1記錄上一步操作(希望添加返回上一步的功能,但設計不夠完善,有一點小bug,後面會提到),record2記錄當前操作,以0表示沒有數字。用random.randint(0,9)來生成隨機數,用以表示刷新2還是4(比例我設置爲9:1)。然後將該數填入矩陣內隨機位置即可,這裏依然採用randint方式。
2、當發生移動時(上下左右),矩陣應該如何變化?
以向上移動爲例說明。首先,排列數字(即不考慮合併),按順序遍歷矩陣,當出現數字時,放在該列最上面,此處順序指的是:向上移動就是由小到大遍歷,向下就是由大到小遍歷,核心思想就是,從移動方向開始,尋找數字,並記錄次數。排列完成後開始,合併相同的數字(如:4+4 = 8)這裏只合並一次(做了適當簡化,正常可玩一下2048,感受一下),合併過程爲,遍歷格子,向四周尋找相同的數字,當找到後,向移動方向合併,並把後續數字想該方向推進,然後跳出循環(這裏對後續數字的判斷有些冗餘,我認爲可以更簡單,這裏先不修改了),其餘方向思想相同。
3、刷新矩陣,即產生一個新的數字(2或4)填入矩陣爲0部分,因爲此處希望可以滿足按其他鍵(非方向鍵)不刷新的要求,先判斷矩陣是否相同,

if not np.array_equal(record2, record1):

如果相同,則不進行刷新。此處刷新大體與初始化的刷新相同,其中一步變化就是需要判斷矩陣元素是否爲0,這裏沒有采用遍歷方式,具體操作如下:

N = random.choice(np.where(record2.reshape(1, 16) == 0)[1])
Next_cell1_location_raw = N // 4
Next_cell1_location_clo = N
while Next_cell1_location_clo > 3:
Next_cell1_location_clo = Next_cell1_location_clo - 4
record2[Next_cell1_location_raw][Next_cell1_location_clo] = Next_cell1      

5、判斷遊戲結束,當矩陣沒有元素爲0時,遍歷每個格子,向四周尋找相同數字,這裏與合併判斷一樣,不懂贅述。(我寫的確實是複雜了/菜)
6、最後一步就是運用pygame的界面設計,具體設計不講,只說pygame中一些函數的運用。

pygame.init()#初始化各模塊部件
size = width, height = 500, 600
screen = pygame.display.set_mode(size,flags = pygame.RESIZEABLE )#設置界面大小,可調模式(屏幕大小可調RESIZEABLE,屏幕沒有邊框NOFRAME,屏幕全屏顯示FULLSCREEN)
icon1 = pygame.image.load('2048.bmp')#加載圖片
pygame.display.set_caption('2048小遊戲')#更改標題
screen.fill((238 ,220 ,130))#屏幕背景色
map_font = pygame.font.Font('msyh.ttc', 40)#生成一個字體對象,這裏是微軟雅黑
font_surf1 = map_font_start.render('2048 小遊戲', True, (105 ,112 ,225))#寫文字
font_rect1 = font_surf1.get_rect()#生成rect(相當於一個矩陣框住文字形成圖片,方便移動)
font_rect1.center = (100,20)#文本位置設置,這裏採用center居中操作
screen.blit(font_surf1, font_rect1)#顯示文字,對所有文字,圖片,模塊的設計都要執行這一步
screen.blit(block_scord, (350, 80))#這是對一個模塊的設計
for i in range(4):
     for j in range(4):
         if record2[i][j] == 0:
             font_surf = map_font.render('', True, (0, 0, 0))
         else:
             font_surf = map_font.render(str(record2[i][j]), True, (0, 0, 0))
         font_rect = font_surf.get_rect()
         font_rect.center = (80 + 110 * j, 190 + 110 * i)
         block = pygame.Surface((100, 100))
         block.fill(eval(color[i][j]))
         screen.blit(block, (30 + 110 * j, 140 + 110 * i))
         screen.blit(font_surf, font_rect)
'''
這裏是循環生成16個模塊分別填充上面矩陣record2的數字
'''
while True:
	 for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()#這裏是事件判斷,包括按鍵事件,鼠標事件等所有事件均採取這種方式(從事件隊列中讀取)讀入。
pygame.mixer.music.load(flag)#讀入音樂
pygame.mixer.music.play()#播放音樂              pygame.mixer.music.set_volume(sound)#調節音樂音量

下面是判斷矩陣不刷新條件,error是爲了區分方向鍵,否則就會出現無法返回上一步(當按下這些鍵時)

if not np.array_equal(record2,record1) and error == 0:
    Flash()

6、我還加了一步最好成績記錄,用txt保存,這一步應該沒有難度,但open中的參數(r w w+ r+ a a+)很有研究的意義,可以查找資料看看。

三、代碼總覽

import numpy as np
import random
import pygame
import sys

#初始化遊戲,建立兩個矩陣,record1記錄上一步,record2實時變化。
#random隨機刷新新的格子
def New_Game():
    global record1, record2
    Gameover = False
    record1 = np.array(
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).reshape(4, 4)
    record2 = np.array(
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).reshape(4, 4)

    if random.randint(0, 10) in range(9):
        rand = 1
    else:
        rand = 2
    First_cell = 2 ** rand
    First_cell_location_raw, First_cell_location_clo = random.randint(0, 3), random.randint(0, 3)
    record2[First_cell_location_clo, First_cell_location_raw] = First_cell

#每次刷新一個格子
def Flash():
    if not np.array_equal(record2, record1):
        try:
            if random.randint(0, 10) in range(9):
                rand = 1
            else:
                rand = 2
            Next_cell1 = 2 ** rand
            N = random.choice(np.where(record2.reshape(1, 16) == 0)[1])
            Next_cell1_location_raw = N // 4
            Next_cell1_location_clo = N
            while Next_cell1_location_clo > 3:
                Next_cell1_location_clo = Next_cell1_location_clo - 4
            record2[Next_cell1_location_raw][Next_cell1_location_clo] = Next_cell1
        except:
            pass

#如果16個格子被佔滿,此時如果一個格子向四周拓展均沒有相同的則判斷此時遊戲結束
def Gameover():
    global gameover
    gameover = 0
    _ = 0
    if len(record2[record2 == 0]) == 0:
        for raw in range(4):
            for clo in range(4):
                if raw == 0:
                    if record2[raw][clo] == record2[raw + 1][clo]:
                        _ = 1
                        break
                elif raw == 3:
                    if record2[raw][clo] == record2[raw - 1][clo]:
                        _ = 1
                        break
                else:
                    if record2[raw][clo] == record2[raw + 1][clo] or record2[raw][clo] == record2[raw - 1][clo]:
                        _ = 1
                        break
                if clo == 0:
                    if record2[raw][clo] == record2[raw][clo + 1]:
                        _ = 1
                        break
                elif clo == 3:
                    if record2[raw][clo] == record2[raw][clo - 1]:
                        _ = 1
                        break
                else:
                    if record2[raw][clo] == record2[raw][clo + 1] or record2[raw][clo] == record2[raw][clo - 1]:
                        _ = 1
                        break
            if _ == 1:
                break
        else:
            gameover = 1

#以下四個函數爲向指定方向移動的變化,包括相同格子的合併,不同格子的移動
def point_up():
    for clo in range(4):
        count = 0
        for raw in range(4):
            if record2[raw][clo] != 0:
                record2[count][clo] = record2[raw][clo]
                if count != raw:
                    record2[raw][clo] = 0
                count += 1
        for raw in range(3):
            if record2[raw][clo] == record2[raw + 1][clo] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if raw == 2:
                    lenth = 0
                elif raw == 1:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw + i + 1][clo] = record2[raw + i + 2][clo]
                record2[3][clo] = 0
                break


def point_left():
    for raw in range(4):
        count = 0
        for clo in range(4):
            if record2[raw][clo] != 0:
                record2[raw][count] = record2[raw][clo]
                if count != clo:
                    record2[raw][clo] = 0
                count += 1
        for clo in range(3):
            if record2[raw][clo] == record2[raw][clo + 1] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if clo == 2:
                    lenth = 0
                elif clo == 1:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw][clo + i + 1] = record2[raw][clo + i + 2]
                record2[raw][3] = 0
                break


def point_down():
    for clo in range(4):
        count = 0
        for raw in list(range(4))[::-1]:
            if record2[raw][clo] != 0:
                record2[3 - count][clo] = record2[raw][clo]
                if 3 - count != raw:
                    record2[raw][clo] = 0
                count += 1
        for raw in list(range(1, 4))[::-1]:
            if record2[raw][clo] == record2[raw - 1][clo] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if raw == 1:
                    lenth = 0
                elif raw == 2:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw - i - 1][clo] = record2[raw - i - 2][clo]
                record2[0][clo] = 0
                break


def point_right():
    for raw in range(4):
        count = 0
        for clo in list(range(4))[::-1]:
            if record2[raw][clo] != 0:
                record2[raw][3 - count] = record2[raw][clo]
                if 3 - count != clo:
                    record2[raw][clo] = 0
                count += 1
        for clo in list(range(1, 4))[::-1]:
            if record2[raw][clo] == record2[raw][clo - 1] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if clo == 1:
                    lenth = 0
                elif clo == 2:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw][clo - i - 1] = record2[raw][clo - i - 2]
                record2[raw][0] = 0
                break

#利用pygame設計遊戲畫面
def gameframe():
    global map_font ,screen
    pygame.init()
    icon1 = pygame.image.load('2048.bmp')
    size = width, height = 500, 600
    screen = pygame.display.set_mode(size)
    pygame.display.set_caption('2048小遊戲')
    screen.fill((238 ,220 ,130))
    map_font = pygame.font.Font('msyh.ttc', 40)

    map_font_start = pygame.font.Font('msyh.ttc', 20)

    font_surf1 = map_font_start.render('2048 小遊戲', True, (105 ,112 ,225))
    font_rect1 = font_surf1.get_rect()
    font_rect1.center = (100,20)

    font_surf2 = map_font_start.render('***祝您遊戲愉快***', True, (150, 0, 221))
    font_rect2 = font_surf2.get_rect()
    font_rect2.center = (100,60)

    font_surf3 = map_font_start.render('白雲蒼狗 製作', True, (152, 52, 12))
    font_rect3 = font_surf3.get_rect()
    font_rect3.center = (100,100)

    font_surf4 = map_font_start.render('歷史最高:{}'.format(eval(maxscord)), True, (0, 0, 0))
    font_rect4 = font_surf3.get_rect()
    font_rect4.center = (395,50)

    screen.blit(font_surf1, font_rect1)
    screen.blit(font_surf2, font_rect2)
    screen.blit(font_surf3, font_rect3)
    screen.blit(font_surf4, font_rect4)

    # fps = 30
    # flock = pygame.time.Clock()

#動態刷新16個格子的值
def show_config():
    color = np.array(['(255 ,246 ,143)','(255 ,226 ,133)','(255 ,216 ,163)','(255 ,216 ,143)','(255 ,236 ,123)','(255 ,246 ,123)','(255 ,255 ,123)','(255 ,255 ,143)','(245 ,246 ,133)','(255 ,216 ,173)','(255 ,226 ,173)','(255 ,226 ,143)','(255 ,246 ,173)','(255 ,246 ,103)',"(255 ,206 ,143)",'(245 ,226 ,163)']).reshape(4,4)

    for i in range(4):
        for j in range(4):
            if record2[i][j] == 0:
                font_surf = map_font.render('', True, (0, 0, 0))
            else:
                font_surf = map_font.render(str(record2[i][j]), True, (0, 0, 0))
            font_rect = font_surf.get_rect()
            font_rect.center = (80 + 110 * j, 190 + 110 * i)
            block = pygame.Surface((100, 100))
            block.fill(eval(color[i][j]))
            screen.blit(block, (30 + 110 * j, 140 + 110 * i))
            screen.blit(font_surf, font_rect)

    map_font_scord = pygame.font.Font('msyh.ttc', 20)
    font_surf4 = map_font_scord.render('總分:{:<}分'.format(np.sum(record2)), True, (0, 0, 0))
    font_rect4 = font_surf4.get_rect()
    font_rect4.center = (400,95)
    block_scord = pygame.Surface((150, 35))
    block_scord.fill((238 ,220 ,130))
    screen.blit(block_scord, (350, 80))
    screen.blit(font_surf4, font_rect4)

#pygame 主循環,鍵盤事件的監測
def main():
    global record1 ,record2,maxscord
    with open('最好成績.txt', 'r') as f:
        maxscord = f.read()
    gameframe()
    New_Game()
    path1 = '古箏.wav'
    path2 = 'River Fflows In You.wav'
    flag = path2
    sound = 0.5
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif pygame.mixer.music.get_busy()==False:
                if flag == path1:
                    flag = path2
                else:
                    flag = path1
                pygame.mixer.music.load(flag)
                pygame.mixer.music.play()
            elif event.type == pygame.KEYUP:
                error = 0
                if event.key == pygame.K_LEFT:
                    record1 = record2 * 1
                    point_left()
                elif event.key == pygame.K_RIGHT:
                    record1 = record2 * 1
                    point_right()
                elif event.key == pygame.K_UP:
                    record1 = record2 * 1
                    point_up()
                elif event.key == pygame.K_DOWN:
                    record1 = record2 * 1
                    point_down()
                elif event.key == pygame.K_BACKSPACE:
                    record2 = record1
                elif event.key == pygame.K_u:
                    sound = sound + 0.05
                    pygame.mixer.music.set_volume(sound)
                    error = 1
                elif event.key == pygame.K_d:
                    sound = sound - 0.05
                    pygame.mixer.music.set_volume(sound)
                    error = 1
                else:
                    error = 1
                if not np.array_equal(record2,record1) and error == 0:
                    Flash()

        show_config()
        #flock.tick(fps)
        Gameover()
        if gameover == 1:
            block_over = pygame.Surface((400,150))
            block_over.fill((238 ,220 ,130))
            map_font_over = pygame.font.Font('msyh.ttc', 60)
            font_surf = map_font_over.render('GAME OVER', True, (0, 0, 0))
            font_rect = font_surf.get_rect()
            font_rect.center = (250, 350)
            screen.blit(block_over, (50,275))
            screen.blit(font_surf, font_rect)

            with open('最好成績.txt', 'w') as f:
                f.write(str(np.sum(record2)))
            if np.sum(record2) > eval(maxscord):
                with open('最好成績.txt', 'w') as f:
                        f.write(str(np.sum(record2)))
            pygame.display.update()
        pygame.display.update()


if __name__ == "__main__":
    main()

四、寫代碼過程中遇到的坑彙總

1、numpy提供的array不能直接判斷是否相等,需要用到函數array_equal(array1,array2)
2、pygame的界面刷新實行的是疊加,而不是覆蓋,且其中存在先後順序。
3、pygame音樂模塊,如果希望打包exe,只能使用wav文件,否則報錯。
4、這是一個推測,如果有大佬懂,歡迎指正。在函數中global一個array,然後給這個array賦值爲另一個array時,如果這樣寫:array1 = array2 那麼接下來array1 與array2將會保持一致,也就是說array2變化後,執行另一個函數時,array1仍是變化後的array2,而不是我想要的之前保存的array1,我是這麼做的,array1 = array2 * 1(感覺有點傻,具體原理我不太懂,隱約感覺是global的原因)

五、收穫總結

通過這次2048的製作,我不僅瞭解到pygame的使用(第一次接觸),同樣對較長代碼(對我來說)有了一定的編寫經驗,花了兩個晚上做的感覺比較滿意,對現階段我的水平,確實發揮出來了,過程也比較順利。再加上第一次寫csdn,總結下來同樣有一些收穫。

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