一起来写2048(160行python代码)

来源:互联网 发布:mac os 10.12官方下载 编辑:程序博客网 时间:2024/05/17 00:04

前言:

        Life is short ,you need python.

                                                                --Bruce Eckel

我与2048的缘,不是缘于一个玩家,而是一次,一次,又一次的ACM比赛.四月份校赛初赛,第一次碰到2048,两周后决赛再次遇到2048,后来五月份的广东省赛,又出现了2048。在这三次比赛过程中,我一次2048都没玩过..全靠队友的解释,直到昨天,我突然想起写个2048吧,于是下了个2048玩了几盘,之后就开始用python来写了,心想就不写界面了,为了简洁。

我对python并不熟悉,但是我在之前的博文就提过我希望成为一个python的玩家,所以我选择了python,同时,也希望大家也可以用非常简单的代码取实现2048。

首先,如果你没有玩过2048,没关系,你只要随便在网上或电子市场一搜索肯定会有各种版本,之后玩几把就会了。

来到这里,我假设大家都玩过2048了,于是我们先分析一下怎么来写一个简单的2048?回想一下,游戏的过程是很简单的:

              开始===>(  进行操作(上下左右)===>矩阵移动===>数字合并===>计分  )===>退出

括号内就是游戏的进行过程了,我们退出大概就是两个:1.出现2048,胜利退出 2.无法合并同时无法出现新数(是且的关系)

懂得这个运行过程后,我们就一步步来写,整个过程也不会难,哪怕你跟我一样基本是新手

先看看简略图,看起来虽然真的很朴素,但是爱美的你,可以在看完我的程序后自己美化。


储存结构:列表,其中包括二维矩阵列表,即所谓的mtr[4][4],以及栈,栈是用于退回,如果大家不想要这个功能可以直接忽略。

我们的变量定义:

declare = "←:a/h  ↓: s/j ↑: w/k →: d/l ,q(uit),b(ack)"illegal = "Illegal operation!"noefficient = "This move has no efficient"score = 0step = 0mtr = init()  # init the matrixmtr_stk = []  # use step for backscr_stk = []

稍微解释一下: declare是解释操作提示,自己喜欢怎么写就怎么写~

                             illegal是我们输入非正常操作提示,,可以省略

                             noefficient是我们往这个方向没效果,也可以省略~

                             score是得分,step是走了多少步,

                             mtr是我们的二维方阵,

                             mtr_stk,scr_stk是储存栈,用于后退功能,如果不要后退,还是可以省略~

算法实现:

 ·「注:其实没有算法可言....更准确叫函数实现。」

(1).在我们这个没有界面的2048中,我们只能用制表符啦,所以我们需要一个display函数。

 这是一个非常简单的事情,跟C语言的printf基本一致, 像如下代码,基本就能绘制出一个固定数字的表格了。

  

        print "┌"+("─"*5+"┬")*3+"─"*5+"┐"        print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4d │%4d │%4d │%4d │"%(1,2048,25,4)        print "└"+("─"*5+"┴")*3+"─"*5+"┘"

但是我们需要的是将固定的数字变成我们矩阵mtr的数字。

另外可以,注意到一点,数字输出的形式是固定的,表格边线,只有交叉点是不同,其他是0。而交叉点有五个,我们可以走循环(觉得麻烦可以直接忽略)

为了美观,我们不将0输出,所以我们输出时候选择的是字符串即%s,这样,我们遇到0,就可以输出' ',其他直接输出。

我们可以用python的三目运算符号

print "%4s " %(mtr[i][j] if mtr[i][j] else ' ')
所以我们的绘制表格写成这样子:最直接的方法!

def T(a):    return a if a else ' 'def display(mtr):        print "┌"+("─"*5+"┬")*3+"─"*5+"┐"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[0][0]),T(mtr[0][1]),T(mtr[0][2]),T(mtr[0][3]))        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[1][0]),T(mtr[1][1]),T(mtr[1][2]),T(mtr[1][3]))        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[2][0]),T(mtr[2][1]),T(mtr[2][2]),T(mtr[2][3]))        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[3][0]),T(mtr[3][1]),T(mtr[3][2]),T(mtr[3][3]))        print "└"+("─"*5+"┴")*3+"─"*5+"┘"

如果走循环可以这样写:就是枚举交叉点情况。

def display(mtrx): #output function    a = ("┌", "├", "├", "├", "└")    b = ("┬", "┼", "┼", "┼", "┴")    c = ("┐", "┤", "┤", "┤", "┘")    for i in range(4):        print a[i] + ("─" * 5 + b[i]) * 3 + ("─" * 5 + c[i])        for j in range(4):            print "│%4s" % (mtrx[i][j] if mtrx[i][j] else ' '),        print "│"    print a[4] + ("─" * 5 + b[4]) * 3 + ("─" * 5 + c[4])
(2)有了表格雏形,我们需要对矩阵进行初始化,then,我们就可以看到一个像模像样的2048开始啦!

初始化矩阵是非常简单的,我们在矩阵中选两个不同位置,设置为2其他为0即可。

先设置为0(没有memset,╮(╯▽╰)╭)

但是这样就可以啦:(看不惯?没关系,非常容易理解,慢慢习惯吧!)

mtr = [[0 for i in range(4)] for j in range(4)]  

我们选的位置是采取随机的,要保证不同,方法很多,但是我们python的random.sample就非常棒了!

我们用简单的一句

ran_pos = random.sample(range(16), 2)
便可以得到一个由两个不同组成的片段

之后我们通过 4*i+j 进行二维到一维的映射,同样也用( num/4 ,num%4)进行一维到二维的映射

所以我们初始话的过程也是十分简单。(要是偷懒,也可以直接忽略随机过程,自己指定吧!哈哈)

def init(): #initial of matrix    mtr = [[0 for i in range(4)] for j in range(4)]      ran_pos = random.sample(range(16), 2)    mtr[ran_pos[0]/4][ran_pos[0]%4] = mtr[ran_pos[1]/4][ran_pos[1]%4] = 2    return mtr
怎么样?2048有2个2啦!!!O(∩_∩)O哈哈~

(3)动起来!!让我们的2048动起来!
老实说,这个过程是全个程序最核心的部分,也是最难过的~不过理解了也就不难了。

我们先看向左移动吧。

记得我们玩的时候,合并过的格子是不会再在这次合并的,比如 某一行为 2 2 2 2 最后移动到的结果是4 4 0 0 而不是 8 0 0 0。我们要理解这个过程:

  我们对于每一行的操作是一样的,即行间无关,所以我们现在要讨论第i行,当我们向左动起来的时候,我们从最左开始确定,我们一开始合并前两个,再合并前三个,再合并前4个。比如

     2 2 4 4   => 4 0 4 4 => 4 4 0 4 =>4 4 4 0 => 4 8 0 0

注意两点: 1.合并过的不再参与合并,所以我们需要一个visit列表,用到前面所讲的映射

                     2.合并的情况=>相邻的相等,移动的情况=>当前元素为0

python代码

visit=[]score=0 for i in range(4):         for j in range(1, 4):              for k in range(j,0,-1):                    if mtr[i][k - 1] == 0 :                        mtr[i][k - 1] = mtr[i][k]                        mtr[i][k] = 0                    elif mtr[i][k - 1] == mtr[i][k] and 4 * i + k - 1 not in visit and 4 * i + k not in visit:                        mtr[i][k - 1] *= 2                        mtr[i][k] = 0                        score += mtr[i][k - 1]                        visit.append(4 * i + k)                        visit.append(4 * i + k - 1)
当你想明白这个过程后,整个move过程就自然清晰起来了。这个是本文第一个,最后一个比较长的函数了!

def move(mtr, dirct):#the core code!move by the four direction    score = 0    visit = []    if dirct == 0:  # left        for i in range(4):            for j in range(1, 4):                for k in range(j,0,-1):                    if mtr[i][k - 1] == 0 :                        mtr[i][k - 1] = mtr[i][k]                        mtr[i][k] = 0                    elif mtr[i][k - 1] == mtr[i][k] and 4 * i + k - 1 not in visit and 4 * i + k not in visit:                        mtr[i][k - 1] *= 2                        mtr[i][k] = 0                        score += mtr[i][k - 1]                        visit.append(4 * i + k)                        visit.append(4 * i + k - 1)    elif dirct == 1:  # down        for j in range(4):            for i in range(3, 0, -1):                for k in range(0,i):                   if mtr[k+1][j] == 0:                      mtr[k+1][j] = mtr[k][j]                      mtr[k][j]=0                   elif mtr[k+1][j]==mtr[k][j] and (4 *(k+1)+j) not in visit and (4*k+j) not in visit:                      mtr[k+1][j]*=2                      mtr[k][j]=0                      score=mtr[k+1][j]                      visit.append(4*(k)+j)                      visit.append(4*(k+1)+j)    elif dirct == 2:  # up        for j in range(4):            for i in range(1,4):                for k in range(i,0,-1):                    if mtr[k-1][j]==0:                        mtr[k-1][j]=mtr[k][j]                        mtr[k][j]=0                    elif mtr[k-1][j]==mtr[k][j] and (4 *(k-1)+j) not in visit and (4*k+j) not in visit:                        mtr[k-1][j]*=2                        mtr[k][j]=0                        score += mtr[k-1][j]                        visit.append(4*(k)+j)                        visit.append(4*(k-1)+j)    elif dirct == 3:  # right        for i in range(4):            for j in range(3, 0, -1):                for k in range(j):                   if mtr[i][k+1]  == 0:                      mtr[i][k+1] = mtr[i][k]                      mtr[i][k]=0                   elif mtr[i][k] ==mtr[i][k+1] and 4 * i + k + 1 not in visit and 4 * i + k not in visit:                      mtr[i][k+1]*=2                      mtr[i][k]=0                      score+=mtr[i][k+1]                      visit.append(4*i+k+1)                      visit.append(4*i+k)    return score
(4)当你来到这步,已经进入轻松的状态了,让控制台读入你的操作,短短两行,其中一行是转成小写

        dirct = raw_input("Step :%d Score :%d (%s):" % (step, score, declare))        dirct = dirct.lower()
(5)判断读入操作并处理

        if dirct == "q":            break        elif dirct == "a" or dirct == "h":            dirct = 0        elif dirct == "s" or dirct == "j":            dirct = 1        elif dirct == "w" or dirct == "k":            dirct = 2        elif dirct == "d" or dirct == "l":            dirct = 3        elif dirct == "b":            if len(mtr_stk) == 1:                print "Can't Back.."            else:                mtr_stk.pop()                scr_stk.pop()                mtr = copy.deepcopy(mtr_stk[-1])                score = scr_stk[-1]                step -= 1            continue        else:            print illegal            continue
我是vim 的脑残粉,所以还支持了hjkl,如果你没有习惯vim什么的,直接asdw就可以了。

上面最麻烦最值得注意的就是要用深复制.这个bug我调了很久啊亲,用"="伤不起啊。

上面的处理意思就是: 映射方向,和b,q的处理(有continue是因为我们将嵌套于while中)。

(6)移动的处理

在上面处理后,筛选出移动的操作,之后我们调用移动函数,进行加分,和比较。

        tmp = copy.deepcopy(mtr)        op_scr = move(mtr, dirct)        if tmp != mtr:            score = score + op_scr            update(mtr) #更新            display(mtr)            tmp = copy.deepcopy(mtr)            mtr_stk.append(tmp)  # 插入后退队列            scr_stk.append(int(score))            step = step + 1  # 步数加1        else:            print noefficient

注意:不能仅仅用加分来判断!因为我们很多时候都是没有碰撞合并,等待下一个2,4出现

我们用深复制,将老的矩阵放在tmp上,再移动,将老新矩阵比较,没改变的时候就输出 noefficient,否则就加分,插入新数字,并显示,最后将矩阵压入栈中,因为要后退!

(7)插入新数字

我们遍历0-16,并映射后,将对应0的位置放入random列表,我们用random.choice选出一个,同理我们生成一个ran_num=[2,4],选取加分

之后对应位置加分咯!但是注意,当我们没有空位时候不要插数字。

def update(mtr):    ran_pos=[]    ran_num=[2,4]    for i in range(4):        for j in range(4):            if mtr[i][j]==0:               ran_pos.append(4*i+j)    if len(ran_pos)>0:        k=random.choice(ran_pos)        n=random.choice(ran_num)        mtr[k/4][k%4]=n
(8)最后一个啦!!!游戏的循环条件

这个还是很简单的。游戏进行,当前仅当:没有2048且(有0或者可以合并)。

所以我们只需要判断2048 in mtr,有就结束,没有 == > 0 in mtr ?有就继续,没有==>可以合并?有继续,没有==》,就返回结束啦

def go_on(mtr, score):    if 2048 in mtr:        print "Great!You win!Your score is ", score        raw_input("Press any key to continue...")        exit()    if 0 in mtr:        return True    for i in range(4):        for j in range(4):            if i < 3 and mtr[i][j] == mtr[i + 1][j]:                return True            if j < 3 and mtr[i][j] == mtr[i][j + 1]:                return True    print "Gameover!"    return False
(9)一些其他的东西

注意一开始要画出开始的画面(循环外),一开始要将初始矩阵,初始分数,压入栈。

最后来个最终版本的

#!/usr/bin/env python# coding=utf-8#********************************************************# > OS     : Linux 3.2.0-60-generic #91-Ubuntu#> Author : yaolong#> Mail   : dengyaolong@yeah.net#> Time   : 2014年06月01日 星期日 13:13:39#********************************************************import randomimport copydef T(a):    return a if a else ' 'def display(mtr):        print "┌"+("─"*5+"┬")*3+"─"*5+"┐"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[0][0]),T(mtr[0][1]),T(mtr[0][2]),T(mtr[0][3]))        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[1][0]),T(mtr[1][1]),T(mtr[1][2]),T(mtr[1][3]))        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[2][0]),T(mtr[2][1]),T(mtr[2][2]),T(mtr[2][3]))        print "├"+("─"*5+"┼")*3+"─"*5+"┤"        print "│%4s │%4s │%4s │%4s │"%(T(mtr[3][0]),T(mtr[3][1]),T(mtr[3][2]),T(mtr[3][3]))        print "└"+("─"*5+"┴")*3+"─"*5+"┘"def init():    mtr = [[0 for i in range(4)] for j in range(4)]  # 小小蛋疼..    ran_pos = random.sample(range(16), 2)    mtr[ran_pos[0]/4][ran_pos[0]%4] = mtr[ran_pos[1]/4][ran_pos[1]%4] = 2        return mtrdef go_on(mtr, score):    if 2048 in mtr:        print "Great!You win!Your score is ", score        raw_input("Press any key to continue...")        exit()    if 0 in mtr:        return True    for i in range(4):        for j in range(4):            if i < 3 and mtr[i][j] == mtr[i + 1][j]:                return True            if j < 3 and mtr[i][j] == mtr[i][j + 1]:                return True    print "Gameover!"    return Falsedef move(mtr, dirct):    score = 0    visit = []    if dirct == 0:  # left        for i in range(4):            for j in range(1, 4):                for k in range(j,0,-1):                    if mtr[i][k - 1] == 0 :                        mtr[i][k - 1] = mtr[i][k]                        mtr[i][k] = 0                    elif mtr[i][k - 1] == mtr[i][k] and 4 * i + k - 1 not in visit and 4 * i + k not in visit:                        mtr[i][k - 1] *= 2                        mtr[i][k] = 0                        score += mtr[i][k - 1]                        visit.append(4 * i + k)                        visit.append(4 * i + k - 1)        # for i in range(4):        #    for j in range(3):    elif dirct == 1:  # down        for j in range(4):            for i in range(3, 0, -1):                for k in range(0,i):                   if mtr[k+1][j] == 0:                      mtr[k+1][j] = mtr[k][j]                      mtr[k][j]=0                   elif mtr[k+1][j]==mtr[k][j] and (4 *(k+1)+j) not in visit and (4*k+j) not in visit:                      mtr[k+1][j]*=2                      mtr[k][j]=0                      score=mtr[k+1][j]                      visit.append(4*(k)+j)                      visit.append(4*(k+1)+j)    elif dirct == 2:  # up        for j in range(4):            for i in range(1,4):                for k in range(i,0,-1):                    if mtr[k-1][j]==0:                        mtr[k-1][j]=mtr[k][j]                        mtr[k][j]=0                    elif mtr[k-1][j]==mtr[k][j] and (4 *(k-1)+j) not in visit and (4*k+j) not in visit:                        mtr[k-1][j]*=2                        mtr[k][j]=0                        score += mtr[k-1][j]                        visit.append(4*(k)+j)                        visit.append(4*(k-1)+j)    elif dirct == 3:  # right        for i in range(4):            for j in range(3, 0, -1):                for k in range(j):                   if mtr[i][k+1]  == 0:                      mtr[i][k+1] = mtr[i][k]                      mtr[i][k]=0                   elif mtr[i][k] ==mtr[i][k+1] and 4 * i + k + 1 not in visit and 4 * i + k not in visit:                      mtr[i][k+1]*=2                      mtr[i][k]=0                      score+=mtr[i][k+1]                      visit.append(4*i+k+1)                      visit.append(4*i+k)    return scoredef update(mtr):    ran_pos=[]    ran_num=[2,4]    for i in range(4):        for j in range(4):            if mtr[i][j]==0:               ran_pos.append(4*i+j)    if len(ran_pos)>0:        k=random.choice(ran_pos)        n=random.choice(ran_num)        mtr[k/4][k%4]=n# map 0 left,1 down,2 up ,3 right# a,h=> left ,s,j=>down, w,k=>up, d,l=>rightdeclare = "←:a/h  ↓: s/j ↑: w/k →: d/l ,q(uit),b(ack)"illegal = "Illegal operation!"noefficient = "This move has no efficient"if __name__ == '__main__':    score = 0    step = 0    mtr = init()    mtr_stk = []  # for back    scr_stk = []    tmp = copy.deepcopy(mtr)    mtr_stk.append(tmp)    scr_stk.append(0)    display(mtr)    while go_on(mtr, score):        dirct = raw_input("Step :%d Score :%d (%s):" % (step, score, declare))        dirct = dirct.lower()        if dirct == "q":            break        elif dirct == "a" or dirct == "h":            dirct = 0        elif dirct == "s" or dirct == "j":            dirct = 1        elif dirct == "w" or dirct == "k":            dirct = 2        elif dirct == "d" or dirct == "l":            dirct = 3        elif dirct == "b":            if len(mtr_stk) == 1:                print "Can't Back.."            else:                mtr_stk.pop()                scr_stk.pop()                mtr = copy.deepcopy(mtr_stk[-1])                score = scr_stk[-1]                step -= 1            continue        else:            print illegal            continue        tmp = copy.deepcopy(mtr)        op_scr = move(mtr, dirct)        if tmp != mtr:            score = score + op_scr            update(mtr) #更新            display(mtr)            tmp = copy.deepcopy(mtr)            mtr_stk.append(tmp)  # 插入后退队列            scr_stk.append(int(score))            step = step + 1  # 步数加1        else:            print noefficient

还有我github的版本,大致一样,点这里

如果我的算法有什么不对,请提出,因为写得匆忙,我未能很完整第测试,但是我自己玩了几把还是OK了,没玩到2048╮(╯▽╰)╭。。。


7 1
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 供应商给购物卡怎么办 挪用公司的钱怎么办 舞东风积分卡怎么办 电信不注销欠费怎么办 养老宝忘记密码怎么办 学生信用卡0额度怎么办 微信商户号怎么办 企业Uk证书过期怎么办 加挂账号不知道怎么办 钱打到对公账户怎么办 新中付没有到账怎么办 qq把钱转错了怎么办 转账输错号码怎么办 给别人转错钱了怎么办 转账到信用卡了怎么办 银行卡限额怎么办京东 预留信息忘了怎么办 中信银行香港卡怎么办 无银行预留信息怎么办 农工商超市红利卡怎么办 余额宝转入不了怎么办 公司车辆怎么办营运证 便利店转不出去怎么办 便利店开业营业额低怎么办 现在etc超时了怎么办? 中国银行e贷逾期怎么办 招商银行e分期逾期怎么办 保单贷款还不了怎么办 离婚时按揭房怎么办 按揭房子不要了怎么办? 征信有问题怎么办房贷 夫妻一方不做房贷共还人怎么办 给你花逾期一年怎么办 文件夹密码忘记了怎么办 网址被qq拦截怎么办 手机qq邮箱中毒怎么办 邮箱附件带病毒怎么办? 打开了病毒附件怎么办? qq邮箱被拦截怎么办 邮件地址已被注册怎么办 信用卡欠款怎么办房贷