用 python 写一个用命令行玩的 2048 小游戏

下面是我们要达到的效果,在命令行里面玩2048,是不是很酷?
写出这个小程序其实用的知识很少,主要是用了嵌套列表,循环,if 判断,还有一点点 random 和copy 模块。作为练习是个很好的项目。


2048.png

初步分析游戏的流程

首先来分析一下这个游戏。

1 . 游戏界面为一个 4 X 4 一共 16 的方格,开始时在随机的两个格子里面生成两个数字 2 。

2 . 可以上下左右滑动界面,数字会向着滑动的方向移动,途中若遇到相同的数字,则合并成一个数,值为二者的和,直到移动不了为止。同时在每次移动的时候,会在空白的地方再次生成两个数字 2 。

3 . 若没有空格子且相邻没有相同的数字,那么游戏结束。

游戏第一步

第一步 当然是先写一个游戏界面,为了方便后面的操作和计算,需要先来装这些数据,因为要涉及到计算和修改,那么 python 内置的只有列表和字典可以考虑,但是字典是无序的,在定位上可能会问题,所以用列表比较合适。可以用一个嵌套列表来表示位置和值,写这个界面的方法很像写九九乘法表。

import random
# 开始界面
def start():
    list_2048 = [['    ', '    ', '    ', '    '], ['    ', '    ', '    ', '    '], ['    ', '    ', '    ', '    '], ['    ', '    ', '    ', '    ']]
    i = 0
    while i < 2:
        x = random.randint(0,3)
        y = random.randint(0,3)
        if list_2048[y][x] == '    ':
            list_2048[y][x] = '%4d'%2
            i += 1
    return list_2048

我开始生成一个空的嵌套列表,里面存放的全是 4 个空格组成的字符串,是因为我玩的时候尝试过,很难玩超过四位数(实际上我只玩到过三位数....),是拿来占位用,以免在游戏玩的过程中,界面出现错位。所以我在后面往里面添加元素的时候,添加的也是长度为 4 的字符串,而不是单纯的数字 2 。

实际上,我们写的游戏的界面就已经完成了,后面全是在此列表中的转换。列表的框架就这个样子了,放了后面方便调用,写一个关于打印的函数。

# 将列表打印出游戏界面的效果
def print_number(li):
    for row in li:
        for number in row:
            print(number,end='|')
        print()

就是如下效果:

start.png

游戏过程

这个游戏操作还是很简单的,只能上下左右四个方向移动,故在玩的过程中只需要把这四个过程都写一遍,后面只是一直循环调用这个四个方法而已。
先来分析向下移动,游戏的效果是,所有的数字向下移动,如果碰到相同的数字,那么合并成一个数字。
初看比较复杂,又要移动,又要合并,而且因为数字在的位置不同,移动的单位也不相同,同时每次移动还要随机在空白区域产生两个数字。

简化过程:
步骤 1 :只移动,不合并。直到数字中间没有空隙(垂直方向),移动过程就结束了。因为中间没有空隙,那说明所有的数字都移动到位了,不能再继续移动了。
步骤 2 :只合并不移动。用循环在判断的方式来验证相应方向上有没有可以合并的。
步骤 3 :综合两个过程,先移动,然后判断合并,因为在合并的过程中,可能会产生空隙,故还需要调用一次移动,最后在两个空白区域在产生两个数字,当然到游戏的中后段,可能只会有一个空格或者没有空格,也要做相应的判断。在我写这篇文章的时候,才发现,我在这一步的判断中的逻辑很有问题,虽然最后呈现的效果一样,但是增加了很多不必要的计算量。(由于现在深夜了,我也懒得改了,给大家留个小作业,读者自己改改吧。)

# down_step1 是将游戏界面里面所有的元素堆在下面,使中间没有空隙
def down_step1(old_list):
    while True:
        for x in range(3):
            for y in range(4):
                if old_list[x][y] != '    ' and old_list[x+1][y] == '    ':
                    old_list[x][y],old_list[x+1][y] = old_list[x+1][y],old_list[x][y]
        count = 0
        for y in range(4):
            judge_list = []
            for x in range(4):
                judge_list.append(str(old_list[x][y]))
            judge_str = ''.join(judge_list)
            judge_str = judge_str.strip()
            if '    ' in judge_str:
                break
            else:
                count += 1
        if count == 4:
            break
    return old_list

# down_step2 是将上下相邻且相同的数字合并,因为是向下移动,故判断的时候是从下像上判断,这点很重要。
def down_step2(old_list):
    global score
    for x in range(4):   # 将前面处理好,挨着紧凑的数据来判断最后两项是否相同,若相同就将两项合并
        if old_list[3][x] != '    ' and old_list[3][x] == old_list[2][x]:
            old_list[3][x] = '%4d'%(int(old_list[3][x]) * 2)
            old_list[2][x] = '    '
            score += int(old_list[3][x])
            if old_list[1][x] != '    ' and old_list[1][x] == old_list[0][x]:
                old_list[1][x] = '%4d'%(int(old_list[1][x]) * 2)
                old_list[0][x] = '    '
                score += int(old_list[1][x])
        elif old_list[2][x] != '    ' and old_list[2][x] == old_list[1][x]:
            old_list[2][x] = '%4d'%(int(old_list[2][x]) * 2)
            old_list[1][x] = '    '
            score += int(old_list[2][x])
        elif old_list[1][x] != '    ' and old_list[1][x] == old_list[0][x]:
            old_list[1][x] = '%4d'%(int(old_list[1][x]) * 2)
            old_list[0][x] = '    '
            score += int(old_list[1][x])
    return old_list
'''
down 是中和两个步骤,然后在把处理完的元素堆在下面。
这里没有用循环处理,是因为要让玩家每操作一次,也只变动一次,
操作一次,而变动多次会使玩家捉摸不到游戏的规律性
'''
def down(old_list):
    judge_list = copy.deepcopy(old_list)
    old_list = down_step1(old_list)
    old_list = down_step2(old_list)
    old_list = down_step1(old_list)
    if judge_list == old_list:
        return old_list
    if old_list[0].count('    ') + old_list[1].count('    ') + old_list[2].count('    ') + old_list[3].count('    ') >= 2:
        k = 0
        while k < 2:
            x = random.randint(0,3)
            y = random.randint(0,3)
            if old_list[y][x] == '    ':
                old_list[y][x] = '%4d'%2
                k += 1
    elif old_list[0].count('    ') + old_list[1].count('    ') + old_list[2].count('    ') + old_list[3].count('    ') == 1:
        k = 0
        while k < 1:
            x = random.randint(0, 3)
            y = random.randint(0, 3)
            if old_list[y][x] == '    ':
                old_list[y][x] = '%4d'%2
                k += 1
    else:
        pass
    return old_list

相同的逻辑,其他几个方向都可以写出来了,其中 score 是游戏的分数,这段代码是我从程序里面复制出来的,就不改了。在这个片段中没有实际作用。

将四个方向写完后,那就是重复的调用了,因为是在命令行里面玩,就需要玩家通过输入命令来操作游戏。
我设定的是通过 wsad 四个键来控制 上下左右四个方向。

最后就是判断游戏结束的条件,此游戏的结束条件是,所有的空格都被数字填满,而且每个数字相邻的位置的数都不相同。

为了增加游戏的趣味性,也做了一个记分规则,每次移动合并后的数字之和为本次移动所获得的分数。比如:2和2合并为4,那么分数为4。最后把所有的分数累加起来就是最后的玩家的得分。

# 启动游戏

old_list = start()
print_number(old_list)

while True:
    # 根据用户输入做出相应的移动
    print()
    user_input = input('请输入你要移动的方向[上(w)下(s)左(a)右(d)]:')
    print()
    if user_input == 'w':
        old_list = up(old_list)
        print_number(old_list)
        print()
        print('目前得分:%d 分'%score)
    elif user_input == 's':
        old_list = down(old_list)
        print_number(old_list)
        print()
        print('目前得分:%d 分' % score)
    elif user_input == 'a':
        old_list = left(old_list)
        print_number(old_list)
        print()
        print('目前得分:%d 分' % score)
    elif user_input == 'd':
        old_list = right(old_list)
        print_number(old_list)
        print()
        print('目前得分:%d 分' % score)
    else:
        print()
        print('输入错误!请输入正确的方向!')
        print_number(old_list)
    # 游戏的终止条件
    # 判断界面是否还有空位
    if old_list[0].count('    ') + old_list[1].count('    ') + old_list[2].count('    ') + old_list[3].count('    ') == 0:
        # 判断每一个数相邻是否还有相同的数字
        count = 0
        for i in range(4):          # 判断横排
            for j in range(3):
                if old_list[i][j] == old_list[i][j+1]:
                    count += 1
        for x in range(4):
            for y in range(3):
                if old_list[y][x] == old_list[y+1][x]:
                    count += 1
        if count == 0:
            print('很遗憾,游戏结束!')
            print('您的最终得分:%d 分' % score)
            break

至此结束。如果有什么更好的做法,欢迎交流,互相学习。
若要代码的小伙伴,可以通过下面地址去拿。
代码 : https://gitee.com/MiltonL/codes/zhuf495dgb7loeavstyc068

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,727评论 25 707
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,197评论 9 467
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,058评论 4 62
  • 001.今天腰疼去推背,每次给我推的那个姐姐没在,老板娘给我推的背。各种揉面式的技巧。还一个劲的说:“你是不是很享...
    Peach桃花阅读 104评论 0 0
  • 从事情从开始到现在过了多久印象已经很模糊了。只记得一开始是不愿意接受这个消息的,即使是看到了也会刻意去逃避。不想接...
    伽布阅读 465评论 0 0