W grze Tetris istnieje 7 rodzajów klocków lub Tetr i minoes , które są matematycznie znane jako tetr o minoes, ponieważ wszystkie są wykonane z 4 kwadratowych segmentów:
Posiadają nazwy I, J, L, O, S, T i Z, które odpowiadają ich przybliżonym kształtom. Przy obrotach 90 ° istnieje łącznie 19 unikalnych kształtów:
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
Wyzwanie
Napisz prostokątny blok kodu, który działa jak segment podstawowy, z którego wykonano 19 kształtów. Kiedy ten kod jest ułożony w jeden z kształtów, należy utworzyć program, który wyprowadza pojedynczą wielką literę związaną z tym kształtem. To musi działać dla wszystkich 19 kształtów.
Wiodące puste obszary obecne w niektórych z 19 kształtów są całkowicie wypełnione spacjami ( ). Końcowe puste obszary nie są niczym wypełnione (więc programy nie zawsze są dokładnie prostokątne).
Przykład
Załóżmy, że to był twój blok kodu:
ABC
123
Wówczas każde ustawienie bloku w kawałku S Tetris byłoby programem, który wypisuje S
:
ABCABC
123123
ABCABC
123123
ABC
123
ABCABC
123123
ABC
123
(Zauważ, że wszystkie wiodące puste miejsca są wypełnione znakami spacji i że żadne wiersze nie mają spacji końcowych).
Ten sam pomysł dotyczy wszystkich 6 innych elementów i ich odpowiednich obrotów.
Notatki
- Wszystkie 19 programów końcowych ma być uruchomionych w tym samym języku programowania.
- W razie potrzeby możesz dodać jeden końcowy znak nowej linii do wszystkich programów (nie tylko niektóre, wszystkie lub żadne).
- Blok kodu może zawierać dowolne znaki (w tym spacje), które nie są zakończeniami linii .
- Wypisz literę na standardowe wyjście (lub najbliższą alternatywę twojego języka) z opcjonalnym końcowym znakiem nowej linii.
Punktacja
Zgłoszenie, którego blok kodu ma najmniejszy obszar (szerokość razy wysokość) wygrywa. Zasadniczo oznacza to, że wygrywa najkrótszy kod i dlatego jest oznaczony kodem golfowym . Tiebreaker przechodzi do najlepiej głosowanej odpowiedzi.
ABC\n123
Przykład ma powierzchnię 3 x 2 = 6.
Skrawek
Biorąc pod uwagę blok kodu, ten fragment kodu wygeneruje wszystkie 19 programów:
<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
123</textarea><br><button type='button' onclick='go()'>Go</button><br><br>All 19 Programs:<br><textarea id='output' rows='24' cols='64'></textarea>
źródło
Odpowiedzi:
<> <(Ryby) - 12 * 32 = 384
Planowałem wybrać bardziej eleganckie rozwiązanie, ale jakoś skończyłem z tym, co jest dość brutalną siłą:
Jest to dość proste, sprawdza kod w kwadracie 3x3 pod kątem tekstu i wykorzystuje wyniki, aby zobaczyć, który tetrimino odpowiada kształtowi kodu. Jeszcze nie starałem się grać w golfa.
Wypróbuj kod tutaj (po użyciu fragmentu w celu ukształtowania go jak tetrimino)
Przykład kodu w kształcie Z (v1) tutaj
źródło
C (gcc) ,
26x20 = 52025x19 = 47523x17 = 391Niedawno zostałem poinformowany o atrybutach funkcji GNU, a co najciekawsze, o
constructor
atrybucie, który pozwala na bardziej zwięzłą implementację tego, co robiłem w bardziej okrągły sposób w moim wcześniejszym podejściu do tego problemu.Idea tego samego pomysłu jest taka sama jak poprzednio: Zbuduj ciąg i wyszukaj go na liście, aby określić, który blok tetris jest ułożony jako kod. Odbywa się to przez wywołanie funkcji, z których każda dodaje znak do łańcucha. Powikłaniem było i pozostaje, że liczba funkcji jest różna.
Zdefiniowanie funkcji za pomocą
attribute((constructor(x)))
powoduje, że funkcja jest uruchamiana przedmain()
wprowadzeniem, z opcjonalnymx
priorytetem (niższy oznacza, że jest uruchamiany wcześniej). Eliminuje to potrzebę używania wskaźników funkcji, co pozwala nam upuszczać makro, niektóre deklaracje i łańcuch wywołań.Użycie
__LINE__
priorytetu jest niepewne, ponieważ poziomy priorytetu 0–100 są zarezerwowane. Nie powoduje to jednak błędów, a jedynie ostrzeżenia, które są obfite podczas gry w golfa, więc co jeszcze?Pomogłoby to ogolić kolejną kolumnę, aby w ogóle nie używać priorytetów, ale kolejność wykonywania nie wydaje się być określona. (W tym przypadku są one odwrócone, ale inne testy są niejednoznaczne).
Przykład L v2 tutaj
Starsze, bardziej przenośne podejście
Jeden z moich ulubionych problemów, które rozwiązałem na tej stronie.
Zacząłem od stwierdzenia, że każdy blok w jakiś sposób wyznaczy własne współrzędne. Rzędy są łatwe
__LINE__
, a liczbę sąsiadujących poziomo bloków można znaleźć, używając długości literału łańcuchowego, tak:Weź długość wynikowego łańcucha i podziel przez odpowiednią liczbę, a uzyskasz szerokość. Niestety ta pusta przestrzeń przed blokiem jest niewidoczna. Wciąż podejrzewa ciągi byłoby rozwiązanie, ponieważ odstępy tylko ma sens poza strun bardzo rzadko, w rzeczy jak
a+++b
vs.a+ ++b
. Przez chwilę zastanawiałem się nad czymś takim, ale nie mogłem znaleźć niczego przydatnego. Inną możliwością byłoby umożliwienie „sklejenia” identyfikatorów w miejscu, w którym spotykają się bloki:Nie zdziwiłbym się, gdyby to wciąż mogło stanowić ciekawe rozwiązanie.
Pomimo swojej prostoty zajęło mi sporo czasu znalezienie rozwiązania łańcuchowego, które opiera się na tym fragmencie bloku:
Jeśli fragment nie ma poziomych sąsiadów, nowa linia w drugiej linii jest poprzedzana ukośnikiem odwrotnym, tworząc ciąg długości 2. Jeśli jednak ma sąsiad, odwrotny ukośnik zamiast tego unika znaku cudzysłowu na początku linii 2 następnego bloku:
Spowoduje to utworzenie ciągu „\” „o długości 5.
Co ważniejsze, pozwala to również na wykrycie pustej przestrzeni przed blokiem:
Znów znak nowej linii jest zastępowany, a spacja pustego bloku po lewej stronie jest zawarta w wynikowym ciągu „” o długości 6.
W sumie istnieje siedem różnych konfiguracji bloków w rzędzie, o które musimy się martwić, i wszystkie tworzą ciągi o unikalnej długości:
Ostateczne bloki oczywiście nie będą miały tak krótkiej długości, ale zasada jest taka sama niezależnie od wielkości bloku. Ma to również tę zaletę, że oddzielny mechanizm wykrywania szerokości jest niepotrzebny. Dodając znak odpowiadający długości tego ciągu do ciągu wyników, każda z 19 konfiguracji daje unikalny ciąg, który wystarczy porównać z odpowiednią listą po uruchomieniu wszystkich bloków.
Po posortowaniu, kolejnym dużym problemem było to, jak „odwiedzić” każdy rząd bloków. W C jesteśmy bardzo ograniczeni do tego, co można zrobić poza funkcjami. Musimy się także
main()
pojawić, ale tylko raz. To drugie jest łatwo osiągalne przez niektóre#define
s, ale jeśli chcemy, aby kod kolejnych bloków był w środkumain()
, problem, jak się dowiedzieć, kiedy umieścić końcowy nawias zamykający. W końcu nie wiemy, ile wierszy bloków faktycznie zostanie wykorzystanych. Musimy więc miećmain()
statyczne, a reszta być dynamiczna.Jeśli pozostałe wiersze bloków mają być samowystarczalne, muszą to być funkcje, ale musimy upewnić się, że każda funkcja ma unikalną nazwę, a jednocześnie jest wystarczająco przewidywalna, aby można ją było wywoływać
main()
. Potrzebujemy również mechanizmu rozpoznawania, które funkcje są rzeczywiście wywoływane. Generowanie unikalnych nazw rozwiązują makra pomocnicze:Wywołanie
F
spowoduje utworzenie identyfikatora, którego nazwa zaczyna się na literę f, a kończy na numerze linii.A
robi to samo, ale z prefiksem as, który jest używany dla drugiej części rozwiązania, czyli wskaźników funkcji. Deklarujemy cztery takie wskaźniki:Ponieważ są one zadeklarowane jako zmienne globalne, wygodnie są ustawione na NULL. Później każdy wiersz bloku będzie zawierał następujący fragment kodu:
To najpierw zadeklaruje funkcję, zdefiniuje odpowiedni wskaźnik funkcji wskazujący na tę funkcję (możemy zdefiniować globały tylko raz, ale wcześniejsza deklaracja nie liczyła się jako definicja, nawet jeśli zainicjowała wartość NULL), a następnie zdefiniowała rzeczywistą funkcjonować. Pozwala
main()
to wywołać dowolny wskaźnik funkcji, który nie jest NULL (a17 nigdy nie będzie NULL):Spowoduje to zbudowanie ciągu znaków
r
, który jest następnie szukany w tabeli ciągów i jeśli zostanie znaleziony, wyświetlana jest odpowiednia litera.Jedyną sztuczką jest to, że lista pasujących ciągów znaków została skrócona, ilekroć można było uniknąć dwuznaczności, lub nakładających się łańcuchów można połączyć.
Przykład L v2 tutaj
źródło
x86 opcode (.com),
8682 bajtówPróbnik:
Źródło:
Uruchom w win7dos, gdzie init AX = 0, SI = 100, BX = 0 Referencje
źródło
mov bx, 100h
na początku.