テトリスブロックのプログラミング(文字通り)


33

ゲームTetrisには、7種類のレンガまたはTetr i minoesがあり、それらはすべて4つの正方形セグメントで作られているため、数学的にtetr o minoesとして知られています。

テトリスレンガ

これらの名前は、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テトリスのピースに配置すると、次のように出力されるプログラムになります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個の異なるテトロミノ形状の1つにブロックを配置するごとに1つあります。これらのプログラムのいずれかが実行されると、対応するテトリスのピースレターが出力されます。
カルビンの趣味

うわー!なんて素晴らしいチャレンジでしょう!使用する言語は重要ですか?
-theonlygusti

@theonlygustiこのサイトのほとんどすべての質問は、あらゆる言語に対応しています。これも例外ではありません。
カルバンの趣味

@ Calvin'sHobbiesええ、知っています。このスニペットをJavaScript-answersを実行するためのコントローラーと誤解しただけです。どうやらコードブロックを配置するだけです。
-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が予約されているため、優先度の使用はiffyです。ただし、エラーは発生せず、警告のみが表示され、ゴルフをするときには警告が豊富に表示されます。

優先順位をまったく使用しないように別の列を削るのに役立ちますが、実行順序は定義されていないようです。(この場合は逆になりますが、他のテストは決定的ではありません。)

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;}  //

このサイトで解決した私のお気に入りの問題の1つ。

私は、各ブロックが何らかの形でそれ自身の座標を占領することを理解することから始めました。行はを使用すると簡単__LINE__になり、水平方向に隣接するブロックの数は、次のように文字列リテラルの長さを使用して見つけることができます。

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

結果の文字列の長さを取得し、適切な数で割ると幅がわかります。悲しいことに、ブロックの前の空のスペースはこのメソッドでは見えません。a+++bvs. などのように、空白は文字列の外側でしか意味を持たないため、文字列が解決策であるとまだ疑っていますa+ ++b。私はそのようなことを簡単に考えましたが、有用なものを思い付くことができませんでした。別の可能性は、ブロックが満たされた場所で識別子を「接着」することでした。

A  BA  B

これがまだ興味深い解決策をもたらすことができれば、私は驚かないでしょう。

その単純さにもかかわらず、このブロックフラグメントに基づく文字列ソリューションを見つけるのにかなり時間がかかりました。

s=//
"  \
";//

フラグメントに水平方向の隣接がない場合、2行目の改行はバックスラッシュによってエスケープされ、長さ2の文字列が作成されます。ただし、隣接がある場合、バックスラッシュは代わりに行頭の引用符をエスケープします次のブロックの2:

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

これにより、長さ5の文字列 "\" "が作成されます。

さらに重要なことは、これにより、ブロックの前の空きスペースの検出も可能になることです。

    s=//
    "  \
    ";//

繰り返しますが、改行はエスケープされ、左側の空のブロックの空白は結果の文字列「」の長さ6に含まれます。

合計で7つの異なるブロック構成があり、心配する必要があり、それらはすべて一意の長さの文字列を作成します。

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

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

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

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

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

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

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

もちろん、最終ブロックの長さはそれほど短くありませんが、原理はブロックサイズに関係なく同じです。これには、幅を検出するための別のメカニズムが不要であるというボーナスもあります。この文字列の長さに対応する文字を結果文字列に追加することにより、19の構成のそれぞれが一意の文字列を生成し、すべてのブロックが実行された後に適切なリストと比較するだけで済みます。

これがソートされると、次の大きな問題は、ブロックの各行を「訪問」する方法でした。Cでは、関数の外で実行できることに非常に限定されています。またmain()、表示する必要がありますが、一度だけです。後者はいくつか#defineのsで簡単に実現できますが、後続のブロックのコードをの中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プレフィックスを使用します。これは、ソリューションの2番目の部分である関数ポインターに使用されます。このような4つのポインターを宣言します。

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

これらはグローバル変数として宣言されているため、便利にNULLに設定されます。その後、各ブロック行には次のコードが含まれます。

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

これは最初に関数を宣言し、その関数を指す適切な関数ポインターを定義します(グローバルを1回しか定義できませんが、以前の宣言は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の代わりにそのレジスタを使用mov bx, 100hして、開始時にドロップすることで3バイトを節約できます。
ガストロプナー

@gastropner完了し、気づかなかった点を修正しました
l4m2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.