高尔夫代码: 玩俄罗斯方块

基本知识:

考虑一下下面的河豚和空旷的场地:

0123456789
I   O    Z    T    L    S    J         [          ]
[          ]
#   ##   ##   ###  #     ##   #        [          ]
#   ##    ##   #   #    ##    #        [          ]
#                  ##        ##        [          ]
#                                      [          ]
[==========]

比赛场地的尺寸是固定的,上面的数字就在这里 指示列号(也参见输入)。

输入:

你被给予了一个特定的竞争环境(基于上述) ,这个环境已经被部分填满了 与 tetrominoes (这可以在一个单独的文件或通过 stdin 提供)。

输入样本:

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]

给你一个字符串来描述(用空格分隔)要插入哪个 tetromino (和 不需要旋转 Tetrominos。输入可以从 stdin 中读取。

输入样本:

T2 Z6 I0 T7

您可以假设输入是“格式良好的”(或者当它不是时产生未定义的行为)。

输出

呈现结果字段(“完整”行必须消失)并打印得分计数 (每条掉线占10分)。

根据上述样本输入的样本输出:

[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

冠军:

最短的解决方案(通过代码字符计数)。使用例子很好。有乐趣高尔夫球!

编辑 : 增加了 +500的声望来吸引更多的注意力到已经做出的努力(可能还有一些新的解决方案) ..。

7168 次浏览

Perl,586523483472427407404386387356353字符

(定义的或 //操作符需要 Perl 5.10)。

从 stdin 获取所有输入
注意,^ Q 表示 ASCII 17(DC1/XON) ,^ C 表示 ASCII 3,^@表示 ASCII 0(NUL)。

while(<>){push@A,[split//]if/]/;while(/\w/g){for$i(0..6){for($f=0,$j=4;$j--;){$c=0;map{if($_){$i--,$f=$j=3,redo if$A[$k=$i+$j][$C=$c+$'+1]ne$";$A[$k][$C]="#"if$f}$c++}split//,unpack"b*",chr vec"3^@'^@c^@^Q^C6^@\"^C^Q^Q",index(OTZLSJI,$&)*4+$j,4;$s+=10,@A[0..$k]=@A[$k,0..$k-1],map{s/#/ /}@{$A[0]},$i++if 9<grep/#/,@{$A[$k]}}last if$f}}}print+(map@$_,@A),$s//0,$/

评论版本:

while(<>){
# store the playfield as an AoA of chars
push@A,[split//]if/]/;
# while we're getting pieces
while(/\w/g){
# for each line of playfield
for$i(0..6){
# for each line of current piece
for($f=0,$j=4;$j--;){
# for each column of current piece
$c=0;
map{
if($_){
# if there's a collision, restart loop over piece lines
# with a mark set and playfield line decremented
$i--,$f=$j=3,redo if$A[$k=$i+$j][$C=$c+$'+1]ne$";
# if we already found a collision, draw piece
$A[$k][$C]="#"if$f
}
$c++
# pieces are stored as a bit vector, 16 bits (4x4) per piece,
# expand into array of 1's and 0's
}split//,unpack"b*",chr vec"3^@'^@c^@^Q^C6^@\"^C^Q^Q",index(OTZLSJI,$&)*4+$j,4;
# if this playfield line is full, remove it. Done by array slicing
# and substituting all "#"'s in line 0 with " "'s
$s+=10,@A[0..$k]=@A[$k,0..$k-1],map{s/#/ /}@{$A[0]},$i++if 9<grep/#/,@{$A[$k]}
}
# if we found a collision, stop iterating over the playfield and get next piece from input
last if$f
}
}
}
# print everything
print+(map@$_,@A),$s//0,$/

编辑1: 一些严重的高尔夫球,修复输出错误。
编辑2: 一些内嵌,合并成一个网节省(鼓滚...)3个字符,混合高尔夫球两个循环。
编辑3: 一些常见的子表达式消除,一点常量合并和调整正则表达式。
编辑4: 改变表示成一个打包位向量,混合高尔夫球。
编辑5: 更直接的翻译从 tetromino 字母到数组索引,使用非打印字符,混合高尔夫球。
编辑6: 修正错误清理顶行,在 r3(编辑2)中引入,由 Nakilon 发现。使用更多不可打印的字符。
编辑7: 使用 vec获取 tetromino 数据。利用游戏场地有固定尺寸的事实。if语句 = > if修饰符,编辑2的循环合并开始产生效果。在0分的情况下使用 //
编辑8: 修复了另一个 bug,在 r6中引入(编辑5) ,由 Nakilon 发现。
编辑9: 在清除行时不要创建新的引用,只是通过数组切片移动引用。把两个 map合并成一个。智能正则表达式。“更聪明”for。Misc 高尔夫球场。
编辑10: 内联 tetromino 数组,添加注释版本。

Python: 504519字符

(Python3解决方案) 当前需要按照顶部所示的格式设置输入(输入代码不计算在内)。稍后我将展开从 file 或 stdin 读取。 Now 使用提示符,只需粘贴输入即可(总共8行)。

R=range
f,p=[input()[1:11]for i in R(7)],p
for(a,b)in input().split():
t=[' '*int(b)+r+' '*9for r in{'I':'#,#,#,#','O':'##,##','Z':'##, ##','T':'###, # ','L':'#,#,##','S':' ##,##','J':' #, #,##'}[a].split(',')]
for r in R(6-len(t),0,-1):
for i in R(len(t)):
if any(a==b=='#'for(a,b)in zip(t[i],f[r+i])):break
else:
for i in R(0,len(t)):
f[r+i]=''.join(a if b!='#'else b for(a,b)in zip(t[i],f[r+i]))
if f[r+i]=='#'*10:del f[r+i];f[0:0]=[' '*10];p+=10
break
print('\n'.join('['+r+']'for r in f[:7]),p,sep='\n')

不知道我能不能在那里省下更多。在转换到位字段时会丢失相当多的字符,但这比使用字符串节省了更多的字符。另外,我不确定是否可以删除更多的空格,但我会尝试以后。
Won't be able to reduce it much more; after having the bitfield-based solution, I transitioned back to strings, as I found a way to compress it more (saved 8 characters over the bitfield!). But given that I forgot to include the L and had an error with the points inside, my character count only goes up 叹气... Maybe I find something later to compress it a bit more, but I think I'm near the end. For the original and commented code see below:

原版:

field = [ input()[1:11] for i in range(7) ] + [ 0, input() ]
# harcoded tetrominoes
tetrominoes = {'I':('#','#','#','#'),'O':('##','##'),'Z':('##',' ##'),'T':('###',' # '),'L':('#','#','##'),'S':(' ##','##'),'J':(' #',' #','##')}
for ( f, c ) in field[8].split():
# shift tetromino to the correct column
tetromino = [ ' ' * int(c) + r + ' ' * 9 for r in tetrominoes[f] ]


# find the correct row to insert
for r in range( 6 - len( tetromino ), 0, -1 ):
for i in range( len( tetromino ) ):
if any( a == b == '#' for (a,b) in zip( tetromino[i], field[r+i] ) ):
# skip the row if some pieces overlap
break
else:
# didn't break, insert the tetromino
for i in range( 0, len( tetromino ) ):
# merge the tetromino with the field
field[r+i] = ''.join( a if b != '#' else b for (a,b) in zip( tetromino[i], field[r+i] ) )


# check for completely filled rows
if field[r+i] == '#' * 10:
# remove current row
del field[r+i]
# add new row
field[0:0] = [' '*10]
field[7] += 10
# we found the row, so abort here
break
# print it in the requested format
print( '\n'.join( '[' + r + ']' for r in field[:7] ) )
# and add the points = 10 * the number of redundant lines at the end
print( str( field[7] ) )

红宝石ー 427408398369359

t=[*$<]
o=0
u=->f{f.transpose}
a=u[t.reverse.join.scan /#{'( |#)'*10}/]
t.pop.split.map{|w|m=(g='I4O22Z0121T01201L31S1201J13'[/#{w[0]}\d+/].scan(/0?\d/).zip a.drop w[1].to_i).map{|r,b|(b.rindex ?#or-1)-r.size+1}.max
g.map{|r,b|b.fill ?#,m+r.size,r.to_i}
v=u[a]
v.reject!{|i|i-[?#]==[]&&(o+=10;v)<<[' ']*10}
a=u[v]}
puts u[a].reverse.map{|i|?[+i*''+?]},t[-1],o

C,727[ ... ]596581556517496471461457字符

这是我的第一个代码高尔夫,我认为字符计数可以得到 很多较低,如果有经验的高尔夫球手可以给我一些提示会很好。

当前版本也可以处理不同尺寸的游戏场。输入可以有 DOS/Windows 和 Unix 格式的换行符。

优化之前的代码非常简单,四角形以4个整数形式存储,解释为一个(7 * 3) x4位数组,游戏场地按原样存储,瓷砖被丢弃,完整的行在开始和每个瓷砖丢弃之后被删除。

我不确定如何计算字符数,所以我使用了代码的文件大小,删除了所有不必要的换行符。

编辑596 = > 581: 感谢 KitsuneYMG,除了 %ls建议之外的一切都很完美,另外,我注意到可以使用 putch而不是 putchar(getch不知何故不起作用) ,并删除了 #define G中的所有括号。

编辑581 = > 556: 对剩余的 for和嵌套的 F循环不满意,所以对循环进行了一些合并、更改和删除,这很令人困惑,但绝对值得。

编辑556 = > 517: 终于找到了一种使 a成为整型数组的方法。一些 N;c合并,不再有 break

编辑496 = > 471: 场地宽度 还有高度现在固定。

编辑471 = > 461: 微小的修改,putchar作为 putch再次使用是没有标准的功能。

编辑: 修正,完整的线被删除 之前瓦下降而不是 之后,所以完整的线可以留在最后。修复不会改变字符数。

#define N (c=getchar())
#define G T[j%4]&1<<t*3+j/4
#define X j%4*w+x+j/4
#define F(x,m) for(x=0;x<m;x++)
#define W while
T[]={916561,992849,217,1},C[99],c,i,j,s,t,x,A,a[99],w=13;
main(){F(j,7)C["IJLSTZO"[j]]=j;
F(j,91)a[j]=N;
W(N>w){t=C[c];x=N-86;
W(c){F(j,12)if(G&&X>1?a[X]-32:0)c=0;
F(j,12)if(G&&X>w&&!c)a[X-w]=35;x+=w;}N;
F(i,6){A=0;t=i*w;F(x,w)A|=(a[t+x]==32);
if(!A){s++;F(j,t)a[t+w-j]=a[t-j];
x=1;W(a[x]-93)a[x++]=32;}}}
F(i,91)putchar(a[i]);printf("%i0",s);}

Golfscript 260字符

我相信这个可以改进,我对 Golfscript 还是个新手。

[39 26.2/0:$14{.(}:?~1?15?1?14 2??27?13.!14?2?27?14 1]4/:t;n/)\n*:|;' '/-1%.,:c;~{)18+:&;'XIOZTLSJX'\%~;,1-t\=\{\{.&+.90>{;.}*|\=32=!{&13-:&;}*}%}6*{&+}/|{\.@<'#'+\)|>+}4*{'['\10*']'++}:
;n/0\~n+:|;0\{.'#'
={;)}{n+|+:|;}if\.}do;' '
n+\.@*|+\$+:$;.,1-<:|;}c*|n?$*

行尾是相关的(行尾不应该有一个)。不管怎样,下面是我使用的一些测试案例:

> cat init.txt
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7> cat init.txt | ruby golfscript.rb tetris.gsc
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10


> cat init.txt
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ##### ]
[==========]
I0 O7 Z1 S4> cat init.txt | ruby golfscript.rb tetris.gsc
[          ]
[          ]
[          ]
[#         ]
[###  #### ]
[### ##### ]
[==========]
10


> cat init.txt
[          ]
[          ]
[          ]
[ ## ###   ]
[ #    #   ]
[ ## ######]
[==========]
T7 I0 I3> cat init.txt | ruby golfscript.rb tetris.gsc
[          ]
[          ]
[          ]
[          ]
[#  #      ]
[## #  # # ]
[==========]
20

注意,输入文件中没有行尾,行尾将按原样中断脚本。

O’Caml 809782 Chars

open String let w=length let c s=let x=ref 0in iter(fun k->if k='#'then incr x)s;!x open List let(@),g,s,p,q=nth,ref[],ref 0,(0,1),(0,2)let l=length let u=Printf.printf let rec o x i j=let a=map(fun s->copy s)!g in if snd(fold_left(fun(r,k)(p,l)->let z=c(a@r)in blit(make l '#')0(a@r)(i+p)l;if c(a@r)=z+l then r+1,k else r,false)(j-l x+1,true)x)then g:=a else o x i(j-1)and f x=let s=read_line()in if s.[1]='='then g:=rev x else f(sub s 1 10::x)let z=f [];read_line();;for i=0to w z/3 do o(assoc z.[i*3]['I',[p;p;p;p];'O',[q;q];'Z',[q;1,2];'T',[0,3;1,1];'L',[p;p;q];'S',[1,2;q];'J',[1,1;1,1;q]])(Char.code z.[i*3+1]-48)(l!g-1);let h=l!g in g:=filter(fun s->c s<>w s)!g;for i=1to h-(l!g)do incr s;g:=make 10' '::!g done;done;iter(fun r->u"[%s]\n"r)!g;u"[==========]\n";u"%d\n"(!s*10)

Ruby 中的另一个,573546字符

: * *

Z={I:?#*4,J:'#,###',L:'###,#',O:'##,##',S:'#,##, #',Z:' #,##,#',T:' #,##, #'}
t=[*$<]
R=->s{s.reverse}
T=->m{m.transpose}
a = T[R[t].join.scan /.#{'(\D)'*10}.$/]
t.pop.split.each{|z|
t,o=Z[z[0].to_sym].split(',').map{|x|x.split //},z[1].to_i
r=0..t.size-1
y=r.map{|u|1+a[o+u].rindex(?#).to_i-t[u].count(' ')}.max
(0..3).each{|i|r.each{|j|t[j][i]==?#&&a[o+j][y+i]=t[j][i]}}}
s=0
a.each{|x|s=a.max_by(&:size).size;x[s-=1]||=' 'while s>0}
a=R[T[a].reject{|x|x*''=~/[#]{10}/&&s+=10}.map{|x|?[+x*''+?]}[0..6]]
puts (0..8-a.size).map{?[+' '*10+?]},a,s

测试:

cat test.txt | ruby 3858384_tetris.rb
[          ]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

GolfScript-181个字符

不需要换行。输出在标准输出中,尽管在 stderr 中存在一些错误。
\10应该被相应的 ASCII 字符所替代,因为程序应该是181个字符。

{):X!-{2B{" #"=}%X" ":f*+-1%}%:P;:>.{\!:F;>P{\(@{3&\(@.2$&F|:F;|}%\+}%\+F![f]P+:P
;}do;{"= "&},.,7^.R+:R;[>0="#"/f*]*\+}0"R@1(XBc_""~\10"{base}:B/3/~4*"nIOZTLSJR "
";:"*~;n%)n*~ 10R*+n*

输入/输出示例:

$ cat inp
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7
$ cat inp|golfscript tetris.gs 2>/dev/null
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

Tetromino 压缩:
零件存储为三个基数为8的数字。这是一个简单的二进制表示,例如 T=[7,2,0], S=[6,3,0], J=[2,2,3][1]用于压缩中的 I片段,但是稍后显式地将其设置为 [1,1,1,1](即代码中的 4*)。所有这些数组被连接成一个单独的数组,该数组被转换成一个整数,然后是一个字符串(以126为基数,以最小化不可打印字符、长度和不遇到 utf8)。这个字符串非常短: "R@1(XBc_"

那么减压就很简单了。我们首先执行基数126转换,然后执行基数8转换("~\10"{base}/,即迭代 "~\10"0并为每个元素执行基数转换)。生成的数组被分成3组,I的数组是固定的("~\10"1)。然后我们将每个元素转换为基数2,并且(在去掉零之后)用字符串 " #"中索引的字符替换每个二进制数字("~\10"2-注意,我们需要反转数组,否则 2将变成 "# "而不是 " #")。

纸板/纸片格式,掉落的纸片
黑板只是一个字符串数组,每行一个字符串。最初没有在这上面做任何工作,所以我们可以在输入上用 n/(生成它。片段也是字符串的数组,在左侧为其 X 位置填充了空格,但没有尾随空格。通过前置到数组并持续测试是否存在冲突,可以丢弃各个部分。

碰撞测试是通过对所有字符进行迭代,并与板上相同位置的字符进行比较来完成的。我们想把 # + =# + #看作是碰撞,所以我们测试((piecchar & 3) & boardchar)是否为非零。在进行这个迭代的同时,我们还使用((pickechar & 3) | boardchar)更新(一个副本)这个板,它正确地设置了对 # + + # + [的值。我们使用这个更新的董事会,如果有一个冲突后移动的一块下来另一行。

删除填充行非常简单。我们删除 "= "&返回 false 的所有行。一个已填充的行既没有 =也没有 ,因此连词将是一个空字符串,这等同于 false。然后我们计算已经删除的行数,将计数添加到分数中,并预先设置许多 "[ ... ]"。我们通过获取网格的第一行并用 替换 #来简洁地生成它。

意外收获
因为我们计算了棋子落下时,棋盘在棋子每个位置的样子,所以我们可以将它们保留在堆栈上而不是删除它们!对于总共多三个字符,我们可以输出所有这些位置(或两个字符,如果我们有板状态单间距)。

{):X!-{2B{" #"=}%X" ":f*+-1%}%:P;:>.{>[f]P+:P(!:F;{\(@{3&\(@.2$&F|:F;|}%\+}%\+F!}
do;{"= "&},.,7^.R+:R;[>0="#"/f*]*\+}0"R@1(XBc_""~\10"{base}:B/3/~4*"nIOZTLSJR "
";:"*~;n%)n*~ ]{n*n.}/10R*

Ruby 505479474442439426字符

第一次尝试。已经用 IronRuby 做到了。我确信它可以改进,但是我今天真的应该完成一些工作!

p,q,r,s=(0..9),(0..2),(0..6),0
t=[*$<]
f=p.map{|a|g=0;r.map{|b|g+=2**b if t[6-b][a+1]==?#};g}
t.pop.split.map{|x|w,y=[15,51,306,562,23,561,113]["IOZTLSJ"=~/#{x[0]}/],x[1].to_i
l=q.map{|d|r.inject{|b,c|f[d+y]&(w>>(d*4)&15-c+1)>0?c:b}}.max
q.map{|b|f[b+y]|=w>>(b*4)&15-l}
r.map{i=f.inject{|a,b|a&b};f.map!{|a|b=i^(i-1);a=((a&~b)>>1)+(a&(b>>1))};s+=i>0?10:0}}
p.map{|a|r.map{|b|t[6-b][a+1]=f[a]&2**b>0??#:' '}}
puts t,s

测试

cat test.txt | ruby tetris.rb
[          ]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

剪辑 现在使用普通的红宝石。得到的壁输出. 。

Common Lisp 667657645字符

我第一次尝试代码高尔夫,所以可能有很多技巧,我还不知道。我在那里保留了一些换行符,以保持一些剩余的“可读性”(我将换行符计算为2个字节,因此删除6个不必要的换行符将增加12个字符)。

在输入中,首先放置形状,然后放置字段。

(let(b(s 0)m(e'(0 1 2 3 4 5 6 7 8 9)))
(labels((o(p i)(mapcar(lambda(j)(+ i j))p))(w(p r)(o p(* 13 r)))(f(i)(find i b))
(a(&aux(i(position(read-char)"IOZTLSJ")))(when i(push(o(nth i'((0 13 26 39)(0 1 13 14)(0 1 14 15)(0 1 2 14)(0 13 26 27)(1 2 13 14)(1 14 26 27)))(read))m)(a))))
(a)(dotimes(i 90)(if(find(read-char)"#=")(push i b)))(dolist(p(reverse m))
(setf b`(,@b,@(w p(1-(position-if(lambda(i)(some #'f(w p i)))e)))))
(dotimes(i 6)(when(every #'f(w e i))(setf s(1+ s)b(mapcar(lambda(k)(+(if(>(* 13 i)k)13(if(<=(* 13(1+ i))k)0 78))k))b)))))
(dotimes(i 6)(format t"[~{~:[ ~;#~]~}]
"(mapcar #'f(w e i))))(format t"[==========]
~a0"s)))

测试

T2 Z6 I0 T7
[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10
NIL

Ruby 1.9,357 355 353 339 330 310309字符

d=0
e=[*$<]
e.pop.split.map{|f|f="L\003\003\007J\005\005\007O\007\007Z\007\013S\013\007I\003\003\003\003T\017\005"[/#{f[j=0]}(\W*)/,1].bytes.map{|z|?\0+?\0*f[1].hex+z.to_s(2).tr("01"," #")[1,9]}
k,f,i=i,[p]+f,e.zip(f).map{|l,m|l.bytes.zip(m.to_s.bytes).map{|n,o|j|=n&3&q=o||0;(n|q).chr}*""}until j>0
e=[]
e+=k.reject{|r|r.sum==544&&e<<r.tr(?#,?\s)&&d+=10}}
puts e,d

请注意,\000转义符(包括第三行上的空字节)应替换为它们实际的不可打印等价物。

输入样本:

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7

用法:

ruby1.9 tetris.rb < input

或者

ruby1.9 tetris.rb input

Bash shell 脚本(301304字符)


更新: 修正了一个错误,涉及到部分扩展到顶行。此外,输出现在被发送到标准输出,作为奖励,可以再次运行脚本继续玩游戏(在这种情况下,你必须自己加总分数)。

这包括不可打印的字符,所以我提供了一个十六进制转储。保存为 tetris.txt:

0000000: 7461 696c 202d 3120 245f 7c7a 6361 743e  tail -1 $_|zcat>
0000010: 753b 2e20 750a 1f8b 0800 35b0 b34c 0203  u;. u.....5..L..
0000020: 5590 516b 8330 10c7 dff3 296e 4c88 ae64  U.Qk.0....)nL..d
0000030: a863 0c4a f57d 63b0 07f7 b452 88d1 b4da  .c.J.}c....R....
0000040: 1a5d 5369 91a6 df7d 899a d05d 5e72 bfbb  .]Si...}...]^r..
0000050: fbff 2fe1 45d5 0196 7cff 6cce f272 7c10  ../.E...|.l..r|.
0000060: 387d 477c c4b1 e695 855f 77d0 b29f 99bd  8}G|....._w.....
0000070: 98c6 c8d2 ef99 8eaa b1a5 9f33 6d8c 40ec  ...........3m.@.
0000080: 6433 8bc7 eeca b57f a06d 27a1 4765 07e6  d3.......m'.Ge..
0000090: 3240 dd02 3df1 2344 f04a 0d1d c748 0bde  2@..=.#D.J...H..
00000a0: 75b8 ed0f 9eef 7bd7 7e19 dd16 5110 34aa  u.....{.~...Q.4.
00000b0: c87b 2060 48a8 993a d7c0 d210 ed24 ff85  .{ `H..:.....$..
00000c0: c405 8834 548a 499e 1fd0 1a68 2f81 1425  ...4T.I....h/..%
00000d0: e047 bc62 ea52 e884 42f2 0f0b 8b37 764c  .G.b.R..B....7vL
00000e0: 17f9 544a 5bbd 54cb 9171 6e53 3679 91b3  ..TJ[.T..qnS6y..
00000f0: 2eba c07a 0981 f4a6 d922 89c2 279f 1ab5  ...z....."..'...
0000100: 0656 c028 7177 4183 2040 033f 015e 838b  .V.(qwA. @.?.^..
0000110: 0d56 15cf 4b20 6ff3 d384 eaf3 bad1 b9b6  .V..K o.........
0000120: 72be 6cfa 4b2f fb03 45fc cd51 d601 0000  r.l.K/..E..Q....

然后,在 bash 命令提示符下,最好将 elvis而不是 vim安装为 vi:

$ xxd -r tetris.txt tetris.sh
$ chmod +x tetris.sh
$ cat << EOF > b
> [          ]
> [          ]
> [          ]
> [          ]
> [ #    #  #]
> [ ## ######]
> [==========]
> EOF
$ ./tetris.sh T2 Z6 I0 T7 2>/dev/null
-- removed stuff that is not in standard out --
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

它是如何工作的

代码自我解压的方式类似于使用 gzexe脚本压缩的可执行程序。Tetromino 片段表示为 vi 编辑器命令序列。字符计数用于检测冲突,行计数用于计算分数。

解压代码:

echo 'rej.j.j.:wq!m'>I
echo '2rejh.:wq!m'>O
echo '2rej.:wq!m'>Z
echo '3rejh1.:wq!m'>T
echo 'rej.j2.:wq!m'>L
echo 'l2rej2h.:wq!m'>S
echo 'lrej.jh2.:wq!m'>J
for t
do for y in `seq 1 5`
do echo -n ${y}jk$((${t:1}+1))l|cat - ${t:0:1}|vi b>0
grep ========== m>0||break
[ `tr -cd '#'<b|wc -c` = `tr -cd '#'<m|wc -c` ]||break
tr e '#'<m>n
done
cat n>b
grep -v '##########' b>m
$((S+=10*(`wc -l < b`-`wc -l < m`)))
yes '[          ]'|head -7|cat - m|tail -7>b
done
cat b
echo $S

打高尔夫球之前的原始规则:

#!/bin/bash


mkpieces() {
pieces=('r@j.j.j.' '2r@jh.' '2r@j.' '3r@jh1.' 'r@j.j2.' 'l2r@j2h.' 'lr@j.jh2.')
letters=(I O Z T L S J)


for j in `seq 0 9`; do
for i in `seq 0 6`; do
echo "jk$(($j+1))l${pieces[$i]}:wq! temp" > ${letters[$i]}$j
done
done
}


counthashes() {
tr -cd '#' < $1 | wc -c
}


droppiece() {
for y in `seq 1 5`; do
echo -n $y | cat - $1 | vi board > /dev/null
egrep '={10}' temp > /dev/null || break
[ `counthashes board` -eq `counthashes temp` ] || break
tr @ "#" < temp > newboard
done
cp newboard board
}


removelines() {
egrep -v '#{10}' board > temp
SCORE=$(($SCORE + 10 * (`wc -l < board` - `wc -l < temp`)))
yes '[          ]' | head -7 | cat - temp | tail -7 > board
}


SCORE=0
mkpieces
for piece; do
droppiece $piece
removelines
done
cat board
echo $SCORE

Python 2.6 +-334 322316个字符

解压 368366字符

#coding:l1
exec'xÚEPMO!½ï¯ i,P*Ýlš%ì­‰=‰Ö–*†­þz©‰:‡—Lò¾fÜ”bžAù,MVi™.ÐlǃwÁ„eQL&•uÏÔ‹¿1O6ǘ.€LSLÓ’¼›î”3òšL¸tŠv[ѵl»h;ÁºŽñÝ0Àë»Ç‡ÛûH.ª€¼âBNjr}¹„V5¾3Dë@¼¡•gO. ¾ô6 çÊsÃЮürÃ1&›ßVˆ­ùZ`Ü€ÿžcx±ˆ‹sCàŽ êüRô{U¯ZÕDüE+³ŽFA÷{CjùYö„÷¦¯Î[0þøõ…(Îd®_›â»E#–Y%’›”ëýÒ·X‹d¼.ß9‡kD'.decode('zip')

单行换行符是必需的,并且我已经将其计算为一个字符。

浏览器代码页胡言乱语可能会阻止成功地复制和粘贴这段代码,因此您可以选择从这段代码生成文件:

s = """
23 63 6F 64 69 6E 67 3A 6C 31 0A 65 78 65 63 27 78 DA 45 50 4D 4F 03 21
10 BD EF AF 20 69 2C 50 2A 02 DD 6C 9A 25 EC AD 07 8D 89 07 3D 89 1C D6
96 2A 86 05 02 1B AD FE 7A A9 89 3A 87 97 4C F2 BE 66 DC 94 62 9E 41 F9
2C 4D 56 15 69 99 0F 2E D0 6C C7 83 77 C1 16 84 65 51 4C 26 95 75 CF 8D
1C 15 D4 8B BF 31 4F 01 36 C7 98 81 07 2E 80 4C 53 4C 08 D3 92 BC 9B 11
EE 1B 10 94 0B 33 F2 9A 1B 4C B8 74 8A 9D 76 5B D1 B5 6C BB 13 9D 68 3B
C1 BA 8E F1 DD 30 C0 EB BB C7 87 DB FB 1B 48 8F 2E 1C AA 80 19 BC E2 42
4E 6A 72 01 7D B9 84 56 35 BE 33 44 8F 06 EB 40 BC A1 95 67 4F 08 2E 20
BE F4 36 A0 E7 CA 73 C3 D0 AE FC 72 C3 31 26 9B DF 56 88 AD F9 5A 60 DC
80 FF 9E 63 78 B1 88 8B 73 43 E0 8E A0 EA FC 52 F4 7B 55 8D AF 5A 19 D5
44 FC 45 2B B3 8E 46 9D 41 F7 7B 43 6A 12 F9 59 F6 84 F7 A6 01 1F AF CE
5B 30 FE F8 F5 85 28 CE 64 AE 5F 9B E2 BB 45 23 96 59 25 92 9B 94 EB FD
10 D2 B7 58 8B 64 BC 2E DF 39 87 6B 44 27 2E 64 65 63 6F 64 65 28 27 7A
69 70 27 29
"""


with open('golftris.py', 'wb') as f:
f.write(''.join(chr(int(i, 16)) for i in s.split()))

测试

Intertis

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7

换行符必须是 Unix 风格的(只能是 linefeed)。最后一行的尾部换行符是可选的。

测试:

> python golftris.py < intetris
[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

这段代码解压 原创的代码,并用 exec执行它。这段解压缩后的代码有366个字符,看起来像这样:

import sys
r=sys.stdin.readlines();s=0;p=r[:1];a='[##########]\n'
for l in r.pop().split():
n=int(l[1])+1;i=0xE826408E26246206601E>>'IOZTLSJ'.find(l[0])*12;m=min(zip(*r[:6]+[a])[n+l].index('#')-len(bin(i>>4*l&31))+3for l in(0,1,2))
for l in range(12):
if i>>l&2:c=n+l/4;o=m+l%4;r[o]=r[o][:c]+'#'+r[o][c+1:]
while a in r:s+=10;r.remove(a);r=p+r
print''.join(r),s

换行符是必需的,每行一个字符。

不要尝试读取这个代码。字面上,变量名是随机选择的,以搜索最高的压缩(使用不同的变量名,压缩后我看到多达342个字符)。更容易理解的版本如下:

import sys


board = sys.stdin.readlines()
score = 0
blank = board[:1] # notice that I rely on the first line being blank
full  = '[##########]\n'


for piece in board.pop().split():
column = int(piece[1]) + 1 # "+ 1" to skip the '[' at the start of the line


# explanation of these three lines after the code
bits = 0xE826408E26246206601E >> 'IOZTLSJ'.find(piece[0]) * 12
drop = min(zip(*board[:6]+[full])[column + x].index('#') -
len(bin(bits >> 4 * x & 31)) + 3 for x in (0, 1, 2))


for i in range(12):
if bits >> i & 2: # if the current cell should be a '#'
x = column + i / 4
y = drop + i % 4
board[y] = board[y][:x] + '#' + board[y][x + 1:]


while full in board:      # if there is a full line,
score += 10           # score it,
board.remove(full)    # remove it,
board = blank + board # and replace it with a blank line at top
        

print ''.join(board), score

关键在于我说过要解释的那三条神秘的线。

四米诺的形状是以十六进制编码的。每个 tetronimo 被认为占据一个3x4的单元格网格,其中每个单元格要么是空的(一个空格) ,要么是满的(一个数字符号)。然后,每一块用3个十六进制数字进行编码,每个数字描述一个4单元格列。最小有效数字描述最左边的列,每个数字中最小有效位描述每列中最上面的单元格。如果一个位为0,则该单元格为空,否则为’#’。例如, tetronimo 被编码为 00F,最左边一列中最低有效数字的四位被设置为编码四个数字符号,而 T131,最上面的位被设置在左边和右边,最上面的两位被设置在中间。

然后将整个十六进制数向左移动一位(乘以二)。这样我们就可以忽略最底层的部分。我一会儿再解释原因。

因此,给定输入的当前片段,我们找到这个十六进制数的索引,其中描述它的形状的12位开始,然后向下移动,以便 bits变量的1-12位(跳过位0)描述当前片段。

drop的赋值决定了在落到其他碎片上之前,碎片将从网格顶部落下多少行。第一行查找游戏区域的每一列顶部有多少个空单元格,而第二行查找每一列中被占用的最低单元格。zip函数返回一个元组列表,其中每个元组由输入列表中每个项的 N这个单元格组成。因此,使用示例输入板,zip(board[:6] + [full])将返回:

[
('[', '[', '[', '[', '[', '[', '['),
(' ', ' ', ' ', ' ', ' ', ' ', '#'),
(' ', ' ', ' ', ' ', '#', '#', '#'),
(' ', ' ', ' ', ' ', ' ', '#', '#'),
(' ', ' ', ' ', ' ', ' ', ' ', '#'),
(' ', ' ', ' ', ' ', ' ', '#', '#'),
(' ', ' ', ' ', ' ', ' ', '#', '#'),
(' ', ' ', ' ', ' ', '#', '#', '#'),
(' ', ' ', ' ', ' ', ' ', '#', '#'),
(' ', ' ', ' ', ' ', ' ', '#', '#'),
(' ', ' ', ' ', ' ', '#', '#', '#'),
(']', ']', ']', ']', ']', ']', ']')
]

我们从这个列表中选择对应于相应列的元组,并在列中找到第一个 '#'的索引。这就是为什么我们在调用 zip之前附加了一个“完整”行,这样当列为空时,index将有一个合理的返回(而不是抛出一个异常)。

然后,为了在每一列中找到最低的 '#',我们移动并掩盖描述该列的4位,然后使用 bin函数将其转换为一个由1和0组成的字符串。bin函数只返回有效位,所以我们只需要计算这个字符串的长度就可以找到最低占用单元(最有效的集合位)。bin函数也在 '0b'之前,所以我们必须减去它。我们也忽略最不重要的部分。这就是为什么十六进制数要向左偏移一位。这是为了解决空列的问题,空列的字符串表示形式与只有顶部单元格满的列具有相同的长度(例如 T片段)。

例如,如前所述,01 tetromino 的列是 F00bin(0xF)'0b1111'。在忽略 '0b'之后,我们得到了一个4的长度,这是正确的。但是 bin(0x0)就是 0b0。在忽略 '0b'之后,我们仍然有一个’1的长度,这是不正确的。为了解决这个问题,我们在结尾添加了一个额外的位,这样我们就可以忽略这个不重要的位。因此,代码中的 +3是为了解释 '0b'在开始时占用的额外长度,以及在结束时占用的无关紧要的位。

所有这些都发生在三列的生成器表达式((0,1,2))中,我们使用 min结果来查找块在触及三列中的任何一列之前可以删除的最大行数。

通过阅读代码,其余部分应该很容易理解,但是这些作业后面的 for循环将把这一部分添加到黑板上。在此之后,while循环删除完整的行,在顶部用空行替换它们,并记录分数。最后,黑板和分数被打印到输出。

Python 298个字符

胜过目前所有非 深奥的语言解决方案(Perl、 Ruby、 C、 bash...)


甚至没有使用密码欺骗。

import os
r=os.read
b='[%11c\n'%']'*99+r(0,91)
for k,v in r(0,99).split():
t=map(ord,' -:G!.:; -:; !-.!"-. !". !./')['IJLOSTZ'.find(k)*4:][:4];v=int(v)-31
while'!'>max(b[v+j+13]for j in t):v+=13
for j in t:b=b[:v+j]+'#'+b[v+j+1:]
b=b.replace('[##########]\n','')
print b[-91:],1060-10*len(b)/13

在测试例子上

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
T2 Z6 I0 T7

它输出

[          ]
[          ]
[          ]
[#      ###]
[#     ### ]
[##### ####]
[==========]
10

修正了 Nakilon 指出的一个错误,成本 + 5