Код гольфу: Гра в тетріс


83

Основи:

Розглянемо такі тетроміно та порожнє ігрове поле:

                                            0123456789
    IOZTLSJ []
                                           []
    # ## ## ### # ## # []
    # ## ## # # ## # []
    # ## ## []
    # []
                                           [==========]

Розміри ігрового поля є фіксованими. Цифри вгорі знаходяться тут, щоб вказати номер стовпця (також див. Введення).

Вхідні дані:

1 . Вам надається конкретне ігрове поле (виходячи з вищезазначеного), яке вже можна частково заповнити тетроміно (це може бути в окремому файлі або надано через stdin).

Зразок введення:

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

2 . Вам дається рядок, який описує (розділяється пробілами), який тетроміно вставити (і випасти) у якому стовпці. Тетроміно не потрібно обертати. Введення можна прочитати з stdin.

Зразок введення:

T2 Z6 I0 T7

Ви можете припустити, що введення є "добре сформованим" (або створювати невизначену поведінку, коли це не так).

Вихідні дані

Візуалізуйте отримане поле ("повні" рядки повинні зникнути) і надрукуйте рахунок балів (кожен опущений рядок складає 10 балів).

Висновок на основі наведеного вище зразка:

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

Переможець:

Найкоротше рішення (за кількістю символів коду). Приклади використання приємні. Веселі гольфу!

Редагувати : додано щедрості +500репутації, щоб привернути ще більше уваги до приємних зусиль, які вже зробили відповіді (і, можливо, деяких нових рішень цього питання) ...


5
@omouse: перевірити meta.stackoverflow.com - гольф із кодом загалом дозволений (у вікі-формі спільноти)
ChristopheD

18
@omouse: Ось для чого голосування для закриття. Перетягування сюди модераторів, позначивши запитання, мабуть, не зробить вас такою популярною, враховуючи те, що спільнота раз за разом (неохоче) дозволяла коду golf існувати (див. Тег code-golf та мета-обговорення; це нічого нового) .
Mark Peters

8
@omouse: Поза темою! = спам. Навіть якщо ви не можете проголосувати за закриття, цей прапор спаму не викликали.
BoltClock

3
Я чекаю APL jock! Б'юсь об заклад, він може зробити це за 3,5 символи
n8wrl

3
Розміри повинні бути фіксованими, але введення зразка та порожнє поле мають різну висоту. Яка висота повинна бути?
Nabb

Відповіді:


27

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

Стиснення тетроміно:
шматки зберігаються у вигляді трьох основних 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"і виконуємо базову перетворення для кожного елемента). Отриманий масив розбивається на групи по 3, масив для Iфіксується ( 3/~4*). Потім ми перетворюємо кожен елемент на базу 2 і (після вилучення нулів) замінюємо кожну двійкову цифру символом цього індексу в рядку " #"( 2base{" #"=}%...-1%- зауважте, що нам потрібно змінити масив в іншому випадку, а 2не "# "замість нього " #").

Формат дошки / шматка, що скидає шматки
Дошка - це просто масив рядків, по одному для кожного рядка. Спочатку над цим не робиться жодної роботи, тому ми можемо генерувати це n/(за допомогою вводу. Частини - це також масиви рядків, заповнені пробілами ліворуч для їх положення X, але без кінцевих пробілів. Шматки скидаються шляхом попереднього додавання до масиву та постійного тестування, чи є зіткнення.

Тестування на зіткнення проводиться шляхом перебору всіх символів у фігурі та порівняння з персонажем тієї самої позиції на дошці. Ми хочемо розглядати #+ =та #+ #як зіткнення, тому перевіряємо, чи є ((piecechar & 3) & boardchar) ненульовим. Роблячи цю ітерацію, ми також оновлюємо (копію) плати за допомогою ((piecechar & 3) | boardchar), який правильно встановлює значення для пар #+ , + #, + [. Ми використовуємо цю оновлену дошку, якщо виникає зіткнення після переміщення фігури вниз на інший ряд.

Видалити заповнені рядки досить просто. Ми видаляємо всі рядки, для яких "= "&повертається 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*

Тут відбувається якийсь екстремальний гольф з кодом (я не думав, що це можна зробити менш ніж за 200 символів). Хороша робота!
ChristopheD

8
Дивовижний. Я хотів би зрозуміти GolfScript. Зачекайте ... ні, ні.
P Daddy

26

Perl, 586 523 483 472 427 407 404 386 387 356 353 символів

(Потрібен 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: більш прямий переклад з букви тетроміно на індекс масиву, використання недрукованих символів, різне гольф.
Редагування 6: виправлена ​​помилка очищення верхньої лінії, введена в r3 (редагування 2), помічена Накілоном Використовуйте більше недрукованих символів.
Редагувати 7: використовувати vecдля отримання даних про тетроміно. Скористайтеся тим, що ігрове поле має фіксовані розміри. ifтвердження =>ifмодифікатор, злиття циклів редагування 2 починає окупатися. Використовуйте //для випадку 0 балів.
Редагування 8: виправлена ​​чергова помилка, введена в r6 (редагування 5), помічена Накілоном.
Редагування 9: не створюйте нових посилань при очищенні рядків, просто переміщуйте посилання за допомогою нарізки масиву. Злиття двох mapв одне. Розумніший регулярний вираз. «Розумніший» for. Різні гольфи.
Редагувати 10: вбудований тетроміно-масив, додана коментована версія.


Працюємо дуже приємно (і вже приємно розраховуємо персонажів для цієї нетривіальної проблеми). Однією з невеликих особливостей є те, що мій perl (perl, v5.10.0, побудований для darwin-thread-multi-2level), здається, друкує результат двічі (введення подається)
ChristopheD

@ChristopheD: виправлено дубльований вихід, я друкував у своєму основному циклі, але лише для ліній без ігрового поля. Напевно, у вас занадто багато нового рядка :)
ninjalj,

Ще 4 символи, щоб перемогти пітона !!
Vivin Paliath

1
Я ще не відмовився від perl! xD (Хоча я хотів би бачити і деякі інші рішення вже зараз ..)
тикайте

@Nakilon: Хороший улов! У вас там гарний тест.
ninjalj

24

Рубін - 427 408 398 369 359

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

Дуже приємне рішення! Мені доведеться поглянути, як саме ви закодували форми тероміноїв (таким чином виглядає дуже компактно).
ChristopheD

3
Я б дуже хотів побачити розширене пояснення цього коду. Це виглядає настільки хворим ... не можу обійти голову.
Nils Riedemann

1
@Nils Riedemann, я пишу пояснення прямо зараз, але я маю намір опублікувати його зараз, або після оголошення переможця) У будь-якому випадку, коли я
розміщу

У рубіні Debian 1.9.2dev (2010-07-30) це не вдається для вашого тесту на paste.org.ru/?6ep1on. Крім того, воно завжди розширює ігрове поле до десяти рядків?
ninjalj

@ninjalj, ruby ​​1.9.2p0 (18.08.2010) [i386-mingw32] paste.org.ru/?1qnjhj Виглядає добре. Ширина 10 - це, як я вважаю, стандарт Тетріса.
Накілон

17

Сценарій оболонки Bash ( 301 304 символи)


ОНОВЛЕННЯ: Виправлена ​​помилка, пов’язана із шматками, які тягнуться до верхнього ряду Також вихідні дані тепер надсилаються на стандартний вихід, і як бонус можна запустити сценарій ще раз, щоб продовжувати грати в гру (у такому випадку загальний рахунок потрібно скласти самостійно).

Сюди входять недруковані символи, тому я надав шістнадцятковий дамп. Збережіть як 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сценарію. Шматки тетроміно представлені у вигляді послідовностей команд редактора 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

1
Файл bash, декомпресія та запуск vi .. не впевнений у законності такої гидоти .. але це найбільше вражає, +1. похвали вам, сер.
Michael Anderson,

Завершується смішно довгий час, а потім генерується неправильний результат для тесту "T2 Z6 I0 T7 T2 Z6 T2 I5 I1 I0 T4 O8 T1 T6 T3 Z0 I9 I6 O7 T3 I2 O0 J8 L6 O7 O4 I3 J8 S6 O1 I0 O4 "(та сама плата, що і вхідний приклад). Більше того, тисячі сміттєпроводів збираються вибиватися з труби, і натомість результат дошки, ймовірно, повинен йти туди.
Nabb

Це було б набагато швидше, якби Елвіса встановили замість Vim як vi.
Будь ласка, зупиніться

2
@Nabb: Я щойно вирішив усі ці проблеми вартістю всього три символи.
Будь ласка, зупиніться

Ого. Це досить вражаюче зловживання bash.
P Daddy

13

Python: 504 519 символів

(Рішення Python 3) Наразі потрібно встановити введення у форматі, як показано вгорі (код вводу не враховується). Пізніше я розширюватимуться для читання з файлу або stdin. Тепер працює з підказкою, просто вставте введення (загалом 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')

Не впевнений, чи зможу я там значно більше заощадити. При перетворенні в бітові поля втрачається досить багато символів, але це економить набагато більше символів, ніж робота зі рядками. Також я не впевнений, що зможу видалити там більше пробілів, але спробую пізніше.
Не зможе зменшити його набагато більше; отримавши рішення на основі розрядів, я перейшов назад до рядків, оскільки знайшов спосіб стиснути його більше (збереглося 8 символів над розрядним полем!). Але з огляду на те, що я забув включити Lі мав помилку з точками всередині, мій підрахунок персонажів лише зітхає ... Можливо, я знайду щось пізніше, щоб трохи більше стиснути це, але я думаю, що я вже в кінці. Оригінальний та коментований код див. Нижче:

Оригінальна версія:

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] ) )

Я не думаю, що це правильно. Не існує правила, згідно з яким може зникнути лише нижній рядок, але, судячи з ваших коментарів, ви перевіряєте лише цей рядок.
Майкл Медсен,

Будь ласка, зробіть свій ввід як у завданні. Я маю на увазі введення з файлу або STDIN.
Nakilon

6
Вам не подобається, як навіть мініфікований код Python все ще досить читабельний?
EMP

@Evgeny, тільки в порівнянні з Perl або Malbolge)
Накілон

Що ж, я мав на увазі "читабельний" щодо інших відповідей на кодовий гольф!
EMP

13

Рубін 1.9, 357 355 353 339 330 310 309 символів

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

Ще один спосіб скинути тетроміно і зберегти все скло в масиві, навіть з межами ... приємно. Тепер ви будете лідером Ruby / Perl. PS: Я не знав про ?\s.
Накілон

12

C, 727 [...] 596 581 556 517 496 471 461 457 символів

Це мій перший кодовий гольф, я думаю, що кількість персонажів може набагато знизитися, було б непогано, якби досвідчені гравці в гольф дали мені кілька підказок.

Поточна версія також може обробляти ігрові поля з різними розмірами. Вхідні дані можуть мати розриви рядків як у форматі DOS / Windows, так і у форматі Unix.

Перед оптимізацією код був досить простим, тетроміно зберігаються у 4 цілих числах, які інтерпретуються як (7 * 3) х4-бітний масив, ігрове поле зберігається як є, плитки скидаються, а повні рядки видаляються на початку та після кожного капля плитки.

Я не був впевнений, як рахувати символи, тому використовував розмір файлу коду з усіма непотрібними розривами рядків.

EDIT 596 => 581: Завдяки KitsuneYMG все, крім %lsпропозиції, спрацювало бездоганно, крім того, я помітив, що putchзамість цього putcharможна використовувати ( getchякось не працює) і видалив усі дужки в #define G.

EDIT 581 => 556: Не задоволений рештою forта вкладеними Fциклами, тому відбулося деяке злиття, зміна та видалення циклів, досить заплутане, але однозначно того варте.

EDIT 556 => 517: Нарешті знайшов спосіб створити aмасив int. Деякі N;злилися з c, breakбільше ні .

EDIT 496 => 471: Ширина та висота ігрового поля встановлені зараз.

EDIT 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);}

1
Ви не можете визначити forяк #define F(x,m) for(x=0;x++<m;)? Це працює на C # ...: P
BrunoLM

@BrunoLM: Дякую, але це не спрацює, F(x,3){printf("%i",x}друкується 12замість 012цієї зміни. Можна змінити на for(x=-1;x++<m;), але це нічого не рятує :)
schnaader

1
Якщо ви написали код правильно, якщо ви компілюєте як C, вам не потрібно включати stdio.h (якщо я щось не пропустив?). Збережіть кілька символів :)

1
Ви можете замінити своє визначення N на (c=getchar())і видалити всі рядки c = N, зберігаючи 6 символів. Якщо я не помиляюся з цим, вам слід дійти до 585
KitsuneYMG

1
за замовчуванням введіть int також для змінних, принаймні для C89.
ninjalj

8

Python 2.6+ - 334 322 316 символів

397 368 366 символів без стиснення

#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()))

Тестування

intetris

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

Нові рядки повинні бути в стилі Unix (лише подача лінії). Кінцевий новий рядок на останньому рядку необов’язковий.

Тестувати:

> 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

Суть у трьох загадкових лініях, про які я сказав, що поясню.

Форма тетроміно кодується там у шістнадцяткове число. Вважається, що кожен тетронімо займає сітку клітин 3x4, де кожна клітинка або порожня (пробіл), або повна (знак цифри). Потім кожен фрагмент кодується 3 шістнадцятковими цифрами, кожна цифра описує один 4-клітинний стовпець. Найменш значущі цифри описують крайні ліві стовпці, а найменш значущі біти в кожній цифрі описують саму верхню комірку в кожному стовпці. Якщо біт дорівнює 0, тоді ця комірка порожня, інакше це "#". Наприклад, I тетронімо кодується як 00F, причому чотири біти найменш значущої цифри встановлені для кодування чотирьох цифрових знаків у крайньому лівому стовпці, а T дорівнює131, причому верхній біт встановлений ліворуч і праворуч, а два верхні біти встановлені посередині.

Потім шістнадцяткове число зміщується на один біт вліво (множиться на два). Це дозволить нам ігнорувати найнижчий біт. Поясню, чому через хвилину.

Отже, враховуючи поточний фрагмент із вхідних даних, ми знаходимо індекс цього шістнадцяткового числа, де починаються 12 бітів, що описують його форму, а потім зміщують його вниз, щоб біти 1–12 (пропускаючи біт 0) bitsзмінної описували поточний фрагмент.

Призначення dropвизначає, скільки рядів зверху сітки впаде шматок до посадки на інші фрагменти шматка. Перший рядок знаходить, скільки порожніх комірок є вгорі кожного стовпця ігрового поля, тоді як другий - найнижче зайняту комірку в кожному стовпці фігури. zipФункція повертає список кортежів, де кожен кортеж складається з п - й клітини від кожного елемента в списку введення. Отже, використовуючи зразок плати введення, zip(board[:6] + [full])поверне:

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

Вибираємо кортеж із цього списку, що відповідає відповідному стовпцю, і знаходимо індекс першого '#'в стовпці. Ось чому ми додали "повний" рядок перед викликом zip, так що indexвін матиме розумне повернення (замість викиду винятку), коли стовпець порожній.

Потім, щоб знайти найнижчий '#'у кожному стовпці фрагмента, ми зміщуємо та маскуємо чотири біти, що описують цей стовпець, а потім використовуємо binфункцію, щоб перетворити це на рядок одиниць та нулів. binФункція повертає тільки значущі біти, так що нам потрібно тільки обчислити довжину цього рядка , щоб знайти найнижчу зайняту комірку (найбільш значимого набір біт). binФункція також приєднує '0b', тому ми повинні відняти це. Ми також ігноруємо найменш значущий біт. Ось чому шістнадцяткове число зміщується на один біт вліво. Це враховує порожні стовпці, подання рядків яких мали б таку саму довжину, як стовпець, лише із заповненою верхньою коміркою (наприклад, шматок Т ).

Наприклад, стовпці I Tetromino, як згадувалося раніше, є F, 0і 0. bin(0xF)є '0b1111'. Після ігнорування '0b'ми маємо довжину 4, що є правильною. Але bin(0x0)є 0b0. Після ігнорування '0b'ми все ще маємо довжину '1, що є неправильним. Щоб це врахувати, ми додали додатковий біт до кінця, щоб ми могли ігнорувати цей незначний біт. Отже, +3код у коді є для врахування зайвої довжини, зайнятої '0b'на початку, і незначного біта в кінці.

Все це відбувається в виразі генератора для трьох стовпців ( (0,1,2)), і ми беремо minрезультат, щоб знайти максимальну кількість рядків, які фрагмент може скинути, перш ніж він торкнеться в будь-якому з трьох стовпців.

Решта має бути досить легко зрозуміти, читаючи код, але forцикл, що слідує цим завданням, додає шматок на дошку. Після цього whileцикл видаляє цілі рядки, замінюючи їх пустими рядками вгорі, і підсумовує рахунок. В кінці дошка та партитура друкуються на виході.


6

Пітон, 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

PS. виправлено помилку, на яку вказав Накілон, вартістю +5


Це досить вражаючий код. Щоб це виправити знадобилося б ще 14 символів ( ideone.com/zeuYB ), якщо не буде кращого способу, але навіть тим не менше, він перемагає все, крім GolfScript і Bash. Це, безумовно, розумне рішення.
P Daddy

Так, абсолютно прекрасне рішення!
ChristopheD

@Nakilon: дякую, явно пропустив це. фіксована @ вартість 293-> 298
Нас Банов

@P Татусю, дякую - я знайшов спосіб виправити помилку під bash & toolchain, щоб я був чесним, вимовляючи "все, що не є езотеричним" :)
Нас Банов,

@Nabb: щоб код був коротшим, його писали з урахуванням певних обмежень. щось на зразок 33 тетроміно макс і 99 крапель лінії макс. Можна легко продовжити за ціною +3. Або за низьку-низьку ціну :), обмеження можна зняти взагалі. До речі, це чудовий приклад того, як наявність тестового набору могло б пояснити специфікацію (те, що я прослуховував ChristopherD в коментарях)
Нас Банов,

5

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

Зверніть увагу, що у вхідному файлі немає кінця рядка, кінець рядка порушить сценарій як є.


2
/ я вважаю GolfScript не справжньою мовою змагань ... Це просто бібліотека, сформована прямо для завдань з гольфу ... Розмір цієї бібліотеки може бути доданий до розміру коду golfscript ...
Nakilon

4
@Nakilon - Чи не можете ви сказати щось подібне про щось, що не написане мовою машинного програмного забезпечення? :) Інтерпретатор Python - це просто бібліотека, додайте його розмір до вашого запису. </sarcasm>
bta

2
@Nakilon: це лише перекладач. Це може бути написано будь-якою іншою мовою; Ви все-таки сказали б, що Golfscript - це не справжня мова?
Michael Foukarakis

1
@Nabb: Дякую, я зрозумів, що було кілька хитрощів, які я пропустив ... Не почувайся погано, я теж не потрудився зрозуміти свій код :).
coderaj

1
@Michael Foukarakis, я можу за 1 хвилину написати власного перекладача, щоб вирішити це завдання одним символом, ну і що?
Nakilon

4

O'Caml 809 782 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)

4

Common Lisp 667 657 645 Chars

Моя перша спроба кодувати гольф, тому, мабуть, є багато хитрощів, яких я поки не знаю. Я залишив там кілька нових рядків, щоб зберегти залишкову "читабельність" (я порахував нові рядки як 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

Не надто короткий, але +1 за потворність! Я гадаю, що так би виглядав алфавітний суп, якби він був у дужках.
P Daddy

@P Daddy: Дякую. Так, мабуть, як би це виглядало :).
lpetru

2

Рубін 505 479 474 442 439 426 символів

Перша спроба. Зробили це за допомогою 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

Редагувати зараз, використовуючи звичайний рубін. Отримали стіни на виході ..


Ще один Рубіст, приємно! Але зробіть склянку навколо цегли.
Накілон

1

Ще один у Ruby, 573 546 символів

: **

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

Виправленоa.each{|x|s=a.max_by(&:size).size;x[s-=1]||=' 'while s>0}
glebm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.