Python 练习 —— 2048

Python 练习 —— 2048

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

1. 引言

     2048 这段时间火的不行啊,大家都纷纷仿造,“百家争鸣”,于是出现了各种技术版本号:除了手机版本号,还有C语言版、Qt版、Web版、java版、C#版等,刚好我接触Python不久,于是弄了个Python版——控制台的2048,正好熟悉下Python语法,程序执行效果例如以下:

Python 练习 —— 2048

图 1  Python版控制台2048执行截图


     程序代码加上凝视大概150行左右,利用了一些Python内置数据类型的操作节省了不少代码量。以下说说我的编写思路,最后会给出
源码

2. 2048 实现思路

     
2.1 游戏规则
     这个游戏可玩性非常好,简单的移动方向键让数字叠加,而且获得这些数字每次叠加后的得分,当出现2048这个数字时游戏胜利。同一时候每次移动方向键时,都会在这个4*4的方格矩阵的空白区域随机产生一个数字2或者4,假设方格被数字填满了,那么就GameOver了。

    
Python 练习 —— 2048


   
 
 2.2 实现思路
     这个游戏的所有操作都是环绕着一个4*4的矩阵进行,每次从用户界面获取用户的操作(即移动方向),然后又一次计算这个4*4矩阵的状态,最后刷新用户界面显示4*4矩阵的最新状态,不断的循环这个过程,直到出现2048或没有空白方块了,以下是一个处理流程示意图:
    
Python 练习 —— 2048

     我写的是控制台程序,没有UI界面,因此用字符(W/S/A/D)代表方向键的输入,以数字0代表空白方格。接下来是计算部分,以向左移动为例,4*4矩阵在接收到向左移动的指令后,应该将每行的数字向左叠加, 将一行的叠加操作定义为函数 handle(list, direction),其第一个參数用来存储4*4矩阵中的某一行(列),第二个參数表示移动的方向(上下左右)。

     这样当左右移动方向键时,能够这样来计算矩阵:遍历矩阵的每行,并将每行的数字沿左或右进行叠加操作。
for row in matrix:
         handle(row, direction)

     对于上下移动方向键时,因为矩阵是按行存储的,不能直接处理矩阵中的列,能够通过变通採用上面的函数handle()。对于矩阵中每一列,先将其复制到一个列表中,然后调用handle()函数对该列表进行叠加处理,最后再将叠加后的新列表拷贝回原始矩阵中其所在的列,其逻辑上等同于以下的代码操作。
for col in matrix:
          handle(col, direction)

     handle(row, direction)函数的作用是沿指定方向叠加一行中的数字,请看以下几个样例:
移动方向 移动前 移动后
handle(x, ‘left’)
x = [0, 2, 2, 2] 
x = [4, 2, 0, 0]
handle(x, ‘left’)
x = [2, 4, 2, 2]
x = [2, 8, 0, 0]
handle(x, ‘right’) x = [2, 4, 2, 2] x = [0, 0, 2, 8]

实现 handle(row, direction) 函数

    依据上面的介绍,实现handle函数是关键。细致观察叠加的过程,其都是由两个子过程组成的:
(1)
 align(row, direction)
   沿direction方向对齐列表row中的数字,比如:
x = [0, 4, 0, 2]
align(x, ‘left’)  后 x = [4, 2, 0, 0]
在 align(x, ‘right’) 后 x = [0, 0, 4, 2]

(2)
 
addSame(row, direction)
 查找
同样且相邻的数字。假设找到,将当中一个翻倍,还有一个置0
(假设direction是’left’将左側翻倍,右側置0,假设direction为’right’,将右側翻倍,左側置0),
并返回True;否则,返回False。比如:
x = [2, 2, 2, 2]
addSame(x, ‘left’) 后 x = [4, 0, 2, 2]      返回 True
再 addSame(x, ‘left’) 后 x = [4, 0, 4, 0]   返回 True
再 addSame(x, ‘left’) 后 x = [4, 0, 4, 0]   返回 False 


     有了上面两个子函数,应该不难实现。有了这两个子函数,函数handle()就非常好实现了,例如以下:
handle(row, direction):
          align(row, direction)
          result = addSame(row, direction)
          while result == True:
                    align(row, direction)
                    result = addSame(row, direction)

     以下结合一个实际样例再来看看handle函数的处理过程:
x = [2, 4, 2, 2]
调用 handle(x, ‘right’),变量 x 变化过程:
align(x, ‘right’)          ->     [2, 4, 2, 2]
addSame(x, ‘right’)   ->     [2, 4, 0, 4]     ->     return True
align(x, ‘right’)          ->     [0, 2, 4, 4]    
addSame(x, ‘right’)   ->     [0, 2, 0, 8]     ->     return True
align(x, ‘right’)          ->     [0, 0, 2, 8]    
addSame(x, ‘right’)   ->     [0, 0, 2, 8]     ->     return False
终于得到的 x = [0, 0, 2, 8]
     
     最基本的部分已经说完了,以下就贴出代码吧,因为代码不多就直接贴出来吧。

3. 代码
   

这些是所有代码,保存在单一文件里就可以执行,执行环境 Python3.0+

# -*- coding:UTF-8 -*-
#! /usr/bin/python3

import random

v = [[0, 0, 0, 0],
     [0, 0, 0, 0],
     [0, 0, 0, 0],
     [0, 0, 0, 0]]

def display(v, score):
        '''显示界面

        '''
        print('{0:4} {1:4} {2:4} {3:4}'.format(v[0][0], v[0][1], v[0][2], v[0][3]))
        print('{0:4} {1:4} {2:4} {3:4}'.format(v[1][0], v[1][1], v[1][2], v[1][3]))
        print('{0:4} {1:4} {2:4} {3:4}'.format(v[2][0], v[2][1], v[2][2], v[2][3]))
        print('{0:4} {1:4} {2:4} {3:4}'.format(v[3][0], v[3][1], v[3][2], v[3][3]), 
'    Total score: ', score)

def init(v):
        '''随机分布网格值
        
        '''
        for i in range(4):
                v[i] = [random.choice([0, 0, 0, 2, 2, 4]) for x in v[i]]

def align(vList, direction):
        '''对齐非零的数字

        direction == 'left':向左对齐,比如[8,0,0,2]左对齐后[8,2,0,0]
        direction == 'right':向右对齐,比如[8,0,0,2]右对齐后[0,0,8,2]
        '''

        # 移除列表中的0
        for i in range(vList.count(0)):
                vList.remove(0)
        # 被移除的0
        zeros = [0 for x in range(4 - len(vList))]
        # 在非0数字的一側补充0
        if direction == 'left':
                vList.extend(zeros)
        else:
                vList[:0] = zeros
        
def addSame(vList, direction):
        '''在列表查找同样且相邻的数字相加, 找到符合条件的返回True,否则返回False,同一时候还返回添加�的分数
        
        direction == 'left':从右向左查找,找到同样且相邻的两个数字,左側数字翻倍,右側数字置0
        direction == 'right':从左向右查找,找到同样且相邻的两个数字,右側数字翻倍,左側数字置0
        '''
        score = 0
        if direction == 'left':
                for i in [0, 1, 2]:
                        if vList[i] == vList[i+1] != 0: 
                                vList[i] *= 2
                                vList[i+1] = 0
                                score += vList[i]
                                return {'bool':True, 'score':score}
        else:
                for i in [3, 2, 1]:
                        if vList[i] == vList[i-1] != 0:
                                vList[i-1] *= 2
                                vList[i] = 0
                                score += vList[i-1]
                                return {'bool':True, 'score':score}
        return {'bool':False, 'score':score}

def handle(vList, direction):
        '''处理一行(列)中的数据,得到终于的该行(列)的数字状态值, 返回得分

        vList: 列表结构,存储了一行(列)中的数据
        direction: 移动方向,向上和向左都使用方向'left',向右和向下都使用'right'
        '''
        totalScore = 0
        align(vList, direction)
        result = addSame(vList, direction)
        while result['bool'] == True:
                totalScore += result['score']
                align(vList, direction)
                result = addSame(vList, direction)
        return totalScore
        

def operation(v):
        '''依据移动方向又一次计算矩阵状态值,并记录得分
        '''
        totalScore = 0
        gameOver = False
        direction = 'left'
        op = input('operator:')
        if op in ['a', 'A']:    # 向左移动
                direction = 'left'
                for row in range(4):
                        totalScore += handle(v[row], direction)
        elif op in ['d', 'D']:  # 向右移动
                direction = 'right'
                for row in range(4):
                        totalScore += handle(v[row], direction)
        elif op in ['w', 'W']:  # 向上移动
                direction = 'left'
                for col in range(4):
                        # 将矩阵中一列拷贝到一个列表中然后处理
                        vList = [v[row][col] for row in range(4)]
                        totalScore += handle(vList, direction)
                        # 从处理后的列表中的数字覆盖原来矩阵中的值
                        for row in range(4):
                                v[row][col] = vList[row]
        elif op in ['s', 'S']:  # 向下移动
                direction = 'right'
                for col in range(4):
                        # 同上
                        vList = [v[row][col] for row in range(4)]
                        totalScore += handle(vList, direction)
                        for row in range(4):
                                v[row][col] = vList[row]
        else:
                print('Invalid input, please enter a charactor in [W, S, A, D] or the lower')
                return {'gameOver':gameOver, 'score':totalScore}

        # 统计空白区域数目 N
        N = 0
        for q in v:
            N += q.count(0)
        # 不存在剩余的空白区域时,游戏结束
        if N == 0:
                gameOver = True
                return {'gameOver':gameOver, 'score':totalScore}

        # 按2和4出现的几率为3/1来产生随机数2和4
        num = random.choice([2, 2, 2, 4]) 
        # 产生随机数k,上一步产生的2或4将被填到第k个空白区域
        k = random.randrange(1, N+1)
        n = 0
        for i in range(4):
                for j in range(4):
                        if v[i][j] == 0:
                                n += 1
                                if n == k:
                                        v[i][j] = num
                                        break

        return {'gameOver':gameOver, 'score':totalScore}

init(v)
score = 0
print('Input:W(Up) S(Down) A(Left) D(Right), press <CR>.')
while True:
        display(v, score)
        result = operation(v)
        if result['gameOver'] == True:
                print('Game Over, You failed!')
                print('Your total score:', score)
        else:
                score += result['score']
                if score >= 2048:
                        print('Game Over, You Win!!!')
                        print('Your total score:', score)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/118684.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • TLS/SSL 协议详解 (29) 国密SSL协议

    TLS/SSL 协议详解 (29) 国密SSL协议测试服务器:https://47.89.249.43:4433/(测试时,先将本机时间设置为2018年7月之前(我证书过期了),然后使用360国密浏览器访问。360国密浏览器会在TLS握手失败后才会发起GMSSL握手,所以访问较慢。出现访问不了的情况,请清除360国密浏览器所有缓存,重启浏览器后再访问)源码在https://github.com/mrpre/atls上可以获得…

    2022年6月2日
    104
  • KL散度、JS散度、Wasserstein距离[通俗易懂]

    KL散度、JS散度、Wasserstein距离[通俗易懂]1.KL散度KL散度又称为相对熵,信息散度,信息增益。KL散度是是两个概率分布P和Q差别的非对称性的度量。KL散度是用来度量使用基于Q的编码来编码来自P的样本平均所需的额外的位元数。典型情况下,P表示数据的真实分布,Q表示数据的理论分布,模型分布,或P的近似分布。定义如下:DKL(P//Q)=−∑x∈XP(x)log1P(x)+∑x∈XP(x)log1Q(x)DKL(…

    2022年4月19日
    115
  • docker pull image 打tag

    docker pull image 打tag

    2021年5月14日
    140
  • random.nextint java_实例讲解Java中random.nextInt()与Math.random()的基础用法

    random.nextint java_实例讲解Java中random.nextInt()与Math.random()的基础用法1、来源random.nextInt()为java.util.Random类中的方法;Math.random()为java.lang.Math类中的静态方法。2、用法产生0-n的伪随机数(伪随机数参看最后注解)://两种生成对象方式:带种子和不带种子(两种方式的区别见注解)Randomrandom=newRandom();Integerres=random.nextInt…

    2022年7月22日
    19
  • 公务员叫停年终奖_100个月年终奖

    公务员叫停年终奖_100个月年终奖年底红包,多少羡慕嫉妒恨  时光荏苒,岁月蹉跎。不知不觉间,2011年即将走过。忙碌了一年的人们,自进入12月便开始了年终奖的各种讨论及猜测。在贴吧,微博网友们纷纷以晒年终奖的方式品味着所有快乐以及忧愁。  2011年,通胀压力下物价节节攀高,进入下半年CPI指数有所下降,但消费支出压力丝毫没有得到缓解,特别是集中到年底流水似的花销更叫人隐隐作痛。在现实的生存压力之下,上班族对年终

    2025年11月5日
    3
  • 必须为元素类型 “mapper” 声明属性 “namespace” 或The content of element type “mapper” must match “EMPTY”

    必须为元素类型 “mapper” 声明属性 “namespace” 或The content of element type “mapper” must match “EMPTY”

    2021年7月19日
    106

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号