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

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


用 python 写一个用命令行玩的 2048 小游戏_第1张图片
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()

就是如下效果:

用 python 写一个用命令行玩的 2048 小游戏_第2张图片
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

你可能感兴趣的:(用 python 写一个用命令行玩的 2048 小游戏)