赞
踩
本文介绍基于pygame编写的2048小游戏程序
包含四个文件
运行效果:
其中config.py用于设置游戏参数
包括游戏窗口大小,刷新率,方块颜色等
class Base:
WINDOW_W = 700
WINDOW_H = 550
GAME_WH = 500
SIZE = 4
FPS = 60
DEBUG = False
COLORS = {
'0': (205, 193, 180),
'2': (238, 228, 218),
'4': (237, 224, 200),
'8': (242, 177, 121),
'16': (245, 149, 99),
'32': (246, 124, 95),
'64': (246, 94, 59),
'128': (237, 207, 114),
'256': (237, 204, 97),
'512': (237, 200, 80),
'1024': (237, 197, 63),
'2048': (255, 0, 0)
}
game.py中定义了游戏实现的函数,设置方块的产生,移动与计算,并判断游戏进行的程度,判断游戏是否完成或结束。
import random
import numpy as np
class Grid:
size = 4
tiles = []
max_tile = 0
def __init__(self, size=4):
self.size = size
self.score = 0
self.tiles = np.zeros((size, size)).astype(np.int32)
def is_zero(self, x, y):
return self.tiles[y][x] == 0
def is_full(self):
return 0 not in self.tiles
# 设置瓷砖
def set_tiles(self, xy, number):
self.tiles[xy[1]][xy[0]] = number
# 获取一个随机的空坐标
def get_random_xy(self):
if not self.is_full():
while 1:
x, y = random.randint(0, self.size - 1), random.randint(0, self.size - 1)
if self.is_zero(x, y):
return x, y
return -1, -1
# 初始设置瓷砖
def add_tile_init(self):
self.add_random_tile()
self.add_random_tile()
# 添加一个随机的瓷砖
def add_random_tile(self):
if not self.is_full():
# 产生2的概率为0.9
# q = 0.9
# for i in range(1,50):
# if random.random() < q or i==50-1:
# value = 2**i
# break
value = 2 if random.random() < 0.9 else 4
self.set_tiles(self.get_random_xy(), value)
def run(self, direction, is_fake=False):
if isinstance(direction, int):
direction = nmap[direction]
self.score = 0
if is_fake:
t = self.tiles.copy()
else:
t = self.tiles
if direction == 'U':
for i in range(self.size):
self.move_hl(t[:, i])
elif direction == 'D':
for i in range(self.size):
self.move_hl(t[::-1, i])
elif direction == 'L':
for i in range(self.size):
self.move_hl(t[i, :])
elif direction == 'R':
for i in range(self.size):
self.move_hl(t[i, ::-1])
return self.score
# 移动某一行或某一列
def move_hl(self, hl):
'''
移动某一行或某一列
对于hl,从大往小移动
:return: 移动后的列表
'''
len_hl = len(hl)
for i in range(len_hl - 1):
if hl[i] == 0:
for j in range(i + 1, len_hl):
if hl[j] != 0:
hl[i] = hl[j]
hl[j] = 0
self.score += 1
break
if hl[i] == 0:
break
for j in range(i + 1, len_hl):
if hl[j] == hl[i]:
hl[i] += hl[j]
self.score += hl[j]
hl[j] = 0
break
if hl[j] != 0:
break
return hl
# 判断是否结束
def is_over(self):
if not self.is_full():
return False
for y in range(self.size - 1):
for x in range(self.size - 1):
if self.tiles[y][x] == self.tiles[y][x + 1] or self.tiles[y][x] == self.tiles[y + 1][x]:
return False
return True
# 判断是否胜利
def is_win(self):
if self.max_tile > 0:
return self.max_tile in self.tiles
else:
return False
def __str__(self):
str_ = '====================\n'
for row in self.tiles:
str_ += '-' * (5 * self.size + 1) + '\n'
for i in row:
str_ += '|{:4d}'.format(int(i))
str_ += '|\n'
str_ += '-' * (5 * self.size + 1) + '\n'
str_ += '==================\n'
return str_
nmap = {0: 'U', 1: 'R', 2: 'D', 3: 'L'}
fmap = dict([val, key] for key, val in nmap.items())
class Game:
score = 0
env = 'testing'
state = 'start'
grid = None
def __init__(self, grid_size=4, env='production'):
self.env = env
self.grid_size = grid_size
self.start()
# 开始或重新开始
def start(self):
self.grid = Grid(self.grid_size)
if self.env == 'production':
self.grid.add_tile_init()
self.state = 'run'
# 运行一步
def run(self, direction):
if self.state in ['over', 'win']:
return None
if isinstance(direction, int):
direction = nmap[direction]
self.grid.run(direction)
self.score += self.grid.score
if self.grid.is_over():
self.state = 'over'
if self.grid.is_win():
self.state = 'win'
# 产生新方块
if self.env == 'production':
self.grid.add_random_tile()
return self.grid
def printf(self):
print(self.grid)
主函数,直接运行此程序即可开始游戏。
此程序负责编辑游戏界面,获取游戏输入,通过判断游戏运行状况与输入指令,通过调用以编写的函数,完成游戏的运行
config = SupperFast()
FPS = config.FPS
SIZE = config.SIZE
DEBUG = config.DEBUG
colors = config.COLORS
GAME_WH = config.GAME_WH
WINDOW_W = config.WINDOW_W
WINDOW_H = config.WINDOW_H
# 格子中的字体
font_h_w = 2 / 1
g_w = GAME_WH / SIZE * 0.9
# font = pygame.font.SysFont('microsoftyahei', 20)
class Main():
def __init__(self):
global FPS
pygame.init()
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 50)
self.set_win_wh(WINDOW_W, WINDOW_H, title='2048')
self.state = 'start'
self.fps = FPS
self.catch_n = 0
self.clock = pygame.time.Clock()
self.game = Game(SIZE)
self.ai = Ai()
self.step_time = config.STEP_TIME
self.next_f = ''
self.last_time = time.time()
self.start_time = time.time()
self.run_time = time.time() - self.start_time
self.jm = -1
def start(self):
# 加载按钮
self.button_list = [
Button('start', '重新开始', (GAME_WH + 50, 150)),
Button('ai', '电脑托管', (GAME_WH + 50, 250)),
]
self.run()
def run(self):
while self.state != 'exit':
if self.game.state in ['over', 'win']:
self.state = self.game.state
self.my_event()
if self.next_f != '' and (
self.state == 'run' or self.state == 'ai' and time.time() - self.last_time > self.step_time):
self.game.run(self.next_f)
self.next_f = ''
self.last_time = time.time()
elif self.state == 'start':
self.start_time = time.time()
self.game.start()
self.state = 'run'
self.set_bg((101, 194, 148))
self.draw_info()
self.draw_button(self.button_list)
self.draw_map()
self.draw_time()
self.update()
print('退出游戏')
def draw_time(self):
if self.state!='over' and self.state!='win':
self.run_time = time.time()-self.start_time
self.draw_text('游戏时间:{}分{}秒'.format(int(self.run_time/60),int(self.run_time%60)), (GAME_WH + 10, 10))
def draw_map(self):
for y in range(SIZE):
for x in range(SIZE):
self.draw_block((x, y), self.game.grid.tiles[y][x])
if self.state == 'over':
pygame.draw.rect(self.screen, (0, 0, 0, 0.5),
(0, 0, GAME_WH, GAME_WH))
self.draw_text('游戏结束!', (GAME_WH / 2, GAME_WH / 2), size=25, center='center')
elif self.state == 'win':
pygame.draw.rect(self.screen, (0, 0, 0, 0.5),
(0, 0, GAME_WH, GAME_WH))
self.draw_text('胜利!', (GAME_WH / 2, GAME_WH / 2), size=25, center='center')
# 画一个方格
def draw_block(self, xy, number):
one_size = GAME_WH / SIZE
dx = one_size * 0.05
x, y = xy[0] * one_size, xy[1] * one_size
# print(colors[str(int(number))])
color = colors[str(int(number))] if number <= 2048 else (0, 0, 255)
pygame.draw.rect(self.screen, color,
(x + dx, y + dx, one_size - 2 * dx, one_size - 2 * dx))
color = (20, 20, 20) if number <= 4 else (250, 250, 250)
if number != 0:
ln = len(str(number))
if ln == 1:
size = one_size * 1.2 / 2
elif ln <= 3:
size = one_size * 1.2 / ln
else:
size = one_size * 1.5 / ln
self.draw_text(str(int(number)), (x + one_size * 0.5, y + one_size * 0.5 - size / 2), color, size, 'center')
def draw_info(self):
self.draw_text('分数:{}'.format(self.game.score), (GAME_WH + 50, 40))
if self.state == 'ai':
self.draw_text('间隔:{}'.format(self.step_time), (GAME_WH + 50, 60))
self.draw_text('评分:{}'.format(self.jm), (GAME_WH + 50, 80))
def set_bg(self, color=(255, 255, 255)):
self.screen.fill(color)
def catch(self, filename=None):
if filename is None:
filename = "./catch/catch-{:04d}.png".format(self.catch_n)
pygame.image.save(self.screen, filename)
self.catch_n += 1
def draw_button(self, buttons):
for b in buttons:
if b.is_show:
pygame.draw.rect(self.screen, (180, 180, 200),
(b.x, b.y, b.w, b.h))
self.draw_text(b.text, (b.x + b.w / 2, b.y + 9), size=18, center='center')
def draw_text(self, text, xy, color=(0, 0, 0), size=18, center=None):
font = pygame.font.SysFont('simhei', round(size))
text_obj = font.render(text, 1, color)
text_rect = text_obj.get_rect()
if center == 'center':
text_rect.move_ip(xy[0] - text_rect.w // 2, xy[1])
else:
text_rect.move_ip(xy[0], xy[1])
# print('画文字:',text,text_rect)
self.screen.blit(text_obj, text_rect)
# 设置窗口大小
def set_win_wh(self, w, h, title='python游戏'):
self.screen2 = pygame.display.set_mode((w, h), pygame.DOUBLEBUF, 32)
self.screen = self.screen2.convert_alpha()
pygame.display.set_caption(title)
def update(self):
self.screen2.blit(self.screen, (0, 0))
# 刷新画面
# pygame.display.update()
pygame.display.flip()
time_passed = self.clock.tick(self.fps)
# 侦听事件
def my_event(self):
if self.state == 'ai' and self.next_f == '':
self.next_f, self.jm = self.ai.get_next(self.game.grid.tiles)
for event in pygame.event.get():
if event.type == QUIT:
self.state = 'exit'
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.state = 'exit'
elif event.key in [K_LEFT, K_a] and self.state == 'run':
self.next_f = 'L'
elif event.key in [K_RIGHT, K_d] and self.state == 'run':
self.next_f = 'R'
elif event.key in [K_DOWN, K_s] and self.state == 'run':
self.next_f = 'D'
elif event.key in [K_UP, K_w] and self.state == 'run':
self.next_f = 'U'
elif event.key in [K_k, K_l] and self.state == 'ai':
if event.key == K_k and self.step_time > 0:
self.step_time *= 0.9
if event.key == K_l and self.step_time < 10:
if self.step_time != 0:
self.step_time *= 1.1
else:
self.step_time = 0.01
if self.step_time < 0:
self.step_time = 0
if event.type == MOUSEBUTTONDOWN:
for i in self.button_list:
if i.is_click(event.pos):
self.state = i.name
if i.name == 'ai':
i.name = 'run'
i.text = '取消托管'
elif i.name == 'run':
i.name = 'ai'
i.text = '电脑托管'
break
def run():
Main().start()
# 按钮类
class Button(pygame.sprite.Sprite):
def __init__(self, name, text, xy, size=(100, 50)):
pygame.sprite.Sprite.__init__(self)
self.name = name
self.text = text
self.x, self.y = xy[0], xy[1]
self.w, self.h = size
self.is_show = True
def is_click(self, xy):
return (self.is_show and
self.x <= xy[0] <= self.x + self.w and
self.y <= xy[1] <= self.y + self.h)
自动游戏模块,即托管。此部分根据进行上下左右操作后形成的局面进行评分,评分依赖数字的位置,通常,大数字越靠近右下角,评分越高。该程序会选择评分更高的操作。
该部分不是很智能,很多情况下不能自主完成游戏。
def get_grid(tiles, directions):
g = Grid(config.SIZE)
g.tiles = tiles.copy()
for direction in directions:
g.run(direction)
g.add_random_tile()
return g.tiles
def printf(tiles):
for row in tiles:
for i in row:
print("{:^6}".format(i), end='')
print()
def my_log2(z):
if z == 0:
return 0
else:
return z
# return np.math.log2(z)
class Ai:
def __init__(self):
self.g = Grid(config.SIZE)
def get_next(self, tiles):
score_list = []
tn = self.get_tile_num(tiles)
if tn >= self.g.size ** 2 / 3:
return "RD"[np.random.randint(0, 2)], 0
kn = min(max(tn ** 2, 20), 40)
for directions in itertools.product("ULRD", repeat=3):
fen = []
for i in range(kn):
t_g = get_grid(tiles, directions)
fen.append(self.get_score(t_g))
print(directions, min(fen))
score_list.append([directions, min(fen)])
score_list = sorted(score_list, key=(lambda x: [x[1]]))
# print(score_list)
for d in score_list[::-1]:
self.g.tiles = tiles.copy()
if self.g.run(d[0][0], is_fake=False) != 0:
return d[0][0], d[1] / kn
self.g.tiles = tiles.copy()
# print('===',score_list[-1][0][0])
return score_list[-1][0][0], score_list[-1][1] / kn
def get_score(self, tiles):
# 格子数量(越少越好) 金角银边()
# bjs = [self.get_bj2(tiles)[i] * 2.8 + self.get_bj(tiles)[i] for i in range(4)]
# return max(bjs)
a = self.get_bj2__4(tiles)
b = self.get_bj__4(tiles)
print(a, b)
return a * 2.8 + b
def debug(self, tiles):
print('\n=======开始判断========')
print('移动前棋盘:')
printf(tiles)
score_list = []
for directions in itertools.product("ULRD", repeat=2):
t_g = get_grid(tiles, directions)
fen = self.get_score(t_g)
score_list.append([directions, fen])
print('==={}=={}=='.format(directions, fen))
printf(t_g)
score_list = sorted(score_list, key=(lambda x: [x[1]]))
# print(score_list)
for d in score_list[::-1]:
# print('-->',d)
self.g.tiles = tiles.copy()
# print(self.g.run(d[0][0],is_fake=True))
if self.g.run(d[0][0], is_fake=True) != 0:
# print('---异动前:')
# print(self.g.tiles)
# print('---异动后:')
self.g.run(d[0][0])
# print(self.g.tiles)
return d[0][0]
# print('===',score_list[-1][0][0])
return score_list[-1][0][0]
# 空格子数量
def get_tile_num(self, tiles):
# l = len(tiles)
n = 0
for row in tiles:
for i in row:
if i == 0:
n += 1
return n
# return np.bincount(tiles)[0]
def get_bj(self, tiles):
gjs = [
self.get_bj__1(tiles),
self.get_bj__2(tiles),
self.get_bj__3(tiles),
self.get_bj__4(tiles)
]
return gjs
def get_bj__4(self, tiles):
bj = 0
l = len(tiles)
size = self.g.size - 1
for y in range(l):
for x in range(l):
z = tiles[y][x]
if z != 0:
z_log = z - 2
bj += z_log * (x + y - (size * 2 - 1))
else:
bj += (100 - 20 * (x + y - (size * 2 - 1)))
# print(z, "-- ", bj)
return bj
def get_bj__3(self, tiles):
bj = 0
l = len(tiles)
size = self.g.size - 1
for y in range(l):
for x in range(l):
z = tiles[y][x]
if z != 0:
z_log = z - 2
bj += z_log * ((size - x) + y - (size * 2 - 1))
else:
bj += (100 - 20 * ((size - x) + y - (size * 2 - 1)))
return bj
def get_bj__2(self, tiles):
bj = 0
l = len(tiles)
size = self.g.size - 1
for y in range(l):
for x in range(l):
z = tiles[y][x]
if z != 0:
z_log = z - 2
bj += z_log * ((size - x) + (size - y) - (size * 2 - 1))
else:
bj += (100 - 20 * ((size - x) + (size - y) - (size * 2 - 1)))
return bj
def get_bj__1(self, tiles):
bj = 0
l = len(tiles)
size = self.g.size - 1
for y in range(l):
for x in range(l):
z = tiles[y][x]
if z != 0:
z_log = z - 2
bj += z_log * (x + (size - y) - (size * 2 - 1))
else:
bj += (100 - 20 * (x + (size - y) - (size * 2 - 1)))
return bj
def get_bj2(self, tiles):
gjs = [
self.get_bj2__1(tiles),
self.get_bj2__2(tiles),
self.get_bj2__3(tiles),
self.get_bj2__4(tiles)
]
return gjs
def get_bj2__1(self, tiles):
bj = 0
l = len(tiles)
for y in range(0, l - 1, 1):
for x in range(l - 1, 0, -1):
z = tiles[y][x]
if tiles[y][x] < tiles[y][x - 1]:
bj -= abs(my_log2(tiles[y][x - 1]) - z)
if tiles[y][x] < tiles[y + 1][x]:
bj -= abs(my_log2(tiles[y + 1][x]) - z)
if tiles[y][x] < tiles[y + 1][x - 1]:
bj -= abs(my_log2(tiles[y + 1][x - 1]) - z)
return bj
def get_bj2__2(self, tiles):
bj = 0
l = len(tiles)
for y in range(0, l - 1):
for x in range(0, l - 1):
z = tiles[y][x]
if tiles[y][x] < tiles[y][x + 1]:
bj -= abs(my_log2(tiles[y][x + 1]) - z)
if tiles[y][x] < tiles[y + 1][x]:
bj -= abs(my_log2(tiles[y + 1][x]) - z)
if tiles[y][x] < tiles[y + 1][x + 1]:
bj -= abs(my_log2(tiles[y + 1][x + 1]) - z)
return bj
def get_bj2__3(self, tiles):
bj = 0
l = len(tiles)
for y in range(l - 1, 0, -1):
for x in range(0, l - 1):
z = tiles[y][x]
if tiles[y][x] < tiles[y][x + 1]:
bj -= abs(my_log2(tiles[y][x + 1]) - z)
if tiles[y][x] < tiles[y - 1][x]:
bj -= abs(my_log2(tiles[y - 1][x]) - z)
if tiles[y][x] < tiles[y - 1][x + 1]:
bj -= abs(my_log2(tiles[y - 1][x + 1]) - z)
return bj
def get_bj2__4(self, tiles):
bj = 0
l = len(tiles)
for y in range(l - 1, 0, -1):
for x in range(l - 1, 0, -1):
z = tiles[y][x]
if z < tiles[y][x - 1]:
bj -= abs(my_log2(tiles[y][x - 1]) - z)
if z < tiles[y - 1][x]:
bj -= abs(my_log2(tiles[y - 1][x]) - z)
if z < tiles[y - 1][x - 1]:
bj -= abs(my_log2(tiles[y - 1][x - 1]) - z)
return bj
完整程序点此下载,运行main.py文件即可运行游戏
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。