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

Основы:

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

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

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

Входные данные:

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

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

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

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

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

T2 Z6 I0 T7

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

Выход

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

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

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

Победитель:

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

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

14 ответов

Решение

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. Затем мы подсчитываем количество удаленных строк, добавляем их к счету и добавляем столько "[ ... ]" s. Мы генерируем это компактно, беря первый ряд сетки и заменяя # с ,

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

{):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*

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

(Требуется Perl 5.10 для определенного или // оператор).

Принимает все входные данные от стандартного ввода. Все еще нужен серьезный гольф.
Обратите внимание, что ^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: встроенный массив tetromino, добавлена ​​прокомментированная версия.

Рубин - 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

Скрипт 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

Питон: 504 519 символов

(Решение Python 3) В настоящее время требуется установить входные данные в формате, показанном вверху (входной код не учитывается). Я расширю, чтобы читать из файла или стандартного ввода позже. Теперь работает с подсказкой, просто вставьте ввод (всего 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] ) )

Рубин 1.9, 357355353339330310 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 escape-коды (включая нулевые байты в третьей строке) должны быть заменены их фактическим непечатаемым эквивалентом.

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

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

Использование:

ruby1.9 tetris.rb < input

или же

ruby1.9 tetris.rb input

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

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

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

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

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

РЕДАКТИРОВАТЬ 596=>581: Благодаря KitsuneYMG, все, кроме %ls предложение сработало отлично, кроме того, я заметил putch вместо putchar может быть использован (getch как-то не работает) и убрал все скобки в #define G,

РЕДАКТИРОВАТЬ 581=>556: не был удовлетворен оставшимися for и вложенные F петли, так что было некоторое слияние, изменение и удаление петель, довольно запутанно, но определенно стоило того.

РЕДАКТИРОВАТЬ 556=>517: наконец-то нашел способ сделать a массив int. Немного N; объединены с cнет break больше.

РЕДАКТИРОВАТЬ 496=>471: ширина и высота игрового поля теперь фиксированы.

РЕДАКТИРОВАТЬ 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);}

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 

Этот код распаковывает исходный код и выполняет его с 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

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

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

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

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

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

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

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

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

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

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

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

Питон, 298 символов

Пока что лучше всех неэзотерических языковых решений (Perl, Ruby, C, bash...)


... и даже не использует chicanery шифрование кода.

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

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

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

О'Камл 809 782 Чарс

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)

Common 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

Рубин 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

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

Еще один в 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
Другие вопросы по тегам