การเขียนโปรแกรมบล็อก Tetris (ตามตัวอักษร)


33

ในเกมTetrisมี 7 ชนิดของอิฐหรือTetr i minoesซึ่งเป็นคณิตศาสตร์ที่รู้จักกันในชื่อtetr o minoesเพราะมันถูกสร้างขึ้นด้วย 4 ส่วนเซ็กเมนต์:

อิฐ Tetris

มีชื่อ 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 (หรือทางเลือกที่ใกล้เคียงที่สุดในภาษาของคุณ) ด้วยการขึ้นบรรทัดใหม่ที่เป็นตัวเลือก

เกณฑ์การให้คะแนน

การส่งที่มีรหัสบล็อกมีพื้นที่น้อยที่สุด (ความสูงคูณความกว้าง) ชนะ นี้เป็นหลักหมายถึงการที่สั้นที่สุดชนะรหัสซึ่งเป็นเหตุผลที่นี้คือแท็กรหัสกอล์ฟ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 รูปทรง Tetricino ที่แตกต่างกัน เมื่อหนึ่งในโปรแกรมเหล่านั้นถูกรันมันจะส่งเอาต์พุตตัวอักษร tetris ที่สอดคล้องกัน
งานอดิเรกของ Calvin

ว้าว! ช่างเป็นความท้าทายที่ยอดเยี่ยม! เราใช้ภาษาอะไรกันแน่?
theonlygusti

@theonlygusti คำถามเกือบทั้งหมดในเว็บไซต์นี้อนุญาตให้ใช้ภาษาใดก็ได้ ไม่มีข้อยกเว้น
งานอดิเรกของ Calvin

@ งานอดิเรกของ Calvin ใช่ฉันรู้ ฉันเพิ่งตีความผิดในฐานะผู้ควบคุมการรันคำตอบ 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 สำหรับข้อความและใช้ผลลัพธ์เพื่อดูว่า tetrimino ที่สอดคล้องกับรูปร่างของรหัส ฉันยังไม่ได้พยายามเล่นกอล์ฟมากนัก

ลองรหัสที่นี่ (หลังจากใช้ตัวอย่างเพื่อกำหนดรูปร่างเหมือน tetrimino)

ตัวอย่างรหัสในรูปร่าง 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+++ba+ ++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()แบบคงที่และส่วนที่เหลือจะเป็นแบบไดนามิก

หากบล็อกแถวอื่น ๆ จะเป็นตนเองมีพวกเขาจะต้องทำงาน แต่เราต้องทำให้แน่ใจว่าแต่ละฟังก์ชั่นมีชื่อที่เป็นเอกลักษณ์ขณะที่ยังเป็นที่คาดการณ์ได้มากพอที่จะ callable 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()

สิ่งนี้จะประกาศฟังก์ชันแรกกำหนดตัวชี้ฟังก์ชันที่เหมาะสมเพื่อชี้ไปยังฟังก์ชันนั้น (เราสามารถกำหนด globals ได้ครั้งเดียวเท่านั้น แต่การประกาศก่อนหน้านี้ไม่นับเป็นคำจำกัดความแม้ว่าจะได้กำหนดเป็น NULL) แล้วกำหนดจริง ฟังก์ชัน สิ่งนี้อนุญาตให้main()เรียกตัวชี้ฟังก์ชันใด ๆ ที่ไม่ใช่ค่า NULL (a17 จะไม่เป็น NULL):

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

การทำเช่นนั้นจะสร้างสตริงrซึ่งจะค้นหาในตารางของสตริงและหากพบตัวอักษรที่เหมาะสมจะถูกส่งออก

กลอุบายที่เหลืออยู่เพียงอย่างเดียวคือรายการของสตริงที่จะเทียบกับถูกย่อให้สั้นลงเมื่อใดก็ตามที่สามารถหลีกเลี่ยงความกำกวมหรือสตริงที่ทับซ้อนกันอาจถูกแฟล็ก

ตัวอย่างของ L v2 ที่นี่


6

x86 opcode (.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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.