最近看到有人用418B用C语言写出了2048,想试一下py语言的能力,遂用890B完成
这里只是我目前的极限,不多说进入正文,最简代码在最后
这里是效果图,也就是不断改变16个点的像素值,然后打印出来而已
关键在于怎么减少代码量
py语言不像C语言可以多行代码写到一行,py有着严格的缩进和分行要求
所以想要减小行数,一个可行的思路是写一行的字符串,用format去代替换行/tab,然后exec
想要减小内存,即减少文字,那必然变量名全是abcd,条件语句都是单行的if else,赋值能写到一行就一行
接下来还是略微进入游戏本体部分;忘记说了本次全程无import
可以看到36行代码,2048的思路是
假如方向是上:
从第二行开始,第2行往第1行移动(或相加)
然后是第三行,第3往第2行移动(或相加),在向第1行移动或相加
然后是第四行,第4行往第3行移动(或相加),向2,向1
假如方向是下,就是第3行,第2行,第1行即可
如果是左右,先对坐标系进行转置,横变纵,纵变横,变换后在转置回来即可
以上是主体部分,还有一些细节,例如,如果进行了移动,则从移动后还剩下的空坐标处随机生成2或者4。如果没移动是不给新生成的,可以思考如下
像这样的肯定不希望往上往右,因为左下角的大数字可能有危险回不去,但实际上2048为了增加难道(游戏体验)是不允许往左往下的,就是如果位置完全不变,那么也不会生成新的,这个功能两行代码已实现
另外方块合成,新合成的不能继续合成,必须是远的先合成,然后近的
比如一条线上4440往左划就是8400而非4800
4444往左划就是8800而非16 000。这个地方由于代码量没有添加,
分析代码,
这是前几行,第一个是range(4),很明显后面会使用很多次range(4),故替换
w是16个点 0
改变一个点,不然刷不出来
定义的这个d()函数是xorshift函数,就是生成随机数的,感兴趣的可以查查xorshift算法,我不懂所以直接抄的代码,W每次都是一个随机的很大的int(每次调用这个函数W随机,不同,但是W出现的顺序是相同的,也就是说你每次运行这个程序,W序列是固定的,所以不同的地方在于传入的参数,是允许生成点的列表,也就是现在16个点中为0的点)
这里是上面说的核心部分,o是转置坐标系,s是用来检测是否变化的,也就是上面提到的不允许生成新的,当下面有方块移动或者合成是s改为1,函数最后用s判定是否生成新点,如果整个坐标系中,经过了变换之后还是占满格子(其实就是既没合成也没转移而且满了,那就gameover了,实际上呢,要四个方向都gameover才是over了,你只用了单一一个方向,所以其实我压根没有成功/失败的判定,你自己玩玩着玩着发现不会变了就知道了)
n是目前的坐标系,上下就是w,左右就是转置。记住这行代码(n=w if r ……),这里用到了单行条件语句
x,y,z就是,x是上面提到的234行或者321行,y:如果上,那么在第2行要运行换1次,在第3行要两次,在第4行要3次,还有后面的-1,自己慢慢想把。反正这样就能把上下合并成一个for x了,左右也是
条件语句a-b:这种形式就是说a-b!=0,也就是a!=b,看看是不是少了一个字(哈哈,呜呜,难受,夹缝生存)
for j in range(4): 假如是上,那每一行有4个吧,p,q就是近的和远的数,远的为0,就把近的移过去,近的变成0,远的近的一样,就把两个加起来放到远的,近的为0
下面那个u=set()也是精华,说白了,找到16个点中的0。一般都是新建空列表,两层for 循环(坐标系本身是两层,w也是两层,然后条件判断一下),我这里十分精简了。
首先是列表里面的for循环,这个很基础了,单行的if else不规范,但是简洁。如果那个点是0就加上他的坐标,否则加上0, 这样新的列表就是位置个数0和一些坐标
怎么去掉0呢,又不知道个数又不知道是否存在,pop要索引,remove只删除一个,del更垃圾
所以先转为集合,集合用diacard(没有这个元素不报错),集合.remove没有该元素就报错了
然后转回来为列表,现在就3行代码得到了为0的点,也就是允许生成新点(2&4)的坐标,然后放入之前定义的随机函数里生出来,f很明显是0或1,也就是你生成的是2还是4
核心部分完了,最后加个while循环获取输入即可
小细节又来了,怎么打印呢用for循环?怎么处理空格呢,1位数2位数3位数4位数呢
这里有个tip很多地方可以用,("0"+str(i))[-2::] 也就是1变为01, 2变为02,10是10,43是43,就整齐了
所以这里转为4个字符长度,细节又来了,用join打印不仅更快而且省代码(比for循环打印)
最后,用上前面说的format即可
附上完整代码如下:
A,B="\n","\t"
C='b=(0,1,2,3){0}w=[[0 for i in b] for j in b]{0}w[2][2]=2{0}X,Y,Z,W=123456789,362436069,521288629,88675123{0}def d(r):{1}global X,Y,Z,W{1}t,X,Y,Z=X^(X<<11),Y,Z,W{1}W=W^(W>>19)^t^(t>>8){1}return r[W%len(r)],W%2{0}def e(r):{1}global w{1}o,s,=[[w[i][j] for i in b] for j in b],0{1}n=w if r in "ws" else o{1}x,y,z=([1,2,3],0,-1) if r in "aw" else ([2,1,0],3,1){1}for i in x:{2}while i-y:{3}for j in b:{4}p,q=n[i][j],n[i+z][j]{4}if p and (q==0 or q==p):{5}n[i+z][j]=p if q==0 else p+q{5}n[i][j]=0{5}s=1{3}i+=z{1}u=set([(x,y) if n[x][y]==0 else 0 for x in b for y in b]){1}u.discard(0){1}u,c=list(u),len(u){1}if c and s:{2}e,f = d(u){2}n[e[0]][e[1]] = 2*f+2{1}if r in "da":{2}w=[[n[i][j] for i in b] for j in b]'.format(*[A+D*B for D in range(6)])
exec(C)
while 1:
e(input("?"))
for i in w:
v=[(B+str(j))[-4::] for j in i]
print("".join(v),end=A+A)
在多说一点,格式化字符也有说法
1最开始是f“{}”型格式化,"\n"、"\t"都是4个字符,换成A,B就简化了
2有的缩进是5缩进,连续5个{B}
3上面的5个{B}改为{5*B}
4{A}{B}改为{A+B}
5发现还是format好用,前面的A+B,A+2*B,A+3*B直接用一个数字即可
6format可以用列表作为参数,加上*号即可
个人愚见,欢迎大家指正以及讨论
对了把上面的C打印出来就是源代码了,exec里不允许有while,所以下面自己添加,
还有直接打印出来的制表符格式不对,不是python标准缩进,总之会报错,自己改改缩进就行
(明显能看出来缩进很宽)而且很少呢,840B