Lập trình khối Tetris (nghĩa đen)


33

Trong trò chơi Tetris , có 7 loại gạch hoặc Tetr i minoes , được biết đến dưới dạng toán học là tetr o minoes vì tất cả chúng đều được làm với 4 đoạn vuông:

Gạch Tetris

Các tên I, J, L, O, S, T và Z, tương ứng với hình dạng gần đúng của chúng. Đếm các góc quay 90 °, có tổng cộng 19 hình dạng độc đáo:

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

Thử thách

Viết một khối mã hình chữ nhật đóng vai trò là đoạn cơ sở mà 19 hình này được tạo từ. Khi mã này được sắp xếp thành một trong các hình dạng, một chương trình sẽ được hình thành để xuất ra chữ cái in hoa duy nhất được liên kết với hình dạng đó. Điều này phải làm việc cho tất cả 19 hình dạng.

Các khu vực trống hàng đầu hiện diện trong một số trong 19 hình được lấp đầy hoàn toàn bằng khoảng trắng ( ). Các khu vực trống ở cuối không chứa đầy bất cứ thứ gì (vì vậy các chương trình không phải lúc nào cũng chính xác là hình chữ nhật).

Thí dụ

Giả sử đây là khối mã của bạn:

ABC
123

Sau đó, việc sắp xếp khối vào mảnh S Tetris sẽ là một chương trình in S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Lưu ý rằng tất cả các khoảng trống hàng đầu đều chứa đầy các ký tự khoảng trắng và không có dòng nào có bất kỳ dấu cách nào.)

Ý tưởng tương tự áp dụng cho tất cả 6 mảnh khác và góc quay tương ứng của chúng.

Ghi chú

  • Tất cả 19 chương trình cuối cùng sẽ được chạy trong cùng một ngôn ngữ lập trình.
  • Nếu muốn, bạn có thể thêm một dòng mới duy nhất vào tất cả các chương trình (không chỉ một số, tất cả hoặc không).
  • Khối mã của bạn có thể chứa bất kỳ ký tự nào (bao gồm khoảng trắng) không phải là dấu kết thúc dòng .
  • Xuất thư đến thiết bị xuất chuẩn (hoặc ngôn ngữ thay thế gần nhất với ngôn ngữ của bạn) với một dòng mới tùy chọn.

Chấm điểm

Bài nộp có khối mã có diện tích nhỏ nhất (chiều rộng lần chiều cao) sẽ thắng. Điều này về cơ bản có nghĩa là chiến thắng mã ngắn nhất, đó là lý do tại sao điều này được gắn thẻ . Tiebreaker đi đến câu trả lời bình chọn cao nhất .

Các ABC\n123ví dụ có diện tích 3 × 2 = 6.

Đoạn trích

Đưa ra một khối mã, đoạn mã này sẽ tạo ra tất cả 19 chương trình:

<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>


Vậy tỷ lệ chiều dài-chiều rộng là 2 đến 3? Hoặc nó có thể là bất kỳ kích thước khác? Ngoài ra, chương trình phải làm gì, ở mức tối thiểu? Giả sử các chương trình trống không được tính, nhưng các chương trình không tạo ra gì.
ASCIIThenANSI

@ASCIIThenANSI Bất kỳ chiều rộng và chiều cao đều ổn. Tôi tưởng tượng một cái gì đó lớn hơn 2 * 3 sẽ là cần thiết. Có 19 chương trình, mỗi chương trình sắp xếp khối thành một trong 19 hình dạng tetromino riêng biệt. Khi một trong những chương trình đó được chạy, nó sẽ xuất ra chữ cái tetris tương ứng.
Sở thích của Calvin

Ồ Thật là một thử thách tuyệt vời! Có vấn đề gì ngôn ngữ chúng ta sử dụng?
theonlygusti

@theonlygusti Hầu như tất cả các câu hỏi trên trang web này cho phép mọi ngôn ngữ. Điều này cũng không ngoại lệ.
Sở thích của Calvin

@ Calvin'sHob sở thích Vâng, tôi biết; Tôi chỉ hiểu sai đoạn mã là bộ điều khiển để chạy các câu trả lời JavaScript. Rõ ràng nó chỉ sắp xếp các khối mã.
theonlygusti

Câu trả lời:


16

<> <(Cá) - 12 * 32 = 384

Tôi đã lên kế hoạch cho một giải pháp thanh lịch hơn, nhưng bằng cách nào đó tôi đã kết thúc với điều này, đó là sức mạnh vũ phu:

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; 

Nó khá đơn giản, nó kiểm tra mã trong một hình vuông 3x3 cho văn bản và sử dụng các kết quả để xem tetrimino nào tương ứng với hình dạng của mã. Tôi chưa mất nhiều công sức để chơi golf.

Hãy thử mã ở đây (sau khi sử dụng đoạn mã để định hình nó giống như một tetrimino)

Ví dụ về mã trong hình Z (v1) tại đây


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

Gần đây tôi đã được thông báo về các thuộc tính chức năng của GNU và điều thú vị nhất là constructorthuộc tính này, cho phép thực hiện ngắn gọn hơn những gì tôi đang làm theo cách vòng vo hơn trong cách tiếp cận vấn đề trước đây của tôi.

Lực đẩy của ý tưởng vẫn giống như trước: Xây dựng một chuỗi và tìm kiếm nó trong một danh sách để xác định khối tetris nào mà mã được đặt ra là. Điều này được thực hiện bằng cách gọi các hàm, mỗi hàm thêm một ký tự vào chuỗi. Sự phức tạp đã và vẫn là số lượng các chức năng khác nhau.

Xác định một chức năng attribute((constructor(x)))làm cho nó để chức năng được chạy trước main()được nhập vào, với tùy chọn xlà ưu tiên (thấp hơn có nghĩa là nó được chạy trước đó). Điều này loại bỏ sự cần thiết của các con trỏ hàm, cho phép chúng ta bỏ một macro, một số khai báo và chuỗi gọi.

Sử dụng __LINE__cho ưu tiên là iffy, vì mức độ ưu tiên 0-100 được bảo lưu. Tuy nhiên, nó không dẫn đến lỗi, chỉ có cảnh báo và những thứ đó rất phong phú khi chơi golf, vậy còn một vài điều nữa?

Nó sẽ giúp loại bỏ một cột khác để không sử dụng các ưu tiên, nhưng thứ tự thực hiện dường như không được xác định. (Chúng được đảo ngược trong trường hợp này, nhưng các xét nghiệm khác là không kết luận.)

Ví dụ về L v2 tại đây

Cũ hơn, di động hơn, cách tiếp cận

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

Một trong những vấn đề yêu thích của tôi đã được giải quyết trên trang web này.

Tôi bắt đầu bằng cách hình dung mỗi khối sẽ thần thánh tọa độ của nó bằng cách nào đó. Các hàng dễ dàng với __LINE__và có thể tìm thấy số khối liền kề theo chiều ngang bằng cách sử dụng độ dài của một chuỗi bằng chữ, như vậy:

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

Lấy độ dài của chuỗi kết quả và chia cho một số thích hợp và bạn có chiều rộng. Đáng buồn thay, bất kỳ không gian trống trước khối là vô hình bằng phương pháp này. Tôi vẫn nghi ngờ dây sẽ là giải pháp, vì khoảng trắng chỉ có ý nghĩa bên ngoài của dây rất hiếm khi, trong những thứ như a+++bvs a+ ++b. Tôi đã xem xét ngắn gọn một cái gì đó như thế, nhưng không thể đưa ra bất cứ điều gì hữu ích. Một khả năng khác có thể là để cho các định danh được "dán" lại với nhau nơi các khối gặp nhau:

A  BA  B

Tôi sẽ không ngạc nhiên nếu điều này vẫn có thể tạo ra một giải pháp thú vị.

Mặc dù đơn giản, tôi đã mất khá nhiều thời gian để tìm giải pháp chuỗi, dựa trên đoạn khối này:

s=//
"  \
";//

Nếu đoạn không có hàng xóm nằm ngang, dòng mới trên dòng thứ hai được thoát bằng dấu gạch chéo ngược, tạo ra một chuỗi có độ dài 2. Tuy nhiên, nếu nó có hàng xóm, thì dấu gạch chéo ngược sẽ thoát khỏi dấu quotion ở đầu dòng 2 khối tiếp theo:

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

Điều này sẽ tạo ra chuỗi "\" "có độ dài 5.

Quan trọng hơn, điều này cũng cho phép phát hiện không gian trống trước khối:

    s=//
    "  \
    ";//

Một lần nữa, dòng mới được thoát và khoảng trắng của khối trống ở bên trái được bao gồm trong chuỗi kết quả "" có độ dài 6.

Tổng cộng có bảy cấu hình khác nhau của các khối trên một hàng mà chúng ta cần phải lo lắng và tất cả chúng đều tạo ra các chuỗi có độ dài duy nhất:

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

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

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

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

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

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

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

Các khối cuối cùng tất nhiên sẽ không có chiều dài ngắn như vậy, nhưng nguyên tắc là như nhau bất kể kích thước khối. Điều này cũng có phần thưởng là một cơ chế riêng biệt để phát hiện chiều rộng là không cần thiết. Bằng cách thêm một ký tự tương ứng với độ dài của chuỗi này vào chuỗi kết quả, mỗi cấu hình trong số 19 cấu hình sẽ tạo ra một chuỗi duy nhất, chỉ cần so sánh với một danh sách phù hợp khi tất cả các khối đã được chạy.

Khi điều này đã được sắp xếp, vấn đề lớn tiếp theo là làm thế nào để "truy cập" từng hàng khối. Trong C, chúng tôi rất hạn chế những gì có thể được thực hiện bên ngoài các chức năng. Chúng tôi cũng cần main()phải xuất hiện, nhưng chỉ một lần. Cái sau có thể dễ dàng đạt được bởi một số #defines, nhưng nếu chúng ta muốn mã của các khối tiếp theo nằm bên trong main(), thì vấn đề là làm thế nào để biết khi nào nên đặt dấu ngoặc nhọn đóng cuối cùng. Rốt cuộc, chúng ta không biết có bao nhiêu hàng khối sẽ thực sự được sử dụng. Vì vậy, chúng ta cần phải có main()tĩnh và bằng cách nào đó phần còn lại để được năng động.

Nếu các hàng khối khác phải khép kín, chúng cần phải là các hàm, nhưng chúng ta cần đảm bảo mỗi hàm có một tên duy nhất, trong khi cũng có thể dự đoán đủ để có thể gọi được main(). Chúng ta cũng cần một cơ chế để biết chức năng nào thực sự được gọi. Tạo tên duy nhất được giải quyết bằng macro trợ giúp:

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

Gọi Fsẽ tạo một định danh có tên bắt đầu bằng một f và kết thúc bằng số dòng. Athực hiện tương tự nhưng với tiền tố là, được sử dụng cho phần thứ hai của giải pháp, đó là các con trỏ hàm. Chúng tôi tuyên bố bốn con trỏ như vậy:

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

Vì chúng được khai báo là biến toàn cục, nên chúng được đặt thuận tiện thành NULL. Sau đó, mỗi hàng khối sẽ có đoạn mã sau:

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

Điều này trước tiên sẽ khai báo một hàm, xác định con trỏ hàm thích hợp để trỏ đến hàm đó (chúng ta chỉ có thể xác định toàn cục một lần, nhưng khai báo trước đó không được tính là một định nghĩa, ngay cả khi nó đã khởi tạo cho NULL), sau đó xác định thực tế chức năng. Điều này cho phép main()gọi bất kỳ con trỏ hàm nào không phải là NULL (a17 sẽ không bao giờ là NULL):

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

Làm như vậy sẽ xây dựng chuỗi r, sau đó được tìm kiếm trong bảng chuỗi và nếu tìm thấy, chữ cái thích hợp là đầu ra.

Thủ thuật duy nhất còn lại là danh sách các chuỗi cần so sánh được rút ngắn bất cứ khi nào có thể tránh được sự mơ hồ hoặc các chuỗi chồng chéo có thể bị xáo trộn.

Ví dụ về L v2 tại đây


6

op86 x86 (.com), 86 82 byte

Người kiểm tra:

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  

Nguồn:

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:

Chạy trong win7dos trong đó init AX = 0, SI = 100, BX = 0 Tài liệu tham khảo


Nếu bạn cảm thấy thoải mái khi giảm số lượng môi trường được hỗ trợ, bạn có thể giả sử SI = 100h và sử dụng thanh ghi đó thay vì BX để lập chỉ mục, để tiết kiệm 3 byte bằng cách bỏ mov bx, 100hkhi bắt đầu.
dạ dày

@gastropner Xong và sửa một điểm mà tôi không nhận thấy
l4m2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.