zoukankan      html  css  js  c++  java
  • 一起来写2048(160行python代码)

    前言:

            Life is short ,you need python.

                                                                     --Bruce Eckel

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

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

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

    来到这里,我如果大家都玩过2048了,于是我们先分析一下怎么来写一个简单的2048?回忆一下,游戏的过程是非常easy的:

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

    括号内就是游戏的进行过程了,我们退出大概就是两个: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 = 0
    step = 0
    mtr = init()  # init the matrix
    mtr_stk = []  # use step for back
    scr_stk = []

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

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

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

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

                                 mtr是我们的二维方阵,

                                 mtr_stk,scr_stk是储存栈,用于后退功能,假设不要后退,还是能够省略~

    算法实现:

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

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

     这是一个很easy的事情,跟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開始啦!

    初始化矩阵是很easy的,我们在矩阵中选两个不同位置,设置为2其它为0就可以。

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

    可是这样就能够啦:(看不惯?没关系,很easy理解,慢慢习惯吧!)

    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)最后一个啦!!!游戏的循环条件

    这个还是非常easy的。游戏进行,当前仅当:没有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 random
    import copy
    
    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 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 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
    
    
    def 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 score
    
    
    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
    
    # map 0 left,1 down,2 up ,3 right
    # a,h=> left ,s,j=>down, w,k=>up, d,l=>right
    declare = "←: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╮(╯▽╰)╭。。。


  • 相关阅读:
    揭晓UX(用户体验)最大的秘密
    Js、jquery学习笔记
    网站建设之高速WEB的实现
    网站改版之指标分析
    Nodejs读写流
    Nodejs查找,读写文件
    网站建设之脚本加载
    如何利用CSS3编写一个满屏的布局
    如何设计自己的UI套件
    用requireJS进行模块化的网站开发
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/3806890.html
Copyright © 2011-2022 走看看