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


83

Основы:

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

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

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

Вход:

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

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

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

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

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

T2 Z6 I0 T7

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

Вывод

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

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

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

Победитель:

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

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


5
@omouse: проверьте meta.stackoverflow.com - код гольф обычно разрешен (в форме вики сообщества)
ChristopheD

18
@omouse: Вот для чего нужно закрытие голосования. Перетаскивание модераторов сюда с пометкой вопроса, вероятно, не сделает вас настолько популярным, учитывая, что сообщество снова и снова (неохотно) позволяло (неохотно) допускать существование кода гольфа (см. Тег code-golf и мета-обсуждения; в этом нет ничего нового) .
Марк Петерс,

8
@omouse: Не по теме! = спам. Даже если вы не можете проголосовать за закрытие, этот флаг спама не нужен.
BoltClock

3
Жду качка APL! Готов поспорить, он может сделать это с помощью 3,5 символов
n8wrl 08

3
Размеры должны быть фиксированными, но ввод образца и пустое поле имеют разную высоту. Какая должна быть высота?
Набб

Ответы:


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, массив для Ifixed ( 3/~4*). Затем мы преобразуем каждый элемент в основание 2 и (после удаления нулей) заменяем каждую двоичную цифру символом этого индекса в строке " #"( 2base{" #"=}%...-1%- обратите внимание, что нам нужно перевернуть массив, иначе 2он стал бы "# "вместо " #").

Формат «доска / кусок», сбрасывание частей
Доска - это просто массив строк, по одной на каждую строку. Изначально над этим не ведется никакой работы, поэтому мы можем сгенерировать его n/(на входе. Куски также представляют собой массивы строк, заполненные пробелами слева для их позиции X, но без завершающих пробелов. Части отбрасываются путем добавления к массиву и непрерывного тестирования, есть ли столкновение.

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

Убрать заполненные ряды довольно просто. Удаляем все строки, для которых "= "&возвращается false. В заполненной строке не будет ни =или , поэтому соединение будет пустой строкой, что приравнивается к 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*

Прямо здесь происходит какой-то экстремальный код-гольф (я не думал, что это можно сделать менее чем за 200 символов). Хорошая работа!
ChristopheD

8
Удивительный. Хотел бы я понять GolfScript. Подождите ... нет, не знаю.
P Daddy

26

Perl, 586 523 483472427 407 404 386 387 356 353 символа

(Требуется Perl 5.10 для //оператора defined-or ).

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

1
Я еще не отказался от Perl! xD (хотя хотелось бы сейчас увидеть и другие решения ..)
тыкаю

@Nakilon: Хороший улов! У вас есть хороший тестовый пример.
ninjalj 06

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
Мне бы очень хотелось увидеть расширенное объяснение этого кода. Выглядит так больно ... не могу осознать это.
Нильс Ридеманн 08

1
@ Нильс Ридеманн, я пишу объяснение прямо сейчас, но думаю опубликовать его сейчас или после объявления победителя) В любом случае, однажды я опубликую и отвечу на все вопросы, потому что это вики сообщества с основной идеей, чтобы быть полезным)
Nakilon

В Debian ruby ​​1.9.2dev (30.07.2010) это не подходит для вашего тестового примера на paste.org.ru/?6ep1on. Кроме того, он всегда расширяет игровое поле до десяти строк?
ninjalj

@ninjalj, ruby ​​1.9.2p0 (18.08.2010) [i386- mingw32 ] paste.org.ru/?1qnjhj Выглядит нормально. Полагаю, ширина 10 - это стандарт тетриса.
Nakilon

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. Престижность вам, сэр.
Майкл Андерсон,

На выполнение требуется до смешного много времени, а затем создается неправильный вывод для тестового примера »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 "(та же плата, что и пример ввода). Более того, тысячи строк мусора попадают в стандартный вывод при передаче по конвейеру, и вместо этого результат платы, вероятно, должен идти туда.
Набб,

Было бы намного быстрее, если бы вместо Vim в качестве vi был установлен Элвис.
PleaseStand

2
@Nabb: Я только что исправил все эти проблемы всего за три символа.
PleaseStand

Вау. Это довольно впечатляющее злоупотребление bash.
P Daddy

13

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

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

Пожалуйста, сделайте свой вклад как в задаче. Я имею в виду ввод из файла или STDIN.
Nakilon 05

6
Разве вам не нравится, что даже минимизированный код Python по-прежнему довольно читабелен?
EMP

@Evgeny, только если сравнивать с Perl или Malbolge)
Nakilon

Ну, я имел в виду «читабельный» по сравнению с другими ответами на гольф-код!
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

Обратите внимание, что \000escape-символы (включая нулевые байты в третьей строке) должны быть заменены их фактическим непечатаемым эквивалентом.

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

[          ]
[          ]
[          ]
[          ]
[ #    #  #]
[ ## ######]
[==========]
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) x4-битный массив, игровое поле сохраняется как есть, плитки удаляются, а полные строки удаляются в начале и после каждого падение плитки.

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

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

1
Разве вы не можете определить forкак #define F(x,m) for(x=0;x++<m;)? Он работает на C # ...: P
BrunoLM

@BrunoLM: Спасибо, но это не сработает, fe F(x,3){printf("%i",x}печатает 12вместо 012этого изменения. Можно было поменять на for(x=-1;x++<m;), но это ничего не спасает :)
schnaader 07

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

Тестирование

Интетрис

[]
[]
[]
[]
[# # #]
[## ######]
[==========]
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 tetronimo кодируется как 00F, с четырьмя битами наименее значимой цифры, установленными для кодирования четырех числовых знаков в крайнем левом столбце, а T - это131, с верхним битом, установленным слева и справа, и двумя верхними битами, установленными в середине.

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

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

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

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

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

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

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

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

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


6

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. исправлена ​​ошибка, указанная Накилоном, стоимостью +5


Довольно впечатляющий код. Чтобы исправить это, потребуется еще 14 символов ( ideone.com/zeuYB ), если нет лучшего способа, но, тем не менее, он превосходит все, кроме GolfScript и Bash. Это, безусловно, умное решение.
P Daddy

Да, абсолютно прекрасное решение!
ChristopheD

@Nakilon: спасибо, явно пропустил. fixed @ cost 293-> 298
Нас Банов,

@P Папа, спасибо - я нашел способ принести исправление в bash и toolchain, чтобы я был честен, говоря «все неэзотерические» :)
Нас Банов

@Nabb: чтобы код был короче, он был написан с учетом некоторых ограничений. что-то вроде 33 tetrominos max и 99 line drop max. Легко продлевается по цене +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?$*

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

> кот init.txt 
[]
[]
[]
[]
[# # #]
[## ######]
[==========]
T2 Z6 I0 T7> cat init.txt | рубиновый golfscript.rb tetris.gsc
[]
[]
[]
[# ###]
[# ###]
[##### ####]
[==========]
10

> кот init.txt
[]
[]
[]
[]
[# # #]
[## #####]
[==========]
I0 O7 Z1 S4> cat init.txt | рубиновый golfscript.rb tetris.gsc
[]
[]
[]
[#]
[### ####]
[### #####]
[==========]
10

> кот init.txt
[]
[]
[]
[## ###]
[# #]
[## ######]
[==========]
T7 I0 I3> cat init.txt | рубиновый golfscript.rb tetris.gsc
[]
[]
[]
[]
[# #]
[## # # #]
[==========]
20

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


2
/ me считает, что GolfScript не является настоящим языком соревнований ... Это просто библиотека, созданная для задач в гольф ... Размер этой библиотеки может быть добавлен к размеру кода сценария гольфа ...
Накилон

4
@Nakilon - Разве вы не можете сказать что-то подобное о том, что написано не на чистом машинном языке? :) Интерпретатор Python - это просто библиотека, добавьте ее размер в свою запись. </
sarcasm

2
@ Накилон: это просто переводчик. Это могло быть написано на любом другом языке; Вы бы все еще сказали, что Golfscript - это не настоящий язык?
Майкл Фукаракис 08

1
@Nabb: Спасибо, я подумал, что кое-что упустил ... Не расстраивайтесь, я тоже не стал разбираться в своем коде :).
coderaj 08

1
@Michael Foukarakis, я могу за 1 минуту написать свой собственный интерпретатор, чтобы решить эту задачу одним символом, и что?
Накилон 08

4

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

4

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

Не слишком коротко, но +1 за уродство! Я полагаю, что именно так выглядел бы алфавитный суп, если бы он шел со скобками.
P Daddy

@P Папа: Спасибо. Да, наверное, так бы это выглядело :).
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

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


Еще один рубист, здорово! Но сделайте стакан вокруг кирпичей.
Накилон 08

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.