2048小游戏成品源码

news2024/9/28 7:22:26

2048小游戏,可以自选背景颜色,方框颜色,音乐播放。
还可以展示当前玩家的排名,动态排名,及历史玩家的排名。

前期需求:
在这里插入图片描述
在这里插入图片描述
使用pygame加载目录音乐。MP3文件:

def music_play():
    import pygame as py
    # 初始化
    py.mixer.init()
    # 文件加载
    py.mixer.music.load(r'.\test.mp3')
    # 播放  第一个是播放值 -1代表循环播放, 第二个参数代表开始播放的时间
    py.mixer.music.play(-1, 10)

看下运行后的效果图:

=========参数设置:
在这里插入图片描述
=========背景设置:
在这里插入图片描述
=========方块设置:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
==========源码分享:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author  : Codeooo
# @Time    : 2022/04/13


"""2048游戏

本模块已完整实现2048游戏的算法及分数的计算算法
本游戏的界面采用python 标准库 tkinter 来实现
此界面的布局采用tkinter中的grid布局
"""

import copy
import random  # 导入随机模块random,主要用于随机生成新的数字及数字摆方位置
import math  # 导入数学模块,用来计算分数

# _map_data 绑定一个 4 x 4 列表,此列表为2048游戏地图,初始值如下:
from tkinter.colorchooser import askcolor

_map_data = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]

score_list = []  # 分数列表


# -------------------------以下为2048游戏的基本算法---------------------------


def reset():
    '''重新设置游戏数据,将地图恢复为初始状态,并加入两个数据 2 作用初始状态'''
    _map_data[:] = []  # _map_data.clear()
    _map_data.append([0, 0, 0, 0])
    _map_data.append([0, 0, 0, 0])
    _map_data.append([0, 0, 0, 0])
    _map_data.append([0, 0, 0, 0])
    # 在空白地图上填充两个2
    fill2()
    fill2()


def get_space_count():
    """获取没有数字的方格的数量,如果数量为0则说有无法填充新数据,游戏即将结束
    """
    count = 0
    for r in _map_data:
        count += r.count(0)
    return count


def get_score():
    '''获取游戏的分数,得分规则是每次有两个数加在一起则生成相应的分数。
    如 2 和 2 合并后得4分, 8 和 8 分并后得 16分.
    根据一个大于2的数字就可以知道他共合并了多少次,可以直接算出分数:
    如:
       4 一定由两个2合并,得4分
       8 一定由两个4合并,则计:8 + 4 + 4 得32分
       ... 以此类推
    '''
    score = 0
    for r in _map_data:
        for c in r:
            score += 0 if c < 4 else c * int((math.log(c, 2) - 1.0))
    return score  # 导入数学模块


def fill2():
    '''填充2到空位置,如果填度成功返回True,如果已满,则返回False'''
    blank_count = get_space_count()  # 得到地图上空白位置的个数
    if 0 == blank_count:
        return False
    # 生成随机位置, 如,当只有四个空时,则生成0~3的数,代表自左至右,自上而下的空位置
    pos = random.randrange(0, blank_count)
    offset = 0
    for row in _map_data:  # row为行row
        for col in range(4):  # col 为列,column
            if 0 == row[col]:
                if offset == pos:
                    # 把2填充到第row行,第col列的位置,返回True
                    row[col] = 2
                    return True
                offset += 1


def is_gameover():
    """判断游戏是否结束,如果结束返回True,否是返回False
    """
    for r in _map_data:
        # 如果水平方向还有0,则游戏没有结束
        if r.count(0):
            return False
        # 水平方向如果有两个相邻的元素相同,应当是可以合并的,则游戏没有结束
        for i in range(3):
            if r[i] == r[i + 1]:
                return False
    for c in range(4):
        # 竖直方向如果有两个相邻的元素相同,应当可以合并的,则游戏没有结束
        for r in range(3):
            if _map_data[r][c] == _map_data[r + 1][c]:
                return False

    # 以上都没有,则游戏结束
    return True


# 以下是2048游戏的基本算法,此算法是在达内的美女老师"贾琳倩"提供算法上改进而来
# 此种算法不是最优算法,但我认为这是目前最容易理解的算法


def _left_move_number(line):
    '''左移一行数字,如果有数据移动则返回True,否则返回False:
    如: line = [0, 2, 0, 8] 即表达如下一行:
        +---+---+---+---+
        | 0 | 2 | 0 | 8 |      <----向左移动
        +---+---+---+---+
    此行数据需要左移三次:
      第一次左移结果:
        +---+---+---+---+
        | 2 | 0 | 8 | 0 |
        +---+---+---+---+
      第二次左移结果:
        +---+---+---+---+
        | 2 | 8 | 0 | 0 |
        +---+---+---+---+
      第三次左移结果:
        +---+---+---+---+
        | 2 | 8 | 0 | 0 |  # 因为最左则为2,所以8不动
        +---+---+---+---+
     最终结果: line = [4, 8, 0, 0]
    '''
    moveflag = False  # 是否移动的标识,先假设没有移动
    for _ in range(3):  # 重复执行下面算法三次
        for i in range(3):  # i为索引
            if 0 == line[i]:  # 此处有空位,右侧相邻数字向左侧移动,右侧填空白
                moveflag = True
                line[i] = line[i + 1]
                line[i + 1] = 0
    return moveflag


def _left_marge_number(line):
    '''向左侧进行相同单元格合并,合并结果放在左侧,右侧补零
    如: line = [2, 2, 4, 4] 即表达如下一行:
        +---+---+---+---+
        | 2 | 2 | 4 | 4 |
        +---+---+---+---+
    全并后的结果为:
        +---+---+---+---+
        | 4 | 0 | 8 | 0 |
        +---+---+---+---+
    最终结果: line = [4, 8, 8, 0]
    '''
    moveflag = False
    for i in range(3):
        if line[i] == line[i + 1]:
            moveflag = True
            line[i] *= 2  # 左侧翻倍
            line[i + 1] = 0  # 右侧归零
    return moveflag


def _left_move_aline(line):
    '''左移一行数据,如果有数据移动则返回True,否则返回False:
    如: line = [2, 0, 2, 8] 即表达如下一行:
        +---+---+---+---+
        | 2 |   | 2 | 8 |      <----向左移动
        +---+---+---+---+
    左移算法分为三步:
        1. 将所有数字向左移动来填补左侧空格,即:
            +---+---+---+---+
            | 2 | 2 | 8 |   |
            +---+---+---+---+
        2. 判断是否发生碰幢,如果两个相临且相等的数值则说明有碰撞需要合并,
           合并结果靠左,右则填充空格 
            +---+---+---+---+
            | 4 |   | 8 |   |
            +---+---+---+---+
        3. 再重复第一步,将所有数字向左移动来填补左侧空格,即:
            +---+---+---+---+
            | 4 | 8 |   |   |
            +---+---+---+---+
        最终结果: line = [4, 8, 0, 0]
    '''
    moveflag = False
    if _left_move_number(line):
        moveflag = True
    if _left_marge_number(line):
        moveflag = True
    if _left_move_number(line):
        moveflag = True
    return moveflag


def left():
    """游戏左键按下时或向左滑动屏幕时的算法"""
    moveflag = False  # moveflag 是否成功移动数字标志位,如果有移动则为真值,原地图不变则为假值

    # 将第一行都向左移动.如果有移动就返回True
    for line in _map_data:
        if _left_move_aline(line):
            moveflag = True
    return moveflag


def right():
    """游戏右键按下时或向右滑动屏幕时的算法
    选将屏幕进行左右对调,对调后,原来的向右滑动即为现在的向左滑动
    滑动完毕后,再次左右对调回来
    """
    # 左右对调
    for r in _map_data:
        r.reverse()
    moveflag = left()  # 向左滑动
    # 再次左右对调
    for r in _map_data:
        r.reverse()
    return moveflag


def up():
    """游戏上键按下时或向上滑动屏幕时的算法
    先把每一列都自上而下放入一个列表中line中,然后执行向滑动,
    滑动完成后再将新位置摆回到原来的一列中
    """
    moveflag = False
    line = [0, 0, 0, 0]  # 先初始化一行,准备放入数据
    for col in range(4):  # 先取出每一列
        # 把一列中的每一行数入放入到line中
        for row in range(4):
            line[row] = _map_data[row][col]
        # 将当前列进行上移,即line 左移
        if (_left_move_aline(line)):
            moveflag = True
        # 把左移后的 line中的数据填充回原来的一列
        for row in range(4):
            _map_data[row][col] = line[row]
    return moveflag


def down():
    """游戏下键按下时或向下滑动屏幕时的算法
    选将屏幕进行上下对调,对调后,原来的向下滑动即为现在的向上滑动
    滑动完毕后,再次上下对调回来
    """
    _map_data.reverse()
    moveflag = up()  # 上滑
    _map_data.reverse()
    return moveflag


def music_play():
    import pygame as py
    # 初始化
    py.mixer.init()
    # 文件加载
    py.mixer.music.load(r'.\test.mp3')
    # 播放  第一个是播放值 -1代表循环播放, 第二个参数代表开始播放的时间
    py.mixer.music.play(-1, 10)


# -------------------------以下为2048游戏的操作界面---------------------------

from tkinter import *
from tkinter import messagebox


def set_score(score):
    score_list.append(score)


def record_score(score):
    _grade_list = copy.copy(score_list)
    _grade_list.append(score)
    grade_list = list(set(_grade_list))
    grade_list.sort(reverse=True)
    for i in range(len(grade_list)):
        # 对比取出数据与待查找数据
        if grade_list[i] == score:
            # 查找成功,返回当前下标值
            return i + 1
        # 查找完所有数据,仍未找到
    # 查找失败,返回非法下标值
    return -1


def on_key_down(event):
    '键盘按下处理函数'
    keysym = event.keysym
    if keysym in keymap:
        if keymap[keysym]():  # 如果有数字移动
            fill2()  # 填充一个新的2
    update_ui()
    if is_gameover():
        set_score(get_score())
        mb = messagebox.askyesno(
            title="gameOver", message="游戏结束!\n是否退出游戏!")
        if mb:
            root.quit()
        else:
            reset()
            update_ui()


def update_ui():
    '''刷新界面函数
    根据计算出的f地图数据,更新各个Label的设置
    '''
    for r in range(4):
        for c in range(len(_map_data[0])):
            number = _map_data[r][c]  # 设置数字
            label = map_labels[r][c]  # 选中Lable控件
            label['text'] = str(number) if number else ''
            label['bg'] = mapcolor[number][0]
            label['foreground'] = mapcolor[number][1]
    label_score['text'] = str(get_score())  # 重设置分数
    label_ranking['text'] = str(record_score(get_score()))  # 重设置排名


# 以下排名按钮
def history_game():
    history_list = list(set(score_list))
    history_list.sort(reverse=True)
    message_data = ""
    for i in range(len(history_list)):
        message_data += f"第{i + 1}名:分数为{history_list[i]}\n"
    if not message_data:
        message_data = "暂无排名"
    messagebox.showwarning(title='历史排名', message=message_data)


# 以下设置重新开始按钮
def reset_game():
    set_score(get_score())
    reset()
    update_ui()
    label_ranking['text'] = '0'  # 重设置排名


if __name__ == '__main__':

    # 初始化音乐
    music_play()

    reset()  # 先重新设置游戏数据
    root = Tk()  # 创建tkinter窗口
    root.title('2048-by@Codeooo')  # 设置标题文字
    screen_width = root.winfo_screenwidth()  # 获得屏幕宽度
    screen_height = root.winfo_screenheight()  # 获得屏幕高度
    root.geometry('%dx%d+%d+%d' % (440, 490, (screen_width - 440) / 2, (screen_height - 490) / 2))
    root.resizable(width=False, height=False)  # 固定宽和高

    game_bg_color = "#bbada0"  # 背景色

    # 设置游戏中每个数据对应色块的颜色
    mapcolor = {
        0: ("#cdc1b4", "#776e65"),
        2: ("#eee4da", "#776e65"),
        4: ("#ede0c8", "#f9f6f2"),
        8: ("#f2b179", "#f9f6f2"),
        16: ("#f59563", "#f9f6f2"),
        32: ("#f67c5f", "#f9f6f2"),
        64: ("#f65e3b", "#f9f6f2"),
        128: ("#edcf72", "#f9f6f2"),
        256: ("#edcc61", "#f9f6f2"),
        512: ("#e4c02a", "#f9f6f2"),
        1024: ("#e2ba13", "#f9f6f2"),
        2048: ("#ecc400", "#f9f6f2"),
        4096: ("#ae84a8", "#f9f6f2"),
        8192: ("#b06ca8", "#f9f6f2"),
        # ----其它颜色都与8192相同---------
        2 ** 14: ("#b06ca8", "#f9f6f2"),
        2 ** 15: ("#b06ca8", "#f9f6f2"),
        2 ** 16: ("#b06ca8", "#f9f6f2"),
        2 ** 17: ("#b06ca8", "#f9f6f2"),
        2 ** 18: ("#b06ca8", "#f9f6f2"),
        2 ** 19: ("#b06ca8", "#f9f6f2"),
        2 ** 20: ("#b06ca8", "#f9f6f2"),
    }

    isParams = messagebox.askyesno(title="设置参数", message="是否进行设置游戏参数")
    if isParams:
        # 设置背景颜色
        def chooseColor(detail_color, title):
            color = askcolor(color=detail_color, title=title)
            return color[1]


        game_bg_color = chooseColor("#bbada0", '选择背景色')
        if not game_bg_color:
            game_bg_color = "#bbada0"

        block_color = chooseColor("#cdc1b4", '选择方块色')
        if not block_color:
            block_color = "#cdc1b4"

        mapcolor[0] = (block_color, block_color)
    else:
        messagebox.showwarning(title='start', message="开始游戏")

    # 以下是键盘映射
    keymap = {
        'a': left,
        'd': right,
        'w': up,
        's': down,
        'Left': left,
        'Right': right,
        'Up': up,
        'Down': down,
    }

    frame = Frame(root, bg=game_bg_color)
    frame.grid(sticky=N + E + W + S)
    # 设置焦点能接收按键事件
    frame.focus_set()
    frame.bind("<Key>", on_key_down)

    # 初始化图形界面
    # 创建4x4的数字块
    map_labels = []  # 游戏各方块的lable Widget
    for r in range(4):
        row = []
        for c in range(len(_map_data[0])):
            value = _map_data[r][c]
            text = str(value) if value else ''
            label = Label(frame, text=text, width=4, height=2,
                          font=("黑体", 30, "bold"))
            label.grid(row=r, column=c, padx=5, pady=5, sticky=N + E + W + S)
            row.append(label)
        map_labels.append(row)

    # 设置显示分数的Lable
    label = Label(frame, text='分数', font=("黑体", 30, "bold"),
                  bg="#bbada0", fg="#eee4da")
    label.grid(row=4, column=0, padx=5, pady=5)
    label_score = Label(frame, text='0', font=("黑体", 30, "bold"),
                        bg="#bbada0", fg="#ffffff")
    label_score.grid(row=4, columnspan=1, column=1, padx=5, pady=5)

    ranking = Label(frame, text='排名', font=("黑体", 30, "bold"),
                    bg="#bbada0", fg="#eee4da")
    ranking.grid(row=4, column=2, padx=5, pady=5)

    label_ranking = Label(frame, text='0', font=("黑体", 30, "bold"),
                          bg="#bbada0", fg="#ffffff")
    label_ranking.grid(row=4, columnspan=3, column=3, padx=5, pady=5)

    history_button = Button(frame, text='排名记录', font=("黑体", 16, "bold"),
                            bg="#8f7a66", fg="#f9f6f2", command=history_game)
    history_button.grid(row=5, column=0, padx=5, pady=5)

    restart_button = Button(frame, text='重新开始', font=("黑体", 16, "bold"),
                            bg="#8f7a66", fg="#f9f6f2", command=reset_game)
    restart_button.grid(row=5, column=3, padx=5, pady=5)

    update_ui()  # 更新界面
    label_ranking['text'] = '0'  # 重设置排名

    root.mainloop()  # 进入tkinter主事件循环

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/934115.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java+SpringBoot+Vue前后端分离工厂车间管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Android实现跟随滑块移动显示的seekBar

概述 详细讲述跟随滑块移动显示的seekBar效果的自定义实现过程 详细 前言 在Android开发过程中&#xff0c;我们有时会使用到自定义Seekbar,如在滑动滑块时&#xff0c;文字随滑块移动之类的效果&#xff0c;今天就来讲讲跟随滑块移动显示的seekBar的实现吧。 今天涉及内容…

IO进程线程、开启进程frok函数,exec函数族

进程是一个独立的可调度的任务 进程是一个抽象实体。当系统在执行某个程序时&#xff0c;分配和释放的各种资源 进程是一个程序的一次执行的过程 主要的进程标识&#xff1a; 进程号(Process Identity Number&#xff0c;PID) 父进程号(Parent Process ID&#xff0c;PPID) …

DML语句的用法(MySQL)

文章目录 前言一、DML介绍二、DML语句操作1、给指定字段添加数据2、给全部字段添加数据3、批量添加数据4、修改数据5、删除数据 总结 前言 本文主要介绍SQL语句中DML语句的用法。 在实验开始之前我们先创建一下所要使用表&#xff0c;如下图所示&#xff1a; 一、DML介绍 DM…

战略在集体学习过程中涌现

战略学习派&#xff1a;战略是涌现的学习过程&#xff0c;中国人的话&#xff0c;要交学习费&#xff01;【安志强趣讲269期】 趣讲大白话&#xff1a;出来混总要交学费 **************************** 中国人有这个意识 新进一个领域&#xff0c;要交学费&#xff0c;有学习过程…

【Flutter】Flutter 使用 infinite_scroll_pagination 实现无限滚动分页

【Flutter】Flutter 使用 infinite_scroll_pagination 实现无限滚动分页 文章目录 一、前言二、安装和基本使用1. 添加依赖2. 基础配置和初始化 三、实际业务中的用法1. 与 API 集成2. 错误处理 四、完整示例1. 创建一个无限滚动列表2. 使用在你的应用中3. 完整代码示例 五、总…

【Qt学习】08:文件读写操作

OVERVIEW 文件读写操作一、文件操作1.QFile2.QFileInfo 二、二进制文件读写三、文本文件读写 文件读写操作 文件操作是应用程序必不可少的部分&#xff0c;Qt 作为一个通用开发库提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象&#xff0c;这些设备具有…

Web自动化测试之图文验证码的解决方案

对于web应用程序来讲&#xff0c;处于安全性考虑&#xff0c;在登录的时候&#xff0c;都会设置验证码&#xff0c; 验证码的类型种类繁多&#xff0c;有图片中辨别数字字母的&#xff0c;有点击图片中指定的文字的&#xff0c;也有算术计算结果的&#xff0c;再复杂一点就是滑…

电子封条监控系统 yolov5

电子封条监控系统利用yoloov5python 深度学习训练模型技术&#xff0c;电子封条监控系统实现对画面内外的出入人员、人数变化及非煤矿山生产作业状态等情况的实时监测和分析&#xff0c;及时发现异常动态&#xff0c;减少了人为介入的过程。介绍Yolo算法之前&#xff0c;首先先…

Android开发之性能测试工具Profiler

前言 性能优化问题&#xff0c;在我们开发时都会遇到&#xff0c;但是在小厂和对自己要求不严格的情况下&#xff0c;我都很少去做性能优化&#xff1b; 在性能优化上&#xff0c;基本大家都是通过自己的开发经验和性能分析工具来发现问题&#xff0c;今天给大家分享一下小编最…

性能优化之分库分表

1、什么是分库分表 1.1、分表 将同一个库中的一张表&#xff08;比如SPU表&#xff09;按某种方式&#xff08;垂直拆分、水平拆分&#xff09;拆分成SPU1、SPU2、SPU3、SPU4…等若干张表&#xff0c;如下图所示&#xff1a; 1.2、分库 在表数据不变的情况下&#xff0c;对…

【GeoDa实用技巧100例】025:geoda空间回归分析案例教程

严重声明:本文来自专栏《GeoDa空间计量案例教程100例》,为CSDN博客专家刘一哥GIS原创,原文及专栏地址为:https://blog.csdn.net/lucky51222/category_12373659.html,谢绝转载或爬取!!! 文章目录 一、空间自回归模型二、Geoda空间回归分析普通最小二乘法回归(OLS)空间…

设计模式--建造者模式(Builder Pattern)

一、什么是建造者模式 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它关注如何按照一定的步骤和规则创建复杂对象。建造者模式的主要目的是将一个复杂对象的构建过程与其表示分离&#xff0c;从而使同样的构建过程可以创建不同的表示。…

基于配置类方式管理 Bean

目录 一、完全注解开发理解 二、配置类和扫描注解 三、Bean定义组件 四、Bean注解细节 五、import 扩展 一、完全注解开发理解 Spring 完全注解配置&#xff08;Fully Annotation-based Configuration&#xff09;是指通过 Java配置类 代码来配置 Spring 应用程序&#…

Folyd 多源最短路

目录 简介 实现 代码 关于Floyd的题目 简介 首先我们要知道a到b的最短路是什么 a到b的最短路是从a点到b点的最小距离&#xff08;花费&#xff09; 那多源最短路呢就是能求任意a和b&#xff0c;之间的最短路 那么Folyd是多源最短路&#xff0c;也就是求任意a和b&#x…

春秋云镜 CVE-2019-13275

春秋云镜 CVE-2019-13275 WordPress Plugin wp-statics SQLI 靶标介绍 WordPress VeronaLabs wp-statistics插件12.6.7之前版本中的v1/hit端点存在SQL注入漏洞。 启动场景 漏洞利用 exp time curl -X POST http://host/wp-json/wpstatistics/v1/hit --data "wp_stati…

SpringBoot权限认证

SpringBoot的安全 常用框架&#xff1a;Shrio,SpringSecurity 两个功能&#xff1a; Authentication 认证Authorization 授权 权限&#xff1a; 功能权限访问权限菜单权限 原来用拦截器、过滤器来做&#xff0c;代码较多。现在用框架。 SpringSecurity 只要引入就可以使…

mybatis:动态sql【2】+转义符+缓存

目录 一、动态sql 1.set、if 2.foreach 二、转义符 三、缓存cache 1. 一级缓存 2. 二级缓存 一、动态sql 1.set、if 在update语句中使用set标签&#xff0c;动态更新set后的sql语句&#xff0c;&#xff0c;if作为判断条件。 <update id"updateStuent" pa…

【C++】容器适配器stack、queue以及deque容器

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、什么是容器适配器1.1 stack的…

开源软件的漏洞响应:应对安全威胁

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…