Программирование блоков Tetris (буквально)


33

В игре Tetris есть 7 типов кирпичей или Tetr i minoes , которые математически известны как tetr o minoes, потому что все они сделаны с 4 квадратными сегментами:

Тетрис кирпичи

Они имеют имена I, J, L, O, S, T и Z, которые соответствуют их приблизительным формам. С учетом поворотов на 90 ° в общей сложности насчитывается 19 уникальных фигур:

I
I
I
I

IIII

 J
 J
JJ

JJJ
  J

JJ
J
J

J
JJJ

L
L
LL

  L
LLL

LL
 L
 L

LLL
L

OO
OO

 SS
SS

S
SS
 S

TTT
 T

T
TT
T

 T
TTT

 T
TT
 T

ZZ
 ZZ

 Z
ZZ
Z

Вызов

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

Ведущие пустые области, присутствующие в некоторых из 19 фигур, полностью заполнены пробелами ( ). Конечные пустые области не заполнены ничем (поэтому программы не всегда точно прямоугольные).

пример

Предположим, это был ваш блок кода:

ABC
123

Тогда любое расположение блока в части S Tetris будет программой, которая печатает S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Обратите внимание, что все начальные пустые пробелы заполнены пробелами и что ни в одной строке нет пробелов.)

Та же самая идея относится ко всем 6 другим частям и их соответствующим вращениям.

Заметки

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

счет

Представление, кодовый блок которого имеет наименьшую площадь (ширину и высоту), выигрывает. По сути, это означает, что выигрывает самый короткий код, поэтому он помечен как . Tiebreaker идет на самый высокий голос .

ABC\n123Пример имеет площадь 3 × 2 = 6.

отрывок

С учетом блока кода этот фрагмент сгенерирует все 19 программ:

<script>function drawShape(X,n,v){for(var t="",e=0;e<v.length;e++)for(var l=0;l<n.length;l++){for(var r=0;r<v[e].length;r++)t+="X"===v[e][r]?n[l]:X[l];t+="\n"}return t}function go(){var X=document.getElementById("input").value;if(0!=X.length){var n=X.replace(/./g," ").split("\n");X=X.split("\n");for(var v="I (v1):|I (v2):|J (v1):|J (v2):|J (v3):|J (v4):|L (v1):|L (v2):|L (v3):|L (v4):|O:|S (v1):|S (v2):|T (v1):|T (v2):|T (v3):|T (v4):|Z (v1):|Z (v2):".split("|"),t="X\nX\nX\nX|XXXX| X\n X\nXX|XXX\n  X|XX\nX\nX|X\nXXX|X\nX\nXX|  X\nXXX|XX\n X\n X|XXX\nX|XX\nXX| XX\nXX|X\nXX\n X|XXX\n X|X\nXX\nX| X\nXXX| X\nXX\n X|XX\n XX| X\nXX\nX".split("|"),e="",l=0;l<v.length;l++)e+=v[l]+"\n\n"+drawShape(n,X,t[l].split("\n"))+"\n";e=e.substring(0,e.length-2),document.getElementById("output").value=e}}</script><style>html *{font-family: monospace;}</style>Code Block:<br><textarea id='input' rows='8' cols='64'>ABC&#010;123</textarea><br><button type='button' onclick='go()'>Go</button><br><br>All 19 Programs:<br><textarea id='output' rows='24' cols='64'></textarea>


То есть отношение длины к ширине составляет 2 к 3? Или это может быть любой другой размер? Кроме того, что должна делать программа, как минимум? Предполагается, что пустые программы не учитываются, но программы, которые ничего не выводят, делают.
ASCIIThenANSI

@ASCIIThenANSI Любая ширина и высота в порядке. Я предполагаю, что что-то большее, чем 2 * 3 будет необходимо. Есть 19 программ, по одной для каждого расположения блока в одну из 19 различных форм тетромино. Когда одна из этих программ запускается, она выводит соответствующую букву тетриса.
Увлечения Кэлвина

Вот Это Да! Какой потрясающий вызов! Имеет ли значение, какой язык мы используем?
theonlygusti

@theonlygusti Почти все вопросы на этом сайте разрешены на любом языке. Это не исключение.
Увлечения Кельвина

@ Calvin'sHobbies Да, я знаю; Я просто неверно истолковал этот фрагмент как контроллер для запуска JavaScript-ответов. Видимо, он просто размещает блоки кода.
theonlygusti

Ответы:


16

<> <(Рыба) - 12 * 32 = 384

Я планировал пойти на более элегантное решение, но я каким-то образом закончил с этим, что довольно грубо:

c  0  g84*%\
c2*0  g84*%\
0  84*g84*%\
c  84*g84*%\
c2*84*g84*%\
0  88*g84*%\
c  88*g84*%\
?v         \
;>?v~~?vv   
"L" o;  >   
"S" o; >~?v 
"T" o;    > 
;  >~?v"L"o 
;     >"J"o 
?v         \
 >~?v~~?vv  
"I"  o;  >  
"J"  o; >   
    \~~?vv  
"T"  o;  >  
"Z"  o; >   
?v         \
 >?v"J"o;   
   >?v"Z"o; 
"L"o;>?!v   
"J"o;   >?v 
"T"o;     > 
?v?v"I"o;  >
   >"L"o;   
 >?v"T"o;   
   >?v"O"o; 
     >"S"o; 

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

Попробуйте код здесь (после использования фрагмента, чтобы придать ему форму тетримино)

Пример кода в форме Z (v1) здесь


14

C (gcc) , 26x20 = 520 25x19 = 475 23x17 = 391

#ifndef M            //
#define M(a,b)a##b   //
#define W(z,x)M(z,x) //
char*s,*S[]={"!!!!c",//
"8M !7! M8 878","77",//
"7!!MO887","788OM!!7"//
,"N7!78","7N87!"},r[5//
],*p=r;i=7;main(){for//
(;i--;)strstr(S[i],r)//
&&putchar("ITOJLSZ"[i//
]);}                 //
#endif               //
__attribute__((      //
constructor(__LINE__)//
))W(f,__LINE__)(){s= //
"                     \
";*p++=strlen(s)+12;}//

Недавно мне сообщили об атрибутах функций GNU и, что наиболее интересно, об constructorатрибуте, который позволяет более кратко реализовать то, что я делал, более окольным путем в моем более раннем подходе к этой проблеме.

Суть идеи та же, что и раньше: создайте строку и найдите ее в списке, чтобы определить, в каком блоке тетриса выложен код. Это делается путем вызова функций, каждая из которых добавляет символ в строку. Осложнение было и остается, что количество функций варьируется.

Определение функции с помощью attribute((constructor(x)))делает так, чтобы функция запускалась до того, main()как была введена, с необязательным, xявляющимся приоритетом (ниже означает, что она запускается раньше). Это устраняет необходимость в указателях функций, что позволяет нам удалять макрос, некоторые объявления и цепочку вызовов.

Использование __LINE__для приоритета ненадежно, поскольку уровни приоритета 0-100 зарезервированы. Тем не менее, это не приводит к ошибкам, а только к предупреждениям, которых много в гольфе, так что еще?

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

Пример L v2 здесь

Старый, более портативный, подход

#ifndef M              //
#define M(a,b) a##b    //
#define W(z,x)M(z,x)   //
#define F W(f,__LINE__)//
#define A W(a,__LINE__)//
char r[5],*S[]={"####k"//
,";<<US##;",";##SU<<;",//
";;",";T<;#","<S #;# S"//
"< <;<","T;#;<"},*s,*p=//
r;i;typedef(*T)();T a17//
,a36,a55,a74;main(){for//
(a17(),a36&&a36(),a55&&//
a55(),a74&&a74();i--;) //
strstr(S[i],r)&&putchar//
("ILJOZTS"[i]);}i=7;   //
#endif                 //
F();T A=F;F(){s=       //
"                       \
";*p++=strlen(s)+12;}  //

Одна из моих любимых проблем, которую я решил на этом сайте.

Я начал с того, что подумал, что каждый блок будет как-то определять свои собственные координаты. С строками легко __LINE__работать, а количество горизонтально смежных блоков можно найти, используя длину строкового литерала, например, так:

char*s=//char*s=//
"       ""       "
;        ;        

Возьмите длину полученной строки и разделите на правильное число, и вы получите ширину. К сожалению, этот метод невидит любое пустое пространство перед блоком. Я до сих пор подозреваемые строка будет решением, так как пробельные имеет смысл только вне строк очень редко, в таких вещах , как a+++bпротив прогноза a+ ++b. Я кратко обдумал что-то подобное, но не смог придумать ничего полезного. Другой возможностью было бы позволить идентификаторам «склеиваться» там, где встречались блоки:

A  BA  B

Я не удивлюсь, если это все еще может сделать интересное решение.

Несмотря на свою простоту, мне потребовалось довольно много времени, чтобы найти решение для строки, основанное на этом фрагменте блока:

s=//
"  \
";//

Если фрагмент не имеет горизонтальных соседей, символ новой строки во второй строке экранируется обратной косой чертой, создавая строку длиной 2. Однако, если у него есть сосед, обратная косая черта вместо этого будет выходить из знака кавычки в начале строки. 2 следующего блока:

s=//s=//
"  \"  \
";//";//

Это создаст строку "\" "длиной 5.

Что еще более важно, это также позволяет обнаруживать пустое пространство перед блоком:

    s=//
    "  \
    ";//

Снова, новая строка экранируется, и пробел слева от пустого блока включается в результирующую строку "" длиной 6.

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

2 "  "
---
s=//
"  \
";//

5 "  \"  "
---
s=//s=//
"  \"  \
";//";//

6 "      "
---
    s=//
    "  \
    ";//

9 "  \"      "
----
    s=//s=//
    "  \"  \
    ";//";//

10 "          "
---
        s=//
        "  \
        ";//

8 "  \"  \"  "
---
s=//s=//s=//
"  \"  \"  \
";//";//";//

11 "  \"  \"  \"  "
----
s=//s=//s=//s=//
"  \"  \"  \"  \
";//";//";//";//

Конечные блоки, конечно, не будут иметь такой короткой длины, но принцип будет одинаковым независимо от размера блока. Это также имеет преимущество в том, что отдельный механизм для определения ширины не нужен. Добавляя символ, соответствующий длине этой строки, в строку результатов, каждая из 19 конфигураций дает уникальную строку, которую нужно сравнивать только с подходящим списком после запуска всех блоков.

Как только это было отсортировано, следующая большая проблема заключалась в том, как «посетить» каждый ряд блоков. В C мы очень ограничены тем, что можно сделать вне функций. Нам тоже нужно main()появиться, но только один раз. Последнее легко достигается некоторыми #defines, но если мы хотим, чтобы код последующих блоков был внутри main(), проблема в том, как узнать, когда ставить заключительную закрывающую фигурную скобку. В конце концов, мы не знаем, сколько строк блоков будет фактически использовано. Таким образом, мы должны быть main()статичными, а остальное - динамичным.

Если другие блочные строки должны быть автономными, они должны быть функциями, но мы должны убедиться, что каждая функция имеет уникальное имя, а также достаточно предсказуемое, чтобы его можно было вызывать main(). Нам также нужен механизм для того, чтобы знать, какие функции на самом деле должны быть вызваны. Генерация уникальных имен решается с помощью вспомогательных макросов:

#define M(a,b) a##b     //
#define W(z,x)M(z,x)    //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //

Вызов Fсоздаст идентификатор, имя которого начинается с f и заканчивается номером строки. Aделает то же самое, но с префиксом as, который используется для второй части решения - указателей на функции. Мы объявляем четыре таких указателя:

typedef(*T)();T a17,a36,a55,a74;

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

F();T A=F;F()

Сначала будет объявлена ​​функция, определен соответствующий указатель на функцию для указания на эту функцию (мы можем определить глобальные переменные только один раз, но более раннее объявление не считается как определение, даже если оно инициализировалось в NULL), а затем определим фактическую функция. Это позволяет main()вызывать любой указатель на функцию, который не равен NULL (a17 никогда не будет NULL):

a17(),a36&&a36(),a55&&a55(),a74&&a74()

В результате будет построена строка r, которую затем ищут в таблице строк, и, если она найдена, выводится соответствующая буква.

Единственная оставшаяся хитрость заключается в том, что список строк для сравнения сокращался всякий раз, когда можно было избежать неоднозначности или можно было бы совмещать перекрывающиеся строки.

Пример L v2 здесь


6

код операции x86 (.com), 86 82 байта

Tester:

org 100h
macro e {
db $F6,$04,$DF,$78,$13,$75,$08,$00,$C0,$40,$83,$C6,$52,$EB,$F1,$88
db $C2,$00,$D0,$00,$D0,$46,$EB,$E8,$05,$02,$40,$73,$ED,$E8,$26,$00
db $50,$08,$43,$4D,$2C,$0C,$1C,$15,$A5,$14,$10,$13,$3F,$27,$20,$0F
db $51,$1D,$29,$49,$49,$4A,$4A,$4A,$4A,$4C,$4C,$4C,$4C,$4F,$53,$53
db $54,$54,$54,$54,$5A,$5A,$5F,$AE,$75,$FD,$8A,$55,$12,$B4,$02,$CD
db $21,$C3
}

macro n { db 82 dup $20 }

macro s { db 10 }

n
e
s
n
e
s
e
e  

Источник:

BOF:    ;mov bx, 100h
p:      test [si], byte $DF
        js _a ; exist
        jnz _b ; newline
_z:     add al, al
        inc ax
q:      add si, EOF-BOF
        jmp p
_b:     mov dl, al
        add al, dl
        add al, dl
        inc si
        jmp p
_a:     add ax, 4002h
        jnc q
        call y
        db 80,8,67,77,44,12,28,21,165,20,16,19,63,39,32,15,81,29,41
        db 'IIJJJJLLLLOSSTTTTZZ'
y:      pop di
        scasb
        jnz y+1
        mov dl,[di+18]
        mov ah,2
        int $21
        ret
EOF:

Запустите в win7dos, где init AX = 0, SI = 100, BX = 0 Ссылки


Если вам удобно несколько уменьшить число поддерживаемых сред, вы можете принять SI = 100h и использовать этот регистр вместо BX для индексации, чтобы сэкономить 3 байта, отбрасывая их mov bx, 100hв начале.
gastropner

@gastropner Закончил и установил точку, где я не заметил
l4m2
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.