给定俄罗斯方块动作列表,返回完成的行数


37

描述

我们考虑一个稍微简化的俄罗斯方块版本,其中的每一步包括:

  • 顺时针旋转0到3次
  • 将作品定位在给定的列
  • 快速下降

目标是确定已完成的行数,并给出此类俄罗斯方块动作的列表。

遵循标准的俄罗斯方块规则,完成的行将随着片段的删除而删除。

游乐场

运动场是10列宽。没有游戏结束,并且假定无论赛场的配置如何,总是有足够的空间和时间来执行上述操作。运动场的高度在这里并不重要,但是您可以使用标准的22行作为上限。

Tetrominoes的形状

形状

输入输出

输入值

以逗号分隔的俄罗斯方块移动列表,其中包含3个字符的编码。前两个字符描述要使用的Tetromino形状,最后一个字符描述其放置位置。

  1. 四格拼板:IOTLJZS,在与上述顺序相同。
  2. 顺时针旋转数:03
  3. 列:09x在旋转1之后,这是作品左上角(在上面的图片中标记为)所在的列

假定提供的列表中的所有移动均有效。无需检查无效条目,例如I07(水平I形状在右侧放置太远)。

1 您可以自由执行真正的旋转算法,也可以对所有不同的形状进行硬编码,只要它们x位于移动的第三个字符所指定的列中即可。

输出量

已完成的行数。

例

O00,T24将生成第一个位置,O00,T24,S02,T01,L00,Z03,O07,L06,I05并生成第二个位置。

因此,以下序列将生成一个俄罗斯方块并应返回4

O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19

测试用例

1) "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" -> 4
2) "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" -> 5
3) "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" -> 4
4) "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,
    I10,I10" -> 8
5) "O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02" -> 8

测试页

您可以使用此JSFiddle测试移动列表。


1
零件绕什么轴旋转?

1
@Arnauld我建议您看一下超级旋转系统,然后将图像编辑成小块。tetris.wikia.com/wiki/SRS

1
因此,我们可以将它们视为25种形状(如果不计算重复数,则为15种),然后呢?

1
解决方案可以将输入作为数组而不是逗号分隔的字符串吗?
约旦

1
这是我很长时间以来见过的最好的PCG问题。真是个好主意!在主观意义上最好是有趣和实用,不要太大也不要太小。
GreenAsJade

Answers:


5

PHP,405 399 378 372 368 360 354 347 331 330 328个 319 309 300字节

(使用Dave的块映射

<?$f=[~0];L:for($y=0;$f[++$d+$y]|$s;$s>>=4)$y-=1022<$f[$d+$y]=$f[$d]|$s%16<<$x;$c-=$y;if(list($p,$r,$x)=$argv[++$z])for($s=I==$p?$r&1?4369:15:hexdec(decoct(O==$p?27:ord(base64_decode('M1ozWjqaF1kemR6ZPJMPyTnSJ0s')[ord($p)/4%5*4+$r])));;$d--)for($y=4;$y--;)if ($f[$d+$y]>>$x&$s>>$y*4&15)goto L;echo$c;

程序,将移动作为单独的参数,打印结果

功能分解:

将移动作为数组,返回结果

function t($a)
{
    $f=[~$z=0];             // init field $f (no need to init $z in golfed version)
    L:                      // jump label
                            // A: paint previous piece at line $d+1:
#   if($s)paint($f,$s,$d+1,$x);
    for($y=0;               // $y = working line offset and temporary score
        $f[++$d-$y]|$s;$s>>=4)// next field line; while field or piece have pixels ...
        $s>>=4)                 // next piece line
        $y+=1022<               // 3: decrease temp counter if updated line is full
            $f[$d-$y]=$f[$d]    // 2: use $y to copy over dropped lines
                |$s%16<<$x;     // 1: set pixels in working line
    $c+=$y;                         // add temp score to global score
#   paint($f);var_dump($c);
    if(list($p,$r,$x)=$a[$z++])// B: next piece: $p=name; $r=rotation, $x=column
#   {echo"<b>$z:$p$r-$x</b><br>";
        for(                // C: create base 16 value:
            $s=I==$p
                ? $r&1?4369:15  // I shape (rotated:0x1111, not rotated:0xf)
                : hexdec(decoct(    // 5: convert from base 8 to base 16
                    O==$p ? 27  // O shape (rotation irrelevant: 033)
                    : ord(          // 4: cast char to byte
                                    // 0: 4 bytes for each remaining tetromino
                        base64_decode('M1ozWjqaF1kemR6ZPJMPyTnSJ0s')[
                            ord($p)/4%5 // 1: map STZJL to 01234
                            *4      // 2: tetromino offset
                            +$r]    // 3: rotation offset
            )));;$d--
        )
            for($y=4;$y--;) // D: loop $y through piece lines
                if ($f[$d+$y]>>$x & $s>>$y*4 & 15) // if collision ...
                    goto L;         // goto Label: paint piece, tetris, next piece
#   }
    return$c;               // return score
}

供参考:旧映射

            hexdec(decoct(          // 3. map from base 8 to base 16
                                    // 0. dword values - from high to low: rotation 3,2,1,0
                [0x991e991e,0xc90f933c,0x4b27d239,0x1b1b1b1b,0,0x5a335a33,0x59179a3a]
                [ord($m[0])/2%9]    // 1. map ZJLO.ST to 0123.56 -> fetch wanted tetromino
                >>8*$m[1]&255       // 2. the $m[1]th byte -> rotated piece
            ))

测试

看到我的其他PHP答案

想看?

#从函数源中删除并添加以下内容:

function paint($f,$s=0,$d=0,$x=0){echo'<pre>';for($y=count($f)+5;$y--;$g=0)if(($t=(($z=$y-$d)==$z&3)*($s>>4*$z&15)<<$x)|$r=$f[$y]|0){$z=$t?" $r|$t=<b".(1022<($z=$t|$r)?' style=color:green':'').">$z":" <b>$r";for($b=1;$b<513;$b*=2)$z=($t&$b?'<b>'.($r&$b?2:1).'</b>':($r&$b?1:'.')).$z;printf("%02d $z</b>\n",$y);}echo'</pre>';}

一些打高尔夫球的步骤

启5:一大飞跃(399- 21 = 378)来通过简单地移动列移位
从单独的循环已有的两个环路。

修订版8:将单件($ s)从阵列切换到基数16并没有多大作用,
但为更多的打高尔夫球提供了空间。

修订版17:使用base64_encode(pack('V*',<values>))
和处理字节值,而不是unpack保存16个字节

修订版25至29:受戴夫(Dave)代码启发:新的散列(-2),新的循环设计(-9),转到(-10)
没有预移位;这将花费17个字节。

更大的潜力

使用/2%9, 通过将二进制数据放入文件然后进行索引,我可以节省15个字节(仅使用14个字节/4%5)。 我要吗
bfile(b)[0]

UTF-8字符的转换成本很高。

在散列上

我用过ZJLO.ST /2%9 -> 0123.56; 但T.ZJLOS /3%7 -> 0.23456还是一样。
再增加一个字节:O.STJLZ %13/2 -> 0.23456
再增加三个字节:OSTZJ.L %17%12%9 -> 01234.6

我找不到一个短的哈希(最多5个字节),没有任何间隙。
但是Dave找到了STZJL /4%5 -> 01234,从列表中删除了O。wtg!

顺便说一句:TIJSL.ZO (%12%8) -> 01234.67叶室的I形状
(和虚构AMY形状)。%28%8%84%8,执行相同的操作(但使用E代替A)。


不错 我喜欢组合的绘画和线条检测功能,并且break 2比我在C语言中做的要干净得多!您也许可以使用array_diff(将完成的行设置为固定值,而不是使用unset然后替换array_valuesarray_diff)来保存一些字节,但是我无法从文档中得知这是否会使重复的值变平(例如array_diff([1,2, 2,3],[1])-> [2,2,3]或仅[2,3])
Dave

@Dave:array_diff不删除重复值;并且我已经有了固定值(1023);但它并没有重新索引数组。好主意,但要花一个字节。
泰特斯

哇,当您飞过我时,那真是大声loud!看来我有一些事要做!
戴夫

恭喜您达到300!我将实施您最新的建议更改(由于我不需要/10到处都没有考虑简化轮换检查),但是否则我认为我已经完成了。我对PHP和C如此直接的竞争感到惊讶。这很有趣-希望OP接受您的回答!
戴夫

@Dave&Titus-我希望我可以接受两个答案。你们做得很好。无论如何,祝贺Titus达到300。我认为实际上是299,因为您在后面有一个无用的空间if
Arnauld

16

C,401 392 383 378 374 351 335 324 320 318 316 305字节

d[99]={'3Z3Z',983177049,513351321,1016270793,970073931,~0},*L=d+5,V,s;char c;main(x){B:for(c=0;c[++L]|V;V>>=4)c-=(L[c]=*L|(V&15)<<x)>1022;s-=c;if(scanf("%c%1d%d,",&c,&V,&x)>2)for(V=c^79?d[c/4%5]>>24-V*8:27,V=c^73?V/64%4<<8|V/8%8<<4|V%8:V&32?15:4369;;--L)for(c=4;c--;)if(L[c]>>x&V>>c*4&15)goto B;return s;}

在stdin上输入逗号分隔的输入,并在退出状态下返回分数。

需要char签名(这是GCC的默认设置),并需要'3Z3Z'解释为861549402(至少在小字节序机器上的GCC就是这种情况)。


用法示例:

echo "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" | ./tetris; echo "$?";

高层解释:

除直线外的所有形状都可以放入3x3网格中,但缺少一个角:

6 7 -
3 4 5
0 1 2

这意味着每个字节存储一个字节很容易。例如:

- - -         0 0 -
- # -   -->   0 1 0   -->   0b00010111
# # #         1 1 1

(我们将每一块对齐框的左下角以使其放下更容易)

由于我们将至少4个字节传递给一个int,这意味着我们可以将每个片段的所有4个旋转都存储在单个整数中,并且该行有特殊情况。我们还可以将游戏网格的每一行都设置为一个int(仅需要10位),而将当前下降的那一部分设置为一个long(4行= 40位)。


分解:

d[99]={             // General memory
  '3Z3Z',           //  Shape of "S" piece (multi-char literal, value = 861549402)
  983177049,        //  Shape of "T" piece
  513351321,        //  Shape of "Z" piece
  1016270793,       //  Shape of "J" piece
  970073931,        //  Shape of "L" piece
  ~0                //  Bottom of game grid (solid row)
},                  //  Rest of array stores lines in the game
*L=d+5,             // Working line pointer (start >= base of grid)
V,                  // Current piece after expansion (also stores rotation)
s;                  // Current score (global to initialise as 0)
char c;             // Input shape identifier (also counts deleted lines)
main(x){            // "x" used to store x location
  B:                                // Loop label; jumps here when piece hits floor
  for(c=0;c[++L]|V;V>>=4)           // Paint latest piece & delete completed lines
    c-=(L[c]=*L|(V&15)<<x)>1022;
  s-=c;                             // Increment score
  if(scanf("%c%1d%d,",&c,&V,&x)>2)  // Load next command
    for(V=c^79?                     // Identify piece & rotation
          d[c/4%5]>>24-V*8          //  Not "O": load from data
        :27,                        //  Else "O": always 27
        V=c^73?                     // If not "I" (line):
          V/64%4<<8|V/8%8<<4|V%8    //  expand shape to 16-bits
        :                           // Else we are a line:
          V&32?                     //  "I" coincides with "J"; use this to check
               15:4369;             //  horizontal/vertical
        ;--L)                       // Loop from top-to-bottom of grid
      for(c=4;c--;)                 //  Loop through rows of piece
        if(L[c]>>x&V>>c*4&15)       //   If collides with existing piece:
          goto B;                   //    Exit loops
  return s;                         // Return score in exit status
}

-4,-1(感谢@Titus)和-23,-11(感谢他们的回答)


好东西!您可以s+=(d[A-x]=d[A])不用使用x吗?
Arnauld

不幸的x是,需要跟踪当前步骤中要折叠A的行数(A-x随着循环的进行,每行都设置为row的值)
Dave

天哪!我的错。很抱歉有这么愚蠢的建议。:)
Arnauld

1
@Titus滥用了C的数组索引。简单地说,1[a]a[1]做同样的事情(或者更准确地说,a[b]翻译成*(a+b))。像这样滥用它是为了避免使用括号。在这种情况下,1[*v]== (*v)[1],即命令的第二个字母,即旋转。
戴夫

1
你能摆脱I占位符吗?如果是这样,请尝试/2%9使用哈希代替%12%12%8如果不。
泰特斯

2

红宝石,474 443 428 379 + 48 = 427个字节

-1感谢@Titus

绝对可以打更多的高尔夫球。

从STDIN或文件名中读取片段的二进制字典(请参见下文),并以移动列表作为参数,例如$ cat pieces | ruby script.rb O00,T24,S02,...

q=$*.pop
z=$<.read.unpack('S*')
b=c=g=i=0
x=10
u=1023
r=->n{n&2**x-1>0?n:r[n>>x]}
y=->n{n>0?[n&u]+y[n>>x]:[]}
l=->c,n{z.find{|m|m>>x==c<<2|n.to_i}||l[c,n-2]}
q.scan(/(\w)(\d)(\d)/){n=l["IOTLJSZ".index($1),$2.to_i]
v=(n&1|(n&6)<<9|(n&56)<<17|(n&960)<<24)<<$3.to_i
(y[b].size+1).times{t=b<<40
t&v>0&&break
g=t|v
v<<=x}
b=0
a=y[r[g]]
a.grep_v(u){|o|b|=o<<x*i
i+=1}
c+=a.count u}
p c

二进制数据(xxd格式)

0000000: c003 4b04 d810 d814 b820 9c24 d029 5a2c  ..K...... .$.)Z,
0000010: 7830 9634 e039 ca3c 3841 d444 c849 4e4c  x0.4.9.<8A.D.INL
0000020: 9861 9869 5c64 5c6c f050 f058 9a54 9a5c  .a.i\d\l.P.X.T.\

在repl.it上看到它(带有硬编码的参数,字典):https ://repl.it/Cqft/2

脱节和解释

# Move list from ARGV
q = $*.pop

# Read piece dictionary; pieces are 16-bit integers: 3 for letter,
# 2 for rotation, 10 for shape (and 1 wasted)
z = $<.read.unpack('S*')

# Empty board and various counters
b = c = g = i = 0
x = 10 # Magic numbers
u = 1023

# A function to remove empty lines
r = ->n{ n & 2**x - 1 > 0 ? n : r[n >> x] }

# A function to split the board into lines
y = ->n{ n > 0 ? [n & u] + y[n >> x] : [] }

# A function to lookup a piece by letter and rotation index
l = ->c,n{ z.find {|m| m >> x == c << 2 | n.to_i } || l[c, n-2] }

# Read the move list
q.scan(/(\w)(\d)(\d)/) {
  # Look up the piece
  n = l["IOTLJSZ".index($1), $2.to_i]

  # Convert the 10-bit piece to a 40-bit piece (4 rows of 10 columns)
  v = (n & 1 |
        (n & 6) << 9 |
        (n & 56) << 17 |
        (n & 960) << 24
      ) << $3.to_i # Shift by the appropriate number of columns

  # Drop the piece onto the board
  (y[b].size + 1).times {
    t = b << 40
    t & v > 0 && break
    g = t | v
    v <<= x
  }

  # Clear completed rows
  b = 0
  a = y[r[g]]
  a.grep_v(u) {|o|
    b |= o << x * i
    i += 1
  }

  c += a.count u # Count cleared rows
}
p c

1个字节:m >> 10可能是m >> x
Titus

@Titus好眼睛。谢谢!
约旦

无需\d在正则表达式中显式要求s:/(\w)(\d)(\d)//(\w)(.)(.)/
manatwork 2013年

2

PHP,454个 435 427 420 414字节

片段和地图的位域;但是没有I像戴夫(Dave)的高尔夫球那样特殊的情况。

<?$t=[I=>[15,4369],O=>[51],T=>[114,562,39,305],L=>[113,802,71,275],J=>[116,547,23,785],Z=>[54,561],S=>[99,306]];foreach($argv as$z=>$m)if($z){$s=$t[$m[0]][$m[1]%count($t[$m[0]])];for($d=$i=0;$i<4;$i++)for($k=0;$k<4;$k++)if($s>>4*$k&1<<$i){for($y=0;$y++<count($f);)if($f[$y-1]&1<<$m[2]+$i)$d=max($d,$y-$k);$k=3;}for($k=$d;$s;$k++,$s>>=4)if(1022<$f[$k]|=$s%16<<$m[2]){$c++;unset($f[$k]);}$f=array_values($f);}echo$c;

从命令行获取参数,打印结果

取消功能

将参数作为数组,返回结果

function t($a)
{
    // bitwise description of the stones and rotations
    $t=[I=>[15,4369],O=>[51],T=>[114,562,39,305],L=>[113,802,71,275],J=>[116,547,23,785],Z=>[54,561],S=>[99,306]];
    foreach($a as$m)
    {
        $s=$t[$m[0]][$m[1]%count($t[$m[0]])];   // $s=stone
        // find dropping space
        for($d=$i=0;$i<4;$i++)
            // a) lowest pixel of stone in column i
            for($k=0;$k<4;$k++)
                if($s>>4*$k&1<<$i)
                {
                    // b) top pixel of field in column x+i 
                    for($y=0;$y++<count($f);)
                        if($f[$y-1]&1<<$m[2]+$i)$d=max($d,$y-$k);
                    $k=3; // one byte shorter than `break;`
                }
        // do drop
        for($k=$d;$s;$k++,$s>>=4)
            if(1022<$f[$k]|=$s%16<<$m[2])   // add block pixels to line pixels ... if full,
            {$c++;unset($f[$k]);}           // tetris
        $f=array_values($f);
    }
    return$c;
}

测试(功能)

$data=[
    "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19"=>4,
    "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" => 5,
    "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" => 4,
    "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,I10,I10" => 8,
    // additional example for the two last tetrominoes:
    'O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02' => 8,
];
function out($a){if(is_object($a)){foreach($a as$v)$r[]=$v;return'{'.implode(',',$r).'}';}if(!is_array($a))return$a;$r=[];foreach($a as$v)$r[]=out($v);return'['.join(',',$r).']';}
function cmp($a,$b){if(is_numeric($a)&&is_numeric($b))return 1e-2<abs($a-$b);if(is_array($a)&&is_array($b)&&count($a)==count($b)){foreach($a as $v){$w = array_shift($b);if(cmp($v,$w))return true;}return false;}return strcmp($a,$b);}
function test($x,$e,$y){static $h='<table border=1><tr><th>input</th><th>output</th><th>expected</th><th>ok?</th></tr>';echo"$h<tr><td>",out($x),'</td><td>',out($y),'</td><td>',out($e),'</td><td>',cmp($e,$y)?'N':'Y',"</td></tr>";$h='';}
foreach($data as $v=>$e)
{
    $x=explode(',',$v);
    test($x,$e,t($x));
}

427?轮到你了!
约旦

@Jordan:那427包括<?开销:)
Titus
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.