Code Golf: игра в тетрис

Основы:

Рассмотрим следующие тетроминоны и пустое игровое поле:

                                            0123456789
    I   O    Z    T    L    S    J         [          ]
                                           [          ]
    #   ##   ##   ###  #     ##   #        [          ]
    #   ##    ##   #   #    ##    #        [          ]
    #                  ##        ##        [          ]
    #                                      [          ]
                                           [==========]

Размеры игрового поля фиксированы. Номера в верхней части находятся здесь для указания номера столбца (также см. ввод).

Вход:

1. Вам предоставляется определенное игровое поле (на основе вышеизложенного), которое уже может быть частично заполнено с tetrominoes (это может быть в отдельном файле или предоставлено через stdin).

Пример ввода:

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

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

Пример ввода:

T2 Z6 I0 T7

Вы можете предположить, что ввод "правильно сформирован" (или создает поведение undefined, когда оно не работает).

Выход

Отобразить полученное поле ( "полные" строки должны исчезнуть) и распечатать счет (каждая потерянная линия учитывает 10 баллов).

Пример вывода на основе введенного выше примера:

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

Победитель:

Кратчайшее решение (по количеству символов кода). Примеры использования хороши. Получите удовольствие от игры в гольф!

Изменить: добавлена ​​награда +500 репутации, чтобы привлечь еще больше внимания к приятным усилиям, которые уже сделали уже создатели (и, возможно, некоторые новые решения по этому вопросу)...

Ответ 1

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"[email protected](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). Эта строка очень короткая: "[email protected](XBc_".

Декомпрессия тогда проста. Сначала мы делаем преобразование базы 126, за которым следует преобразование базы 8 ("~\10"{base}/, т.е. Итерация через "~\10" и базовое преобразование для каждого элемента). Полученный массив разбивается на группы по 3, массив для I фиксирован (3/~4*). Затем мы преобразуем каждый элемент в базу 2 и (после удаления нулей) заменим каждую двоичную цифру символом этого индекса в строке " #" (2base{" #"=}%...-1%) - обратите внимание, что нам нужно отменить массив, иначе 2 станет "# " вместо " #").

Формат платы/фрагмента, падающие фрагменты
Плата - это просто массив строк, по одному для каждой строки. Сначала изначально не было сделано никаких работ, поэтому мы можем сгенерировать его с помощью n/( на входе. Пьесы также представляют собой массивы строк, заполненные пробелами слева для их положения X, но без конечных пространств. Куски отбрасываются путем добавления к массиву и непрерывного тестирования наличия столкновений.

Тестирование на столкновение выполняется путем повторения всех символов в куске и сравнения с характером того же самого положения на доске. Мы хотим рассматривать # + = и # + # как коллизии, поэтому мы проверяем, является ли ((штукар и 3) и доски) ненулевой. Выполняя эту итерацию, мы также обновляем (копию) доску с ((штукарем и 3) | доской), которая правильно устанавливает значение для пар # + , + #, + [. Мы используем эту обновленную плату, если есть столкновение после перемещения части вниз в другую строку.

Удаление заполненных строк довольно просто. Мы удаляем все строки, для которых "= "& возвращает false. Заполненная строка не будет иметь ни =, ни , поэтому конъюнкция будет пустой строкой, которая равна false. Затем мы подсчитываем количество удаленных строк, добавляем счет к счету и добавляем много "[ ... ]" s. Мы порождаем это компактно, беря первую строку сетки и заменяя # на .

Bonus
Поскольку мы вычисляем, как будет выглядеть доска в каждой позиции куска, когда она падает, мы можем сохранить их в стеке вместо их удаления! В общей сложности на три символа мы можем выводить все эти позиции (или два символа, если у нас есть одноразрядные состояния платы).

{):X!-{2B{" #"=}%X" ":f*+-1%}%:P;:>.{>[f]P+:P(!:F;{\(@{3&\(@.2$&F|:F;|}%\+}%\+F!}
do;{"= "&},.,7^.R+:R;[>0="#"/f*]*\+}0"[email protected](XBc_""~\10"{base}:B/3/~4*"nIOZTLSJR "
";:"*~;n%)n*~ ]{n*n.}/10R*

Ответ 2

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(<>){[email protected],[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][email protected][$k,0..$k-1],map{s/#/ /}@{$A[0]},$i++if 9<grep/#/,@{$A[$k]}}last if$f}}}print+([email protected]$_,@A),$s//0,$/

Комментариев:

while(<>){
    # store the playfield as an AoA of chars
    [email protected],[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 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 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 "#" in line 0 with " "'s
                            $s+=10,@A[0..$k][email protected][$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+([email protected]$_,@A),$s//0,$/

Редактировать 1: некоторые серьезные игры в гольф, исправить ошибку выхода.
Отредактируйте 2: некоторые вставки, объединили две петли в одну для чистой экономии (барабан-ролл...) 3 символа, разное игра в гольф.
Редактирование 3: устранение некоторых общих подвыражений, небольшое постоянное слияние и изменение регулярного выражения.
Редактировать 4: изменилось представление tetrominoes в упакованном битовом векторе, misc golfing.
Редактировать 5: более прямой перевод из тетроминовой буквы в индекс массива, использовать непечатаемые символы, разное гольф.
Редактировать 6: исправлена ​​верхняя строка очистки ошибок, введенная в r3 (править 2), замеченная Nakilon. Используйте больше непечатаемых символов.
Изменить 7: использовать vec для получения данных tetromino. Воспользуйтесь тем, что игровое поле имеет фиксированные размеры. if statement = > if, слияние циклов редактирования 2 начинает окупаться. Используйте // для случая с 0 баллами.
Изменить 8: исправлена ​​еще одна ошибка, введенная в r6 (правление 5), замеченная Накилоном.
Редактирование 9: не создавайте новые ссылки при очистке строк, просто перемещайте ссылки вокруг с помощью массива. Слейте два map в один. Умное регулярное выражение. "Умнее" for. Разное для гольфа.
Редактировать 10: встроенный массив тетроминов, добавлена ​​прокомментированная версия.

Ответ 3

Ruby - 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

Ответ 4

Bash shell script ( 301 304 символа)


UPDATE: Исправлена ​​ошибка с участием фрагментов, которые распространяются в верхнюю строку. Кроме того, вывод теперь отправляется на стандартную версию, и в качестве бонуса можно снова запустить script, чтобы продолжить игру (в этом случае вы должны сами добавить общий балл).

Это включает непечатаемые символы, поэтому я предоставил шестнадцатеричный дамп. Сохраните его как 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  [email protected]
0000080: 6433 8bc7 eeca b57f a06d 27a1 4765 07e6  d3.......m'.Ge..
0000090: 3240 dd02 3df1 2344 f04a 0d1d c748 0bde  [email protected]=.#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 script do. Частицы Tetromino представлены в виде последовательностей команд редактора 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=('[email protected]' '[email protected]' '[email protected]' '[email protected]' '[email protected]' '[email protected]' '[email protected]')
    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

Ответ 5

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

Ответ 6

Ruby 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

Ответ 7

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

Это мой первый гольф для кодов, я думаю, что число персонажей может значительно < , было бы неплохо, если опытные игроки в гольф могут дать мне несколько советов.

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

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

Я не был уверен, как подсчитывать символы, поэтому я использовал размер файла кода со всеми ненужными удалениями строк.

EDIT 596 = > 581: Благодаря KitsuneYMG все, кроме предложения %ls отлично работало, кроме того, я заметил, что putch вместо putchar может использоваться (getch как-то не работает) и удаляется все круглые скобки в #define G.

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

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

ИЗМЕНИТЬ 496 = > 471: ширина и высота поля для игры теперь зафиксированы.

EDIT 471 = > 461: Незначительные изменения, putchar снова используются, поскольку putch не является стандартной функцией.

РЕДАКТИРОВАТЬ: Исправление ошибок, после того, как после удаления плитки были удалены полные строки, после этого завершенные строки могут быть оставлены в конце. Fix не изменяет количество символов.

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

Ответ 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')

Требуется одна новая строка, и я подсчитал ее как один символ.

Кодовая страница браузера mumbo jumbo может помешать успешной копированию и вставке этого кода, поэтому вы можете при необходимости сгенерировать файл из этого кода:

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

Суть в трех загадочных строках, которые я сказал, я объясню.

Форма тетроминонов кодируется в шестнадцатеричном числе. Предполагается, что каждый tetronimo занимает 3x4 сетку ячеек, где каждая ячейка является либо пустой (пробел), либо полной (знак числа). Затем каждая часть кодируется тремя шестнадцатеричными цифрами, каждая цифра описывает один столбец с 4 ячейками. Наименее значащие цифры описывают самые левые столбцы, а младший значащий бит в каждой цифре описывает самую верхнюю ячейку в каждом столбце. Если бит равен 0, то эта ячейка пуста, в противном случае это '#'. Например, тетронимо I кодируется как 00F, причем четыре бита наименьшей значащей цифры установлены для кодирования четырех знаков числа в крайнем левом столбце и T - 131, причем верхний бит установлен слева и справа, а верхние два бита установлены в середине.

Все шестнадцатеричное число затем сдвигается на один бит влево (умножается на два). Это позволит нам игнорировать самый нижний бит. Я объясню, почему через минуту.

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

Назначение drop определяет, сколько строк с вершины сетки кусок упадет перед посадкой на другие фрагменты фрагмента. Первая строка показывает, сколько пустых ячеек находится в верхней части каждого столбца игрового поля, а вторая находит самую низкую занятую ячейку в каждом столбце части. Функция zip возвращает список кортежей, где каждый кортеж состоит из ячейки n th от каждого элемента в списке ввода. Таким образом, используя входную плату образца, zip(board[:6] + [full]) вернется:

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

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

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

Например, столбцы Iтетромино, как упоминалось ранее, являются F, 0 и 0. bin(0xF) '0b1111'. После игнорирования '0b' мы имеем длину 4, что является правильным. Но bin(0x0) 0b0. После игнорирования '0b' мы все еще имеем длину 1, что неверно. Чтобы учесть это, мы добавили дополнительный бит в конец, чтобы мы могли игнорировать этот незначительный бит. Следовательно, +3 в коде должен учитывать дополнительную длину, занятую '0b' в начале, и незначительный бит в конце.

Все это происходит в выражении генератора для трех столбцов ((0,1,2)), и мы берем результат min, чтобы найти максимальное количество строк, которые кусок может упасть, прежде чем он коснется любого из трех столбцов.

Остальное должно быть довольно легко понять, прочитав код, но цикл for, следующий за этими назначениями, добавляет часть к доске. После этого цикл while удаляет полные строки, заменяя их пустыми строками вверху и подсчитывает счет. В конце плата и оценка печатаются на выходе.

Ответ 9

Python, 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. исправлена ​​ошибка, отмеченная Nakilon по цене +5

Ответ 10

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*{&+}/|{\[email protected]<'#'+\)|>+}4*{'['\10*']'++}:
;n/0\~n+:|;0\{.'#'
={;)}{n+|+:|;}if\.}do;' '
n+\[email protected]*|+\$+:$;.,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

Обратите внимание, что во входном файле нет конца строки, конец строки разбивает script как есть.

Ответ 11

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([email protected])in blit(make l '#')0([email protected])(i+p)l;if c([email protected])=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)

Ответ 12

Общий Lisp 667 657 645 символов

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

Ответ 13

Ruby 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

Edit Теперь используйте обычный рубин. Получены стены.

Ответ 14

Другой в 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