猫史档案馆


2048(pygame)

用户:meowmeow查看:0 回复:0 评论:0 创建时间:2023-06-17T12:12:19


#引入各种模块----------------------------
import pygame,time,sys,random
from pygame.locals import *
import threading    #多线程模块

#定义常量,存储界面尺寸-------------------
SIZE = 4    #2048是4*4的小格,所以尺寸为4
LENGTH = 130#每个小格,边长长度为130
SCORE_HEIGHT = 130#计分区所占高度为130

#第1次添加程序开始------------------------------
#用元组定义背景颜色和每个数字对应的方块颜色-------
DEFAULT=(205, 193, 180) #背景颜色
C_FONT=(120, 111, 102)  #字体颜色
C_2=(238, 228, 218)     #数字2方块色
C_4=(237, 224, 200)     #以下都是对应的方块色
C_8=(242, 177, 121)     #全部采用RGB颜色混合
C_16=(245, 149, 99)
C_32=(246, 124, 95)
C_喵=(246, 94, 59)
C_128=(237, 207, 114)
C_256=(237, 204, 98)
C_512=(237, 200, 80)
C_1024=(237, 197, 63)
C_2048=(225, 187, 0)
#第1次添加程序结束--------------------------------

#第2次添加程序开始--------------------------------
#定义一个函数,用来选取对应数字的颜色---------------
def get_color(n):#参数是数字n,依靠n来选取对应颜色
    n_t=0#设定一个变量,用来确定颜色列表的第几个位置
    for i in range(1,12):#因为2到2048一共11个数字
                         #所以range(1,12)
        if n>>i==1:#因为2到2048这几个数字全是2的次方
                   #所以利用二进制右移,移1次变成1的话
                   #说明是第一个数字,也就是2
            n_t=i  #以此类推,移几次表示是第几个数字
    #接下来定义一个列表,按顺序把背景色、数字色、字体色放进去(第一个是背景色,最后一个是字体)
    color=[DEFAULT,C_2,C_4,C_8,C_16,C_32,C_喵,C_128,C_256,C_512,C_1024,C_2048,C_FONT]
    return color[n_t]   #根据前面的计算结果,从列表里取出对应的方块色
#第2次添加程序结束----------------------------------------------------------------------

#第3次添加程序开始-----------------------------------------------------------------
#定义一个类,用于画面内容控制--------------------------------------------------------
class Map:#这个类取名叫Map(注意首字母大写),表示整个画面内的内容
    def __init__(self,size):#初始化函数,参数是方格的尺寸
        self.size = size    #把传入的参数,给类的size变量
        #接下来创建一个二维数组,这里使用了列表生成式,第一个[0 for i in range(size)]
        #表示创建一个有四个元素(size我们之前定义了4)的列表,里面的每一个元素都是0
        #第二个以此参考,就是再创建一个4元素列表,每个元素都是刚才创建的第一个列表
        #这样的话,就形成了4*4二维数组,每个数值都是0(如果看不懂,可以用循环嵌套创建)
        self.map=[[0 for i in range(size)] for i in range(size)]
        self.score = 0      #创建用于计分的变量
        self.is_move = 0    #创建用于判断是否移动的变量
        self.add()      #随机添加一个数字2或4,这个add函数随后就定义
        self.add()      #再添加一个,因为起始的时候应该有两个数字
    def add(self):#定义一个添加函数,专门用来随机添加数字2和4
        #注意,在前面引入random模块,因为马上要用随机功能
        while True:#在没有成功随机出数字之前,应该一直循环,所以True
            pos = random.randint(0,self.size*self.size-1)  #0-15(含15)中随机出一个数
                                                           #因为4*4=16
            flag = self.map[pos//self.size][pos%self.size]  #取得这个数字对应位置的数值
            #这里是4*4个格子,第一个//地板除,取得整除的整数部分,表示第几排,第二个取余%
            #表示取得整除之后的余数,也就是那一排的第几个(这个比较绕,好好画一下想想)
            if flag == 0:#如果这个位置的数是0,那说明这个位置可以给新数,不然就要再循环
                num = random.randint(0,3)#0-3中随机出一个数,用来决定要添加2还是4
                n=2#默认新数是2
                if num == 0:#如果出现的随机数是0(概率4分之1),那就添加4
                    n=4
                self.map[pos//self.size][pos%self.size]=n#把新数给这个位置
                self.score+=n#每次生成都要加对应的分数
                break#完成之后就可以中断循环了
#第3次添加程序结束----------------------------------------------------------------------

#第6次添加程序开始,注意缩进,这部分仍然属于上面的Map类------------------------------------
#添加一个判定失败的函数,注意缩进--------------------------------------------------------
    def failed(self):
        for i in self.map:#利用循环嵌套来访问每一个元素
            for j in i:#只要有0,说明还有空位,就不算失败
                if j==0:
                    return False#false表示没有失败,true表示失败了
        for i in range(0,self.size):#还有一种情况,就是虽然填满了
            for j in range(0,self.size):#但是还有相邻位置可合成
                #这种需要多个条件结合判断,上下左右相邻的两个能不能合成
                #这里面的i-1,j-1等只是为了防止访问超出,不要超出最外层
                if (i-1>=0 and self.map[i][j]==self.map[i-1][j])\
                    or (j-1>=0 and self.map[i][j]==self.map[i][j-1])\
                    or (i+1<self.size and self.map[i][j]==self.map[i+1][j])\
                    or (j+1<self.size and self.map[i][j]==self.map[i][j+1]):
                    return False
        return True#如果上面两种情况都没出现,那就返回true,表示游戏失败
#第6次添加程序结束--------------------------------------------------------------------

#第7次添加程序开始,注意缩进,这部分仍然属于上面的Map类------------------------------------
#添加一个检测某个数字存不存在的函数,注意缩进---------------------------------------------
    def check(self,num):#参数是你需要检测的数字,num
        for i in self.map:#还是通过嵌套访问每一个数字
            for j in i:
                if j==num:#如果有相等的,说明这个数字存在
                    return True#那么返回true
        return False#不存在就返回false
#第7次添加程序结束---------------------------------------------------------------------

#第8次添加程序开始,注意缩进,这部分仍然属于上面的Map类------------------------------------
#添加一个让整个数字方阵往左移动的函数,注意缩进--------------------------------------------
    def move_to_left(self):
        changed=False#每次移动完之后,更新是否移动,默认是不移动
        for a in self.map:#利用循环,取出每一行,存入a
            b = []#新建一个空列表,用于临时存合成后的
            last=0#新建一个变量,用来存上一个元素,这样才好比较
            for v in a:#从每一行里面读取每一个元素,开始判断
                if v!=0:#如果这个元素不是0,就添加到b中
                    if v!=last:#如果读取的数和上一个元素不等
                        b.append(v)#那么将v添入b列表,这个是不可合成的
                        last=v#然后更新last,用于下一次合成判断
                    else:#否则的话,那就说明v和last相等,可以合成
                        b.append(b.pop()*2)#这时直接让b的最后弹出一个并*2
                        last=0#更新last,已经合成了,所以这个位置应该变成0
            b+=[0]*(self.size-len(b))#合成完之后,在末尾添加0,凑足4个元素
            for i in range(0,self.size):#每次合成完毕之后,检查和原来的是否相同
                if a[i]!=b[i]:  #如果和原来的不同,说明移动成功
                    changed = True#只要有一行移动成功,说明左移成功,返回true
            a[:]=b#每一行移动完毕后,把移动后的b列表传给原来的a列表,进行替换
        return changed#所有行都移动完毕之后,返回移动是否成功的结果
#第8次添加程序结束---------------------------------------------------------------------

#第9次添加程序开始,注意缩进,这部分仍然属于上面的Map类------------------------------------
#添加一个让整个数字方阵顺时针90度的函数,注意缩进------------------------------------------
    def change(self):
        #顺时针旋转90度,就是用reversed倒序访问每一排,然后每j个喵来,看图理解好一点
        self.map=[[self.map[i][j] for i in reversed(range(self.size))] for j in range(self.size)]
#第9次添加程序结束---------------------------------------------------------------------

#第10次添加程序开始,注意缩进,这部分仍然属于上面的Map类------------------------------------
#添加四个上下左右移动方块的函数,注意缩进--------------------------------------------------
    def move_left(self):    #左移不需要旋转,只要能移,就直接移
        if self.move_to_left():#判断能不能移
            self.is_move=1#能移就标记为移动了
            self.add()  #移动完毕之后需要添加一个数
    def move_up(self):#上移,就比左移多了旋转操作,先顺时针3次
        self.change()
        self.change()
        self.change()
        if self.move_to_left():#然后和左移一样,合并
            self.is_move=1
            self.add()
        self.change()#完事再移一次就恢复了,其他方向都类似了
    def move_right(self):#右移,先旋转两次,移动后再旋转两次
        self.change()
        self.change()
        if self.move_to_left():#然后和左移一样,合并
            self.is_move=1
            self.add()
        self.change()
        self.change()
    def move_down(self):
        self.change()
        if self.move_to_left():#然后和左移一样,合并
            self.is_move=1
            self.add()
        self.change()
        self.change()
        self.change()
#第10次添加程序结束-------------------------------------------------------


#第4次添加程序开始-------------------------------------------------------
#定义一个显示函数-------------------------------------------------------
def display(map,screen):#参数是我们定义的画面类实例和屏幕信息
    block_font=pygame.font.Font(None,86)#设定方块上的数字字体和大小
    score_font=pygame.font.Font(None,86)#分数字体和大小
    screen.fill(DEFAULT)#使用默认颜色填充屏幕
    for i in range(map.size):#使用循环嵌套来访问每一个位置的数值
        for j in range(map.size):
            block = pygame.Surface((LENGTH,LENGTH))#定义方块表面
            block.fill(get_color(map.map[i][j]))#调用函数获取对应颜色
            #接下来定义字喵置、表面、外接矩形等信息
            font_surf=block_font.render(str(map.map[i][j]),True,C_FONT)
            #这里因为要显示文字,是字符串,所以要str转换一下
            font_rect=font_surf.get_rect()
            font_rect.center=(j*LENGTH+LENGTH/2,LENGTH*i+LENGTH/2)
            #方块的中心坐标注意除以2
            screen.blit(block,(j*LENGTH,i*LENGTH))#显示对应方块
            if map.map[i][j]!=0:#如果对应位置不是0,那么应该显示数字
                screen.blit(font_surf,font_rect)#把对应数字放这里
    #上面循环嵌套已经显示完4*4所有方格,下面来显示分数
    score_surf=score_font.render('score:'+str(map.score),True,C_FONT)
    score_rect = score_surf.get_rect()
    score_rect.center=(LENGTH*SIZE/2,LENGTH*SIZE+SCORE_HEIGHT/2)
    screen.blit(score_surf,score_rect)
    pygame.display.update()#设定完毕,更新画面
#第4次添加程序结束--------------------------------------------------------------

#定义主逻辑------------------------------
def main():
    pygame.init()#初始化界面
    screen = pygame.display.set_mode((LENGTH*SIZE,LENGTH*SIZE+SCORE_HEIGHT))#设置屏幕尺寸
    pygame.display.set_caption("2048")  #设置窗体标题
    clock = pygame.time.Clock()#用于控制帧率
    #第5次添加程序开始----------------------------------------------
    #创建map实例,并调用显示
    map = Map(SIZE)#初始化实例,用之前定义的尺寸常量
    display(map,screen)#调用显示函数,参数就是上面创建的
    #第5次添加程序结束----------------------------------------------

    #第11次添加程序开始---------------------------------------------
    #添加键盘检测相关内容-------------------------------------------
    while not map.failed():#如果没有失败,就一直检测键盘
        for event in pygame.event.get():#获取事件情况
            if event.type==QUIT:#处理退出情况
                pygame.quit()
                sys.exit()
            elif event.type==KEYDOWN:#键盘按下的话,准备判断
                keys=pygame.key.get_pressed()#获取键值
                map.is_move=0#记录是否移动
                if keys[K_UP]:#如果是上键
                    map.move_up()#就调用上移函数
                elif keys[K_DOWN]:#之后的都类似
                    map.move_down()
                elif keys[K_RIGHT]:
                    map.move_right()
                elif keys[K_LEFT]:
                    map.move_left()
            elif event.type==KEYUP:#按键松开之后,就更新画面,使用子线程来更新
                t=threading.Thread(target=display,args=(map,screen))
                #创建一个线程,目标函数是显示,参数是map和screen信息,可回看上次课
                t.setDaemon(True)#设置线程
                t.start()#开始执行
        if map.is_move==1:#每次键盘检测结束后,都判断是否移动了
            if map.check(2048):#如果移动了,检测有没有2048出现
                break#出现了说明游戏结束,赢了,循环终止
            time.sleep(0.01)#每次判定完毕,留一点时间让画面刷新
    #第11次添加程序结束-----------------------------------------------------------

    #第12次添加程序开始-----------------------------------------------------------
    #胜负情况显示处理-------------------------------------------------------------
    result = 'You Failed!'#默认情况下,上面循环结束,游戏失败了
    if map.check(2048):#如果上面循环结束的情况下,检测到2048
        result = 'You Win!'#那这时说明游戏你赢了
    screen.fill(DEFAULT)#游戏结算界面直接默认底色
    map_font = pygame.font.Font(None,86)#设置一下字体什么的
    font_surf = map_font.render(result,True,C_FONT)
    font_rect = font_surf.get_rect()
    font_rect.center = (SIZE*LENGTH/2,SIZE*LENGTH/2)
    screen.blit(font_surf,font_rect)
    pygame.display.update()
    #第12次添加结束----------------------------------------------------------------
    while True:
        #检测是否退出
        clock.tick(5)#控制帧率为5
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()


#调用主要逻辑-----------------------------
if __name__ == "__main__":
    main()
#----------------------------------------


回复

上一页1 页 / 共 0下一页