Lời khuyên cho việc chơi golf trong brainfuck


23

Bạn có lời khuyên chung nào cho việc chơi golf trong brainfuck? Tôi đang tìm kiếm những ý tưởng có thể được áp dụng cho các vấn đề về golf nói chung ít nhất là cụ thể đối với brainfuck (ví dụ: "xóa bình luận" không phải là một câu trả lời). Xin vui lòng gửi một lời khuyên cho mỗi câu trả lời.

Câu trả lời:


25

Đặt một mẹo cho mỗi câu trả lời sẽ là quá nhiều câu trả lời.

  • Học cách suy nghĩ trong Brainfuck. Nó rất khác so với mọi thứ khác. Đọc và viết, viết lại và viết lại rất nhiều chương trình brainfuck. Ngôn ngữ không mang lại cho bạn nhiều thứ để làm việc, vì vậy điều quan trọng là sử dụng những gì nó mang lại cho bạn một cách linh hoạt và hiệu quả. Đừng để bất kỳ sự trừu tượng nào có được giữa bạn và ngôn ngữ - hãy vào đó và vật lộn với nó.

  • Rất thoải mái với kiểm soát dòng chảy không phá hủy. Để thoát khỏi vòng lặp quyết định, thay vì bỏ ô bắt đầu bằng cách sao chép nó ở nơi khác và sau đó sao chép lại sau khi rời khỏi vòng lặp, tốt hơn hết là di chuyển con trỏ đến số 0 tồn tại gần đó. Đúng, điều này có nghĩa là con trỏ sẽ ở những nơi khác nhau tùy thuộc vào việc bạn có đi qua vòng lặp hay không, nhưng điều đó cũng có nghĩa là những nơi đó có thể có sự sắp xếp khác nhau của các số 0 và số khác gần đó, mà bạn có thể sử dụng để gắn lại vị trí con trỏ bằng một vòng lặp khác. Kỹ thuật này là nền tảng để lập trình Brainfuck tốt, và các hình thức khác nhau của nó sẽ liên tục chứng minh hữu ích.

  • Điều đó và thực tế là mọi >hoặc< chi phí có nghĩa là các chi tiết về bố trí bộ nhớ đều quan trọng. Hãy thử nhiều biến thể của bố cục của bạn như bạn có kiên nhẫn. Và hãy nhớ rằng, bố cục bộ nhớ của bạn không nhất thiết phải là ánh xạ dữ liệu cứng nhắc đến các vị trí. Nó có thể biến hình trong quá trình thực hiện.

  • Ở quy mô lớn hơn, hãy xem xét và thậm chí thử thực hiện nhiều thuật toán khác nhau. Ban đầu, nó sẽ không rõ ràng chính xác thuật toán nào sẽ là tốt nhất; thậm chí có thể không rõ ràng cách tiếp cận cơ bản nào sẽ tốt nhất, và nó có thể sẽ là một cái gì đó khác với cách tốt nhất trong ngôn ngữ bình thường.

  • Nếu bạn đang xử lý dữ liệu lớn hoặc có kích thước thay đổi, hãy xem liệu có cách nào bạn có thể xử lý dữ liệu cục bộ không mà không cần phải theo dõi mức độ lớn của nó hoặc vị trí số của bạn trong đó.

  • Cùng một dữ liệu có thể là hai điều khác nhau. (Thông thường, một số hoặc ký tự và cũng là một điểm đánh dấu vị trí khác không. Nhưng hãy xem Random.b , trong đó một bộ đếm bit nhân đôi giá trị của một ô của một máy tự động di động.)

  • Cùng một mã có thể thực hiện hai điều khác nhau và việc thực hiện mã đó dễ dàng hơn rất nhiều trong ngôn ngữ mà mã chung chung như <+<. Hãy cảnh giác với những khả năng như vậy. Trên thực tế, đôi khi bạn có thể nhận thấy, ngay cả trong những gì dường như là một chương trình được viết tốt, rằng có những phần nhỏ có thể bị xóa hoàn toàn, không có gì được thêm vào, và điều đó, bằng cách ngẫu nhiên, vẫn chạy hoàn hảo.

  • Trong hầu hết các ngôn ngữ, bạn thường xuyên sử dụng trình biên dịch hoặc trình thông dịch để kiểm tra hành vi của chương trình. Ngôn ngữ Brainfuck đòi hỏi sự kiểm soát khái niệm lớn hơn; nếu bạn cần một trình biên dịch để cho bạn biết chương trình của bạn làm gì, bạn không nắm vững chương trình của mình và có lẽ bạn cần nhìn chằm chằm vào nó nhiều hơn - ít nhất là nếu bạn muốn có một hình ảnh đủ rõ ràng về hào quang khái niệm của các chương trình tương tự để giỏi golf. Với thực tế, bạn sẽ tạo ra một tá phiên bản chương trình của mình trước khi bạn thử chạy một phiên bản và đến thời điểm đó, bạn sẽ chắc chắn 95% rằng phiên bản ngắn nhất của bạn sẽ chạy chính xác.

  • Chúc may mắn! Rất ít người bận tâm viết Brainfuck một cách chính xác, nhưng tôi nghĩ đó là cách duy nhất mà ngôn ngữ có thể biện minh cho sự chú ý tiếp tục - như một hình thức nghệ thuật tối nghĩa tuyệt đẹp.


3
Đọc điều này khiến tôi muốn thử lập trình trong Brainfuck ngay bây giờ ..
Claudiu

Holy shit, nghĩ rằng tôi nhận ra tên của bạn. Fan hâm mộ lớn của các chương trình brainfuck của bạn, đặc biệt là trình thông dịch tự siêu ngắn của bạn!
Jo King

"đôi khi bạn có thể nhận thấy ... rằng có những phần nhỏ có thể bị xóa hoàn toàn, không có gì được thêm vào, và điều đó, bằng cách ngẫu nhiên, vẫn chạy hoàn hảo." Điều này rất đúng, đặc biệt đối với người mới bắt đầu. Tôi chỉ từng viết một câu trả lời trong BF theo như tôi có thể nhớ, nhưng lịch sử sửa đổi của nó chứa ít nhất 3 trường hợp tôi đang chơi với chương trình và ngẫu nhiên đi, "này, chương trình vẫn hoạt động nếu tôi gỡ bỏ bit này! "
Sản phẩm ETH

"Hãy thử nhiều biến thể bố cục của bạn như bạn có kiên nhẫn," hoặc có thể vũ phu : P
Esolanging Fruit 21/2/18

9

Một vài lời khuyên ở đây:

Hằng số:

Các trang hằng Esolangs' có một danh sách rất hữu ích trong những cách nhanh nhất để tạo ra giá trị cụ thể. Tôi thấy mình tư vấn trang này ít nhất hai lần mỗi chương trình.

Sự khởi đầu của mọi thứ:

+++[[<+>>++<-]>]

Điều này thiết lập băng ở định dạng 3 * n ^ 2, trông giống như

3 6 12 24 48 96 192 128 0 0 '

Tại sao cái này lại quan trọng đến vậy?

Hãy đi xuống danh sách:

  • 3 và 6 là nhàm chán
  • 12: Gần đến 10 (dòng mới) hoặc 13 (trở về vận chuyển). Cũng có thể được sử dụng cho quầy cho 0-9
  • 24: Gần 26, số chữ cái trong bảng chữ cái
  • 48: ASCII cho 0
  • 96: Gần 97, ASCII cho a
  • 196 và 128: 196-128 = 64, gần 65, ASCII cho A.

Từ thuật toán này, chúng tôi bắt đầu thực tế mọi chuỗi trong phạm vi ASCII, cùng với bộ đếm cho từng và một dòng mới dễ dàng tiếp cận.

Một ví dụ thực tế:

In tất cả chữ hoa và chữ thường và chữ số.

Với thuật toán:

+++[[<+>>++<-]>]<<[-<->]<<<<++[->>+.>+.<<<]<--[>>.+<<-]

Không có:

+++++++++++++[->+++++++>++>+++++>++++>+<<<<<]>+++++>[-<+.>>.+<]>>---->---[-<.+>]

Chúng tôi dành hầu hết các byte chỉ khởi tạo băng trong ví dụ thứ hai. Một số điều này bù đắp bằng các chuyển động bổ sung trong ví dụ đầu tiên, nhưng phương pháp này rõ ràng có lợi thế.

Một vài thuật toán thú vị khác trong cùng một hướng:

3 * 2 ^ n + 1:

+++[[<+>>++<-]+>]
Tape: 4 7 13 25 49 65 197 129 1 0'

Điều này bù đắp các giá trị bằng 1, thực hiện một số điều. Nó làm cho 12 trở lại vận chuyển, 64 bắt đầu thực tế của bảng chữ cái in hoa và 24 gần hơn với 26.

2 ^ n:

+[[<+>>++<-]>]
Tape: 1 2 4 8 16 32 64 128

Vì 64 là tốt cho chữ in hoa, 32 là ASCII cho không gian và 128 có thể được sử dụng làm bộ đếm cho 26 (130/5 = 26). Điều này có thể lưu byte trong các tình huống nhất định trong đó các chữ số và chữ thường không cần thiết.

Chọn cách thực hiện phù hợp với câu hỏi:

  • Các tế bào tiêu cực hầu như luôn luôn hữu ích và không có lý do gì để tránh chúng (trừ khi nó không thay đổi số lượng của bạn)
  • Hầu như điều tương tự chính xác với các ô bao bọc, thậm chí còn nhiều hơn bởi vì nhiều hằng số sử dụng gói.
  • Kích thước ô tùy ý rất hữu ích cho các chuỗi toán học vô hạn, chẳng hạn như tính toán chuỗi Fibonacci vô hạn ( +[[-<+>>+>+<<]>]) hoặc xử lý các số lớn hơn / âm. Nhược điểm là một số phương pháp phổ biến, chẳng hạn như [-][->+<]không thể dựa vào, chỉ trong trường hợp số đó là âm.
  • EOF là 0, -1 hoặc không thay đổi. 0 thường là tốt hơn, vì bạn có thể lặp qua toàn bộ đầu vào mà không cần kiểm tra thêm. -1 rất hữu ích khi lặp trên các cấu trúc mảng. Tôi chưa tìm thấy cách sử dụng để không thay đổi :(.

Theo dõi những gì frick đang diễn ra:

Tại mọi thời điểm, bạn nên có nhận xét về vị trí của con trỏ liên quan đến dữ liệu xung quanh nó và đảm bảo rằng bạn biết phạm vi giá trị có thể có của mỗi ô. Điều này đặc biệt quan trọng khi bạn chia con trỏ trước một vòng lặp, bởi vì bạn sẽ muốn kết hợp hai khả năng lại với nhau sau đó.

Tại bất kỳ thời điểm nào, mã của tôi chứa đầy các bình luận trên mỗi dòng khác trông như thế này:

*0 *dat a_1 ?  0' !0 0*
 or
*0 *dat 0' ap1 0  !0 0*

Một số lời khuyên thêm là gán biểu tượng ý nghĩa đặc biệt. Trong ví dụ trên, 'là nơi con trỏ, *có nghĩa là sự lặp lại theo hướng đó, ?có nghĩa là một ô có giá trị không xác định, !0có nghĩa là một ô khác không, _là một thay thế cho -plà một thay thế cho +.orngụ ý rằng băng có thể trông giống như một trong các đại diện, và cần phải được xử lý như vậy.

Sơ đồ biểu tượng của bạn không nhất thiết phải giống như của tôi (có một vài sai sót), nó chỉ cần nhất quán. Điều này cũng cực kỳ hữu ích khi gỡ lỗi, bởi vì bạn có thể chạy nó đến thời điểm đó và so sánh băng thực tế với những gì bạn nên có, có thể chỉ ra các lỗ hổng tiềm năng trong mã của bạn.


5

Lời khuyên chính của tôi sẽ là không.

OK, tốt, bạn muốn một cái gì đó hữu ích hơn thế. BF đã là một ngôn ngữ rất ngắn gọn, nhưng điều thực sự giết chết bạn là số học, điều này thực sự cần phải được thực hiện trong unary. Thật đáng để đọc qua trang hằng số ở Esolang để chọn ra chính xác cách viết số lớn một cách hiệu quả và khai thác gói bất cứ nơi nào có thể.

Bộ nhớ truy cập cũng rất tốn kém. Vì bạn đang đọc từ một cuộn băng, bạn cần lưu ý rằng đầu của bạn đang di chuyển bất cứ lúc nào. Không giống như các ngôn ngữ khác, nơi bạn chỉ có thể viết a,b , c, trong bf bạn phải di chuyển một cách rõ ràng người đứng đầu một số số byte sang trái hoặc phải, vì vậy bạn phải lưu tâm về nơi bạn lưu trữ những gì. Tôi khá chắc chắn rằng tổ chức bộ nhớ của bạn theo cách tối ưu là NP-hard, thật may mắn với điều đó.


5

Trong câu trả lời này, tôi sẽ đề cập đến một ô cụ thể trên băng nhiều lần. Không quan trọng đó là ô nào, nhưng đó là cùng một ô trong toàn bộ câu trả lời. Đối với mục đích của bài đăng này, tôi sẽ gọi ô đó là "Todd".

Khi cố gắng đặt một ô thành một giá trị không đổi, đôi khi nó trả tiền để không hoàn thành nó ngay lập tức. Ví dụ: giả sử bạn muốn Todd chứa 30. Sau đó, trong mã của bạn (có thể sửa đổi giá trị của Todd nhưng không bao giờ đọc nó), bạn quay lại với Todd. Nếu giá trị của Todd là 0, chương trình sẽ thoát. Mặt khác, giá trị của Todd được in mãi mãi.

Theo trang esolangs.org của các hằng số brainfuck (có thể là chủ đề của một mẹo riêng!) Cách ngắn nhất để có được 30 là >+[--[<]>>+<-]>+. Sự dẫn đầu đó >chỉ ở đó để đảm bảo rằng không có gì ở bên trái của con trỏ được sửa đổi, nhưng trong trường hợp này, chúng tôi sẽ cho rằng chúng tôi không quan tâm đến điều đó và bỏ nó đi. Sử dụng mã đó, mã của bạn sẽ trông giống như thế này:

+[--[<]>>+<-]>+(MISC. CODE)(GO TO TODD)[.]

Bạn có thể nghĩ về đoạn mã đầu tiên như thế này:

(SET TODD TO 30)(MISC. CODE)(GO TO TODD)[.]

Nhưng hãy nhớ hai ký tự cuối cùng trong đoạn đó : >+. Nghĩ về nó theo cách này là hợp lệ:

(SET TODD TO 29)(GO TO TODD)(ADD 1 TO TODD)(MISC. CODE)(GO TO TODD)[.]

Lưu ý rằng bạn (GO TO TODD)hai lần! Thay vào đó, bạn có thể viết mã theo cách này:

(SET TODD TO 29)(MISC. CODE)(GO TO TODD)(ADD 1 TO TODD)[.]
+[--[<]>>+<-](MISC. CODE)(GO TO TODD)+[.]

Giả sử rằng số byte cần thiết (GO TO TODD)là như nhau trước đó, một lần di chuyển ít hơn == một byte ít hơn! Đôi khi thực tế là vị trí bắt đầu của bạn đã thay đổi sẽ làm mất đi lợi ích đó, nhưng không phải lúc nào cũng vậy.


0

Một mẹo nhỏ cho các thách thức mà không cần đầu vào. Bạn có thể sử dụng ,thay vì [-], nếu bạn cần xóa ô một cách nhanh chóng, vì hầu hết các trình thông dịch (bao gồm cả TIO.run một) sẽ đặt nội dung ô thành biểu diễn EOF bằng không. Điều này làm cho các chương trình trở nên nhỏ bé không đáng tin cậy, nhưng ai quan tâm đến nó trong mã golf?

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.