用户:
meow查看: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()
#----------------------------------------