Получив список ходов тетриса, верните количество завершенных строк


37

Описание

Мы рассматриваем слегка упрощенную версию тетриса, где каждый ход состоит из:

  • вращая деталь по часовой стрелке, от 0 до 3 раз
  • позиционирование фигуры в данном столбце
  • быстрое падение

Цель состоит в том, чтобы определить количество завершенных линий, учитывая список таких ходов тетриса.

Завершенные строки удаляются по мере удаления частей, следуя стандартным правилам тетриса.

Playfield

Игровое поле шириной 10 колонок. Там нет Game Over, и предполагается, что всегда есть достаточно места и времени для выполнения вышеуказанных действий, независимо от конфигурации игрового поля. Высота игрового поля здесь не имеет большого значения, но вы можете использовать стандартные 22 строки в качестве верхнего предела.

Формы Тетромино

формы

Ввод, вывод

вход

Разделенный запятыми список ходов тетриса, закодированных 3 символами. Первые два символа описывают используемую фигуру Тетромино, а последний - позицию, где она выпала.

  1. Tetromino: I, O, T, L, J, Zили S, в том же порядке , как указано выше.
  2. Количество вращений по часовой стрелке: 0до3
  3. Колонка: 0до 9. Это столбец , в котором верхний левый угол элемента (отмеченные звездочкой 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
Могут ли решения принимать входные данные в виде массива, а не через запятую?
Джордан

1
Это лучший вопрос PCG, который я видел долгое время. Какая прекрасная идея! Лучший в субъективном смысле интересный и практичный и не слишком большой, но не слишком маленький.
GreenAsJade

Ответы:


5

PHP, 405 399 378 372 368 360 354 347 331 330 328 319 309 300 байт

блок-картой Дейва )

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

некоторые шаги в гольф

Rev. 5: Большой скачок (399- 21 = 378) пришел, просто перемещая сдвиг колонки
от отдельного контура для двух существующих контуров.

Версия 8: Переключение с массива на основание 16 для фигуры ($ s) не дало много,
но уступило место еще большему гольфу.

Версия 17: сжатые значения с base64_encode(pack('V*',<values>))
использованием байтовой индексации вместо unpackсохранения 16 байтов

От 25 до 29: вдохновлено кодом Дэйва: новое хэширование (-2), новый дизайн цикла (-9),
переход к (-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так же хорошо.
один байт длиннее O.STJLZ %13/2 -> 0.23456
и еще три:OSTZJ.L %17%12%9 -> 01234.6

Я не смог найти короткий хеш (макс. 5 байт), который не оставляет пробела;
но Дейв нашел STZJL /4%5 -> 01234, выронив O из списка. WTG!

кстати: TIJSL.ZO (%12%8) -> 01234.67оставляет место для Iфигуры
(и вымышленной A, Mили Yфигуры). %28%8и %84%8сделать то же самое (но с Eвместо A).


Ницца; Мне нравится комбинированное распознавание линий и линий, и break 2оно намного чище, чем то, что я должен был сделать в C! Вы могли бы быть в состоянии сохранить несколько байт, используя array_diff(набор завершен строки фиксированного значения вместо использования unsetзатем заменить array_valuesс array_diff), но я не могу сказать , из документации , если это будет выравниваться повторяющиеся значения (например , array_diff ([1,2, 2,3], [1]) -> [2,2,3] или просто [2,3])
Дейв

@Dave: array_diffне удаляет повторяющиеся значения; и у меня уже есть фиксированное значение (1023); но он не переиндексирует массив. Отличная идея, но это будет стоить байта.
Тит

вау, это был громкий свист, когда ты пролетел мимо меня! Похоже, мне нужно наверстать упущенное!
Дейв

Поздравляю с достижением 300! Я внесу ваше последнее предлагаемое изменение (не думал, что теперь нужно упростить проверку поворота, поскольку мне не нужно /10все время), но в остальном я думаю, что все готово. Я удивлен тем, насколько конкурентными оказались PHP и C. Это было весело - надеюсь, что ОП примет ваш ответ!
Дейв

@ Дэйв и Титус - я бы хотел принять оба ответа. Вы, ребята, отлично поработали. В любом случае поздравляю Тита с достижением 300. И я думаю, что на самом деле это 299, так как у вас есть бесполезное пространство после a if.
Арно

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

Объяснение высокого уровня:

Все фигуры, кроме линии, могут поместиться в сетку 3х3 без одного угла:

6 7 -
3 4 5
0 1 2

Это означает, что их легко хранить в байтах. Например:

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

(мы выравниваем каждую часть по левому нижнему краю коробки, чтобы сделать ее более удобной)

Поскольку мы получаем по крайней мере 4 байта для целого числа, это означает, что мы можем хранить все 4 вращения каждого куска в одном целом числе со специальным случаем для строки. Мы также можем вписать каждый ряд игровой сетки в целое число (нужно всего 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
}

-4, -1 благодаря @Titus, и -23, -11 с вдохновением от их ответа


Хороший! Не могли бы вы просто обойтись s+=(d[A-x]=d[A])без использования x?
Арно,

К сожалению x, необходимо отслеживать, сколько строк свернуть в текущем шаге (каждая строка Aимеет значение строкиA-x в ходе цикла)
Дейв

D'о! Виноват. Извините за такое глупое предложение. :)
Арнаулд

1
@Titus - это злоупотребление индексацией массива Си. Проще говоря, 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

Ungolfed & объяснение

# 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 Хороший глаз. Благодарность!
Джордан,

Нет необходимости явно требовать \ds в регулярном выражении: /(\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;

принимает аргументы из командной строки, печатает результат

разряженный как функция

принимает аргументы как массив, возвращает результат

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 включают <?накладные расходы :)
Тит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.