前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java开发者的Python进修指南:2048小游戏编程解析

Java开发者的Python进修指南:2048小游戏编程解析

原创
作者头像
努力的小雨
发布2024-02-28 22:58:46
3400
发布2024-02-28 22:58:46
举报
文章被收录于专栏:灵墨AI探索室

最近我写的源码解析文章有点多了,想换个口味。今天决定练习一下Python,尝试实现一款当时风靡一时的2048小游戏。

游戏规则:《2048》是一款数字合并游戏,玩家通过上下左右滑动来控制所有方块的移动,当相同数字的方块移动时会合并成一个方块,数值相加。游戏的终极目标是合成一个数值为2048的方块。

在Python编程语言中,为了表示2048游戏的棋盘,可以采用二维列表的数据结构。在这个二维列表中,每个方块都会被一个数字所代表,其中0表示空格。

我们写一段简单的代码,不需过多的UI框架,直接在控制台运行即可。让我们来使用一下colorama。colorama是一个Python模块,专门用于在控制台和命令行中输出彩色文字,能够在各种操作系统上使用。

游戏逻辑

在这里简要介绍游戏逻辑,以便更好地理解业务代码。

  1. 初始化游戏棋盘,随机生成一个数字2。
  2. 检查游戏是否结束,即棋盘是否填满且不能再移动。
  3. 实现上下左右滑动操作,合并相同数字的方块。
  4. 判断是否达到2048,游戏胜利。
  5. 根据用户输入的方向操作,更新棋盘状态。

在这里我将详细解释实现的逻辑。这里只涉及数字向左移动,无论用户是向上、向右还是向下移动,都会被转换为向左移动。接下来我将演示如何根据向左移动的业务逻辑来实现向上移动。

image
image

再仔细审视一下向右移动的逻辑,同样的思路也可以应用到向右移动,即直接使用::-1来实现。完成了向左移动的合并逻辑后,再使用::-1来恢复原始顺序即可。

image
image

解决了上一个问题后,我们会进一步深入探讨如何在向左移动时优化合并相同数字的操作。这个过程也相对简单,简单来说,就是对列表进行递归处理:如果前两个元素相等,则将它们合并,并继续处理剩余部分;如果前两个元素不相等,则保留第一个元素并继续处理剩余部分。直至列表长度小于2时停止递归,最终返回处理完的结果列表。

image
image

主程序流程

根据上述基本逻辑,我们将简单实现主程序流程。考虑到需要持续监听用户的键盘操作,因此我们的主程序必须以一个while循环来实现。但是如何处理用户想要强制退出的情况呢?不能让用户关机,因此我们需要设定一个退出键来实现用户主动退出的功能。

  1. 初始化游戏棋盘。
  2. 进入游戏循环,直到游戏结束或者胜利。
  3. 在每轮循环中,接受用户输入的方向(W/A/S/D键)。
  4. 判断是否退出游戏(Q键)
  5. 根据用户输入的方向更新棋盘状态(全部转化为左)。
  6. 判断游戏是否结束或者胜利。
代码语言:python
代码运行次数:0
复制
import subprocess
import sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "readchar"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "colorama"])
subprocess.check_call([sys.executable, "-m", "pip", "install", "termcolor"])
  
from random import choice
from os import system
from readchar import readchar, readkey
from colorama import init
from termcolor import colored

N = 4
FGS = ['white', 'green', 'yellow', 'blue', 'cyan', 'magenta', 'red']
TERM = (10, 80)
OFFSET = (TERM[0] // 2 - 2, TERM[1] // 2 - 10)

pos          = lambda y, x: '\x1b[%d;%dH' % (y, x)
color        = lambda i: colored('%4d' % i, FGS[len('{:b}'.format(i)) % len(FGS)] if i else 'grey')
formatted    = lambda m: '\n'.join(pos(y, OFFSET[1]) + ' '.join(color(i) for i in l) for l, y in zip(m, range(OFFSET[0], OFFSET[0] + 4))) ## 正方形格式化打印出二维数组
combine      = lambda l: ([l[0] * 2] + combine(l[2:]) if l[0] == l[1] else [l[0]] + combine(l[1:])) if len(l) >= 2 else l ## 如果列表的前两个元素相等,就将它们合并并递归地继续处理剩余部分;如果前两个元素不相等,则保留第一个元素并继续处理剩余部分。直到列表长度小于2时停止递归,返回结果列表。
expand       = lambda l: [l[i] if i < len(l) else 0 for i in range(N)] ## 函数的作用是扩展列表 l 的长度至 N,如果 l 的长度小于 N,则在末尾添加足够多的 0 使其达到长度 N。
merge_left   = lambda l: expand(combine(list(filter(bool, list(l))))) 
merge_right  = lambda l: merge_left(l[::-1])[::-1]  ## 先倒置,按照左合并一样,再倒置回来
left         = lambda m: list(map(merge_left, m))
right        = lambda m: list(map(merge_right, m))
up           = lambda m: list(map(list, zip(*left(zip(*m)))))  ## 先反转列表,然后左移再反转
down         = lambda m: list(map(list, zip(*right(zip(*m))))) ## 先反转列表,然后右移再反转
add_num_impl = lambda m, p: m[p[0]].__setitem__(p[1], 2)  # 这里写死的2,其实可以选择一个(2和4)随机值增加游戏体验
add_num      = lambda m: add_num_impl(m, choice([(x, y) for x in range(N) for y in range(N) if not m[x][y]])) ## 随机选择一个符合要求的二维坐标地址
win          = lambda m: 2048 in sum(m, []) ## 只要存在2048即赢
gameover     = lambda m: all(m == t(m) for t in trans.values()) ## 如果所有变换都一样则结束游戏
draw         = lambda m: system("clear") or print(formatted(m)) ## 打印数组,这里注意下,如果CLS找不到命令可切换clear,目的是清空控制台

trans = {'a': left, 'd': right, 'w': up, 's': down}
m = [[0] * N for _ in range(N)] ## 初始化一个N x N的二维列表,并且每个元素值都是0
init() ## 命令行输出彩色文字
add_num(m)
draw(m)
while True:
    while True:
        move = readkey()
        if move in list(trans.keys()) + ['q']:
            break
    if move == 'q': ## 键盘‘Q’是游戏退出
        break
    n = trans[move](m)
    if n != m:
        add_num(n)
    m = n
    draw(m)
    if win(m):
        print('\n' + colored('(^_^) You Win!'.center(TERM[1]), 'yellow'))
        break
    elif gameover(m):
        print('\n' + colored('(>﹏<) Game Over!'.center(TERM[1]), 'red'))
        break

总结

最终,我们成功实现了经典游戏2048。现在,可以直接运行代码。本游戏利用二维列表数据结构来表示游戏棋盘,并在控制台中利用colorama模块实现了彩色文字输出。游戏的逻辑包括初始化棋盘、检查游戏是否结束、执行滑动操作、检查胜利条件等。通过简单的代码,我们实现了主程序流程,监听用户操作并更新棋盘状态,使得游戏具有交互逻辑。其中,最具挑战性的部分在于方向转换、合并和扩展数组。其他操作则相对基础。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 游戏逻辑
  • 主程序流程
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档