З огляду на список переходів тетрісу, поверніть кількість виконаних рядків


37

Опис

Ми розглядаємо трохи спрощену версію Tetris, де кожен хід складається з:

  • обертаючи шматок за годинниковою стрілкою, від 0 до 3 разів
  • позиціонування шматка в заданій колонці
  • швидка крапля

Мета - визначити кількість виконаних рядків з урахуванням переліку таких рухів тетрісу.

Завершені ряди видаляються у міру скидання шматочків, дотримуючись стандартних правил Tetris.

Ігрове поле

Поле шириною 10 колонок. Ніякої гри не закінчено, і передбачається, що для виконання вищезазначених дій завжди достатньо місця та часу, незалежно від конфігурації ігрового поля. Висота ігрового поля тут насправді не має значення, але ви можете використовувати стандартні 22 ряди як верхню межу.

Форми тетроміно

форми

Введення-виведення

Вхідні дані

Список переміщених комами тетріс кодується з 3 символами. Перші два символи описують форму Tetromino, яку слід використати, а останній описує положення, де вона випала.

  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
Це найкраще питання про ПГГ, яке я бачив уже давно. Яка чудова ідея! Найкраще в суб’єктивному розумінні цікавого та практичного та не надто великого, але не надто малого.
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) прийшов, просто переміщуючи зрушення колонки
від окремого контуру для двох існуючих контурів.

Rev. 8: Перехід від масиву до бази 16 за штуку ($ s) не дав багато,
але створив місце для ще кількох гольфів.

Rev. 17: хрустів значення з base64_encode(pack('V*',<values>))
і використовував байти індексацію замість , unpackщоб зберегти 16 байт

Rev. 25 - 29: натхненний кодом Дейва: новий хешинг (-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само добре.
на один байт довше: O.STJLZ %13/2 -> 0.23456
і ще три:OSTZJ.L %17%12%9 -> 01234.6

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

btw: 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. Це було весело - сподіваємось, що ОП прийме вашу відповідь!
Дейв

@Dave & Titus - Я хотів би прийняти обидві відповіді. Ви, хлопці, зробили чудову роботу. І все-таки вітаю з Тітом за досягнення 300. І я думаю, що це насправді 299, оскільки у вас є марний простір після 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 байти до int, це означає, що ми можемо зберігати всі 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'oh! Моє ліжко. Вибачте за таку дурну пропозицію. :)
Арнольд

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 Добре око. Спасибі!
Йорданія

Не потрібно чітко вимагати \ds у регулярному виразі: /(\w)(\d)(\d)//(\w)(.)(.)/
манатура

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.