테트리스 이동 목록이 주어지면 완성 된 줄 수를 반환하십시오.


37

기술

우리는 각각의 움직임이 다음과 같이 구성된 약간 단순화 된 테트리스 버전을 고려합니다.

  • 조각을 시계 방향으로 0 ~ 3 회 회전
  • 주어진 열에 조각을 배치
  • 빠른 드롭

목표는 그러한 테트리스 이동 목록이 주어지면 완성 된 라인의 수를 결정하는 것입니다.

표준 테트리스 규칙에 따라 조각을 놓으면 완성 된 행이 제거됩니다.

운동장

운동장은 10 열 너비입니다. 아무 없습니다 게임 오버 와는 더 운동장의 구성을 문제가, 위의 작업을 수행하지 항상 충분한 공간과 시간이 있다고 가정한다. 운동장의 높이는 여기서 중요하지 않지만 표준 22 행을 상한으로 사용할 수 있습니다.

테트로 미노의 모양

모양

입출력

입력

쉼표로 구분 된 Tetris 목록은 3 자로 인코딩됩니다. 처음 두 문자는 사용할 Tetromino 모양을 나타내고 마지막 두 문자는 떨어 뜨린 위치를 나타냅니다.

  1. 의 Tetromino : I, O, T, L, J, Z또는 S, 상기와 같은 순서로한다.
  2. 시계 방향 회전 수 : 0~3
  3. 칼럼 : 09. x회전 후 조각의 왼쪽 상단 모서리 ( 위 그림에 표시되어 있음 )가있는 열입니다. 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
솔루션이 쉼표로 구분 된 문자열이 아닌 배열로 입력을받을 수 있습니까?
Jordan

1
이것은 내가 오랫동안 본 최고의 PCG 질문입니다. 정말 좋은 생각입니다! 흥미롭고 실용적이며 너무 크지는 않지만 너무 작지 않은 주관적 의미에서 최고입니다.
GreenAsJade

답변:


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 : 16 바이트 를 절약하는 base64_encode(pack('V*',<values>))
대신 바이트 인덱싱을 사용 하여 값을 크런치했습니다.unpack

Rev. 25 to 29 : Dave의 코드에서 영감을 얻은 것 : 새로운 해싱 (-2), 새로운 루프 디자인 (-9), goto (-10)
프리 시프트 없음; 그 비용은 17 바이트입니다.

더 많은 잠재력

으로 /2%9, 나는 15 바이트 (만 14 바이트를 저장할 수 /4%5)
파일에 바이너리 데이터를 넣어 b다음 색인 file(b)[0].
내가 원하는가요?

UTF-8 문자는 변환에 많은 비용이 듭니다.

해싱

나는 사용했다 ZJLO.ST /2%9 -> 0123.56; 그러나 T.ZJLOS /3%7 -> 0.23456좋다.
1 바이트 이상 : O.STJLZ %13/2 -> 0.23456
3 개 이상 :OSTZJ.L %17%12%9 -> 01234.6

간격이없는 짧은 해시 (최대 5 바이트)를 찾을 수 없습니다.
그러나 Dave STZJL /4%5 -> 01234는 목록에서 O를 삭제 하여을 찾았습니다 . wtg!

BTW : TIJSL.ZO (%12%8) -> 01234.67에 대한 잎 실 I모양
(과 허구 A, M또는 Y모양). %28%8, 대신 %84%8동일하게 수행하십시오 .EA


좋은; 나는 결합 된 그림 + 선 감지를 좋아하며 break 2C에서해야했던 것보다 훨씬 깨끗합니다! 를 사용하여 일부 바이트를 절약 할 수 있지만 array_diff(완료 된 줄을 고정 값으로 설정 unset한 다음 바꾸기 array_values로 대체하십시오 array_diff) 반복되는 값을 병합하는 경우 문서에서 알 수 없습니다 (예 : array_diff ([1,2, 2,3], [1])-> [2,2,3] 또는 단지 [2,3])
Dave

@Dave : array_diff중복 값을 제거하지 않습니다. 그리고 나는 이미 고정 값을 가지고 있습니다 (1023). 그러나 배열을 다시 색인화 하지 않습니다 . 좋은 생각이지만 바이트 비용이 듭니다.
Titus

당신이 나를 지나쳐 날아 갔을 때 와우! 내가 따라 잡아야 할 것 같아!
Dave

300에 도달 한 것을 축하합니다! 나는 당신의 최근 제안 된 변경 사항을 구현할 것입니다 (지금은 필요하지 않기 때문에 회전 확인을 단순화하려고 생각하지 않았습니다 /10). 그렇지 않으면 내가 끝났다고 생각합니다. PHP와 C가 얼마나 직접적으로 경쟁하는지에 놀랐습니다. 이것은 재미있었습니다. OP가 귀하의 답변을 받아들이기를 바랍니다.
Dave

@ Dave & Titus-두 가지 답변을 모두 받아 들일 수 있기를 바랍니다. 너희들은 훌륭한 일을했다. 어쨌든 Titus에게 300에 도달 한 것을 축하합니다. 그리고 실제로 299 후에는 쓸모없는 공간이 있기 때문에 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 비트 만 필요)에 맞추고 현재 떨어지는 부분을 긴 (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
}

@Titus 덕분에 -4, -1, 답변에서 영감을 얻은 -23, -11


좋은 것! 당신은 s+=(d[A-x]=d[A])사용 하지 않고 그냥 할 수 x있습니까?
Arnauld

불행히도 x현재 단계에서 축소 할 행 수를 추적해야합니다 ( 루프가 진행됨에 따라 각 행 A이 행 값으로 설정 됨 A-x)
Dave

도! 내 잘못이야. 그런 바보 같은 제안에 대해 죄송합니다. :)
Arnauld

1
@Titus C의 배열 인덱싱을 남용합니다. 간단히 말하면, 1[a]그리고 a[1](더 정확하게 또는 같은 일을 a[b]에 번역 *(a+b)). 대괄호를 피하는 방법으로 이와 같이 남용됩니다. 이 경우 1[*v]== (*v)[1]즉, 명령의 두 번째 문자, 즉 회전입니다.
Dave

1
I자리 표시자를 제거 할 수 있습니까 ? 그렇다면 /2%9대신 해시로 시도하십시오 %12. %12%8그렇지 않다면.
Titus

2

루비 474 443 428 379 + 48 = 427 바이트

@Titus 덕분에 -1

이것은 확실히 더 골프 수 있습니다.

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 좋은 눈. 감사!
Jordan

\d정규 표현식에서 s 를 명시 적으로 요구할 필요는 없습니다 . /(\w)(\d)(\d)//(\w)(.)(.)/
manatwork

2

PHP, 454 435 427 420 414 바이트

조각과 맵을위한 비트 필드; 그러나 I데이브의 골프와 같은 형태의 특별한 경우는 없습니다 .

<?$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;

명령 행에서 인수를 가져오고 결과를 인쇄합니다

함수로 ungolfed

인수를 배열로 받아서 결과를 반환

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.