Програмування блоків тетрісу (буквально)


33

У грі Тетріс є 7 типів цегли або тетр я minoes , які математично відомі як Tetr про 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 кінцевих програм мають виконуватися однією і тією ж мовою програмування.
  • За бажанням, ви можете додати один простий новий рядок до всіх програм (не лише деяких, всіх або жодних).
  • Ваш код коду може містити будь-які символи (включаючи пробіли), які не є строковими термінаторами .
  • Виведіть лист у stdout (або найближча альтернатива вашої мови) за допомогою додаткового зворотного рядка.

Оцінка балів

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

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; 

Це досить просто, він перевіряє код у квадраті 3х3 на текст і використовує результати, щоб побачити, який тетриміно відповідає формі коду. Я ще не доклав багато зусиль, щоб пограти в нього.

Спробуйте тут код (після використання фрагмента, щоб сформувати його як тетриміно)

Приклад коду у формі 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робить те ж саме, але з префіксом, який використовується для другої частини рішення, яка є покажчиками функції. Ми оголошуємо чотири таких покажчики:

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 байт

Тестер:

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 Виконано та зафіксовано точку, де я не помітив
l4m2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.