【python大作业】pygame实战(python编写2048小游戏)

本文介绍基于pygame编写的2048小游戏程序
包含四个文件

在这里插入图片描述运行效果:
在这里插入图片描述 在这里插入图片描述

点击此处下载完整程序,下载即可运行

config.py

其中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

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)

main

主函数,直接运行此程序即可开始游戏。
此程序负责编辑游戏界面,获取游戏输入,通过判断游戏运行状况与输入指令,通过调用以编写的函数,完成游戏的运行

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)

ai

自动游戏模块,即托管。此部分根据进行上下左右操作后形成的局面进行评分,评分依赖数字的位置,通常,大数字越靠近右下角,评分越高。该程序会选择评分更高的操作。
该部分不是很智能,很多情况下不能自主完成游戏。

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文件即可运行游戏

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

feiGeorge

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值