Tạo một ngôn ngữ lập trình dường như không thể sử dụng được


85

Chủ đề thách thức của tên cướp là ở đây .

Thách thức của Cops: Thiết kế một ngôn ngữ lập trình dường như không thể sử dụng để lập trình, nhưng thừa nhận tính toán (hoặc ít nhất là hoàn thành nhiệm vụ) thông qua một số cơ chế không rõ ràng.

Bạn nên thiết kế một ngôn ngữ lập trình đơn giản đọc mã từ tệp đầu vào và sau đó thực hiện ... một cái gì đó. Bạn phải chuẩn bị một chương trình giải pháp tìm số lớn thứ 3 trong đầu vào khi chạy trong trình thông dịch. Bạn cần phải làm cho kẻ cướp càng khó tìm ra một chương trình giải pháp. Lưu ý rằng những tên cướp có thể đăng bất kỳ giải pháp nào hoàn thành nhiệm vụ, không chỉ là giải pháp bạn có trong đầu.

Đây là một cuộc thi phổ biến. Mục tiêu của cảnh sát là lấy càng nhiều phiếu càng tốt trong khi tồn tại 8 ngày sau khi đăng thông dịch viên mà không bị bẻ khóa. Để kết thúc này, các thực hành sau đây sẽ giúp:

  • Giải thích chính xác ngữ nghĩa ngôn ngữ của bạn
  • Viết mã có thể đọc được

Các chiến thuật sau đây được khuyến khích mạnh mẽ:

  • Sử dụng mã hóa, băm hoặc các phương thức mã hóa khác. Nếu bạn thấy một ngôn ngữ sử dụng mã hóa RSA hoặc từ chối thực thi chương trình trừ khi hàm băm SHA-3 của nó bằng 0x1936206392306, vui lòng đừng ngần ngại tải xuống.

Thử thách của kẻ cướp: Viết chương trình tìm số nguyên lớn thứ ba trong đầu vào khi chạy trong trình thông dịch của cảnh sát.

Điều này là tương đối đơn giản. Để bẻ khóa một câu trả lời của cảnh sát, bạn phải tạo một chương trình hoàn thành nhiệm vụ khi chạy trong trình thông dịch của nó. Khi bạn bẻ khóa một câu trả lời, hãy đăng một bình luận có nội dung "Cracked" trên câu trả lời của cảnh sát liên kết đến bài đăng của bạn. Bất cứ ai bẻ khóa được nhiều cảnh sát nhất đều thắng sợi chỉ của bọn cướp.

Quy tắc I / O

  • Thông dịch viên nên lấy tên tệp trên dòng lệnh cho chương trình và sử dụng đầu vào và đầu ra tiêu chuẩn khi chạy nó.
  • Đầu vào sẽ được đưa ra một cách đơn nhất và chỉ bao gồm các ký tự 01(48 và 49 trong ASCII). Một số N được mã hóa thành N 1s theo sau là a 0. Có một bổ sung 0trước khi kết thúc tập tin. Ví dụ: Đối với chuỗi (3, 3, 1, 14), đầu vào là 11101110101111111111111100.
  • Đầu vào được đảm bảo có ít nhất 3 số. Tất cả các số là số nguyên dương.
  • Đầu ra sẽ được đánh giá bằng số 1s được in trước khi chương trình tạm dừng. Các nhân vật khác được bỏ qua.

Trong các ví dụ sau, dòng đầu tiên là đầu vào ở định dạng thập phân; thứ hai là đầu vào chương trình thực tế; thứ ba là một đầu ra mẫu.

1, 1, 3
101011100
1

15, 18, 7, 2, 15, 12, 3, 1, 7, 17, 2, 13, 6, 8, 17, 7, 15, 11, 17, 2
111111111111111011111111111111111101111111011011111111111111101111111111110111010111111101111111111111111101101111111111111011111101111111101111111111111111101111111011111111111111101111111111101111111111111111101100
111111,ir23j11111111111u

247, 367, 863, 773, 808, 614, 2
<omitted>
<contains 773 1's>

Quy tắc nhàm chán cho câu trả lời của cảnh sát:

  • Để ngăn chặn bảo mật thông qua che khuất, trình thông dịch nên được viết bằng ngôn ngữ trong top 100 của chỉ số TIOBE này và có trình biên dịch / trình thông dịch có sẵn miễn phí.
  • Thông dịch viên không được giải thích một ngôn ngữ đã được xuất bản trước thách thức này.
  • Thông dịch viên phải phù hợp với bài viết của bạn và không được lưu trữ bên ngoài.
  • Thông dịch viên nên được xác định
  • Thông dịch viên nên được xách tay và tuân theo tiêu chuẩn của ngôn ngữ riêng của mình; không sử dụng hành vi hoặc lỗi không xác định
  • Nếu chương trình giải pháp quá dài để phù hợp với câu trả lời, bạn phải đăng một chương trình tạo ra nó.
  • Chương trình giải pháp chỉ nên bao gồm ASCII có thể in và các dòng mới.
  • Bạn phải thực hiện chương trình giải pháp của mình trong vòng chưa đầy 1 giờ trên máy tính của riêng bạn cho mỗi ví dụ đầu vào ở trên.
  • Chương trình nên hoạt động cho mọi số nguyên nhỏ hơn 10 6 và bất kỳ số nguyên nào dưới 10 6 (không nhất thiết phải dưới một giờ), với điều kiện là tổng chiều dài đầu vào nhỏ hơn 10 9 .
  • Để trở nên an toàn, một cảnh sát phải chỉnh sửa chương trình giải pháp thành câu trả lời sau 8 ngày trôi qua.

Chấm điểm

Người cảnh sát trở nên an toàn với số điểm cao nhất và điểm số tích cực sẽ chiến thắng câu hỏi này.


Bạn không nói rõ điều này nhưng tôi có đúng không khi cho rằng cảnh sát thực sự phải viết và đăng thông dịch viên trong câu trả lời của họ?
Màu xanh

@muddyfish Có, thông dịch viên nên là nội dung của câu trả lời của cảnh sát.
frageum

1
@ kirbyfan64sos Kết quả sẽ được đánh giá bằng số 1 giây được in trước khi chương trình tạm dừng. Các nhân vật khác được bỏ qua.
mbomb007


20
Tôi đã tự bắn tỉa mình với thử thách này. Tôi tạo ra một ngôn ngữ lập trình và sau đó đã mất nhiều giờ lập trình công việc để xem nếu ngôn ngữ thậm chí có thể làm việc chỉ để tìm ra rằng nó thực sự không sử dụng được.
Chiếm

Câu trả lời:


24

Thay đổi (an toàn)

ShapeScript

ShapeScript là một ngôn ngữ lập trình xuất hiện tự nhiên. Bộ thay đổi hình dạng (hoặc Thay đổi , vì chúng thích được gọi) có thể chuyển đổi thành một bộ hướng dẫn cho phép chúng xử lý dữ liệu.

ShapeScript là một ngôn ngữ dựa trên ngăn xếp với cú pháp tương đối đơn giản. Không có gì đáng ngạc nhiên, hầu hết các thỏa thuận tích hợp của nó với việc thay đổi hình dạng của chuỗi. Nó được giải thích, từng nhân vật, như sau:

  • '"bắt đầu một chuỗi bằng chữ.

    Cho đến khi trích dẫn phù hợp được tìm thấy trong mã nguồn, tất cả các ký tự giữa các trích dẫn khớp này được thu thập trong một chuỗi, sau đó được đẩy lên ngăn xếp.

  • 0để 9đẩy các số nguyên 0 đến 9 trên ngăn xếp. Lưu ý rằng 10đẩy hai số nguyên.

  • ! bật một chuỗi từ ngăn xếp và cố gắng đánh giá nó dưới dạng ShapeScript.

  • ? bật một số nguyên từ ngăn xếp và đẩy một bản sao của mục ngăn xếp tại chỉ mục đó.

    Chỉ số 0 tương ứng với mục ngăn xếp trên cùng (LIFO) và chỉ mục -1 cho mục dưới cùng.

  • _ bật một vòng lặp từ ngăn xếp và đẩy chiều dài của nó.

  • @ bật hai mục từ ngăn xếp và đẩy chúng theo thứ tự đảo ngược.

  • $bật hai chuỗi từ ngăn xếp và tách chuỗi nhiều nhất ở lần xuất hiện của chuỗi trên cùng. Danh sách kết quả được đẩy trở lại.

  • &bật một chuỗi (trên cùng) và một lần lặp từ ngăn xếp và tham gia lặp lại, sử dụng chuỗi làm dấu phân cách. Chuỗi kết quả được đẩy trở lại.

  • Nếu ShapeScript được sử dụng trên hành tinh chúng ta, kể từ khi con trăn là Changelings người thân gần gũi nhất trên trái đất, tất cả các nhân vật khác c pop hai mục xy (trên cùng) từ ngăn xếp, và cố gắng để đánh giá các mã Python x c y.

    Ví dụ: chuỗi ký tự 23+sẽ đánh giá 2+3, trong khi chuỗi ký tự "one"3*sẽ đánh giá 'one'*3và chuỗi ký tự 1''Asẽ đánh giá 1A''.

    Trong trường hợp cuối cùng, do kết quả không phải là Python hợp lệ, Changeling sẽ phàn nàn rằng hình dạng hiện tại của anh ta không được chỉnh sửa (lỗi cú pháp), vì nó không phải là ShapeScript hợp lệ.

Trước khi thực thi mã, trình thông dịch sẽ đặt toàn bộ đầu vào của người dùng dưới dạng một chuỗi trên ngăn xếp. Sau khi thực thi mã nguồn, trình thông dịch sẽ in tất cả các mục trên ngăn xếp. Nếu bất kỳ phần nào ở giữa không thành công, Changeling sẽ phàn nàn rằng hình dạng hiện tại của anh ta không đủ (lỗi thời gian chạy).

Thay đổi hình dạng

Ở trạng thái tự nhiên, Changelings không có hình dạng của ShapeScript. Tuy nhiên, một số trong số chúng có thể chuyển đổi thành một mã nguồn tiềm năng (không nhất thiết phải hữu ích hoặc thậm chí hợp lệ về mặt cú pháp).

Tất cả các Thay đổi đủ điều kiện có dạng tự nhiên sau:

  • Tất cả các dòng phải có cùng số lượng ký tự.

  • Tất cả các dòng phải bao gồm các ký tự ASCII có thể in được, theo sau là một dòng cấp dữ liệu.

  • Số lượng dòng phải phù hợp với số lượng ký tự có thể in trên mỗi dòng.

Ví dụ, chuỗi byte ab\ncd\nlà một Changeling đủ điều kiện.

Trong nỗ lực chuyển sang ShapeScript, Changeling trải qua quá trình chuyển đổi sau:

  • Ban đầu, không có mã nguồn.

  • Đối với mỗi dòng sau đây xảy ra:

    • Bộ tích lũy của Changeling được đặt thành không.

    • Đối với mỗi ký tự c của dòng (bao gồm cả nguồn cấp dữ liệu theo dõi), điểm mã của c được XOR với bộ tích lũy chia cho 2 và ký tự Unicode tương ứng với điểm mã kết quả được gắn vào mã nguồn.

      Sau đó, sự khác biệt giữa điểm mã của c và điểm mã của khoảng trắng (32) được thêm vào bộ tích lũy.

Nếu bất kỳ phần nào ở trên không thành công, Changeling sẽ phàn nàn rằng hình dạng hiện tại của nó là khó chịu.

Sau khi tất cả các dòng đã được xử lý, việc chuyển đổi của Changeling thành ShapeScript (hy vọng hợp lệ) đã hoàn tất và mã kết quả được thực thi.

Giải pháp (ShapeScript)

"0"@"0"$"0"2*&"0"@+0?_'@1?"0"$_8>"1"*+@1?"0"+$""&'*!#

ShapeScript thực sự hóa ra khá hữu dụng; nó thậm chí có thể thực hiện kiểm tra tính nguyên thủy ( bằng chứng ) và do đó thỏa mãn định nghĩa của chúng tôi về ngôn ngữ lập trình.

Tôi đã xuất bản lại ShapeScript trên GitHub , với cú pháp được sửa đổi một chút và I / O tốt hơn.

Mã này làm như sau:

"0"@    Push "0" (A) and swap it with the input string (S).
"0"$    Split S at 0's.
"0"2*   Push "00".
&       Join the split S, using "00" as separator.
"0"@+   Prepend "0" to S.
        The input has been transformed into
        "0<run of 1's>000<run of 1's>0...0<run of 1's>0000".
0?_     Push a copy and compute its length (L).
'       Push a string that, when evaluated, does the following:
  @1?     Swap A on top of S, and push a copy of S.
  "0"$    Split the copy of S at 0's.
  _8>     Get the length of the resulting array and compare it with 8.
          If this returns True, there are more than eight chunks, so there are
          more then seven 0's. With two 0's surrounding each run of 1's and
          three extra 0's at the end, this means that there still are three or
          more runs of 1's in the string.
  "1"*    Push "1" if '>' returned True and "" if it returned False.
  +       Append "1" or "" to A.
  @1?     Swap S on top of A, and push a copy of A.
  "0"+    Append "0" to the copy of A.
  $       Split S at occurrences of A+"0".
  ""&     Flatten the resulting array of strings.
'       This removes all occurrences of "010" in the first iteration, all
        occurrences of "0110" in the second, etc., until there are less than
        three runs of 1's left in S. At this point, A is no longer updated,
        and the code inside the string becomes a noop.
*!      Repeat the code string L times and evaluate.
#       Drop S, leaving only A on the stack.

Giải pháp (Thay đổi)

"1+-.......................
""B1.......................
"" 2)+7....................
"" 2)=<....................
""( $86=...................
""B8=......................
""247......................
""]`b......................
""B1.......................
""%D1=.....................
""%500=....................
""%&74?....................
""%&#.2....................
""%[bdG....................
""%D1=.....................
""%<5?.....................
""%:6>.....................
""%&65?....................
""%&-%7>...................
""%D1=.....................
""%500=....................
""%&74?....................
""%&,)5>...................
""%&%,79...................
"" )$?/=...................
""),-9=....................
""# !......................

Giống như tất cả các chương trình Changeling, mã này có một dòng cấp dữ liệu.

ShapeScript sẽ báo lỗi ngay lập tức trên bất kỳ ký tự nào không hiểu, nhưng chúng ta có thể đẩy các chuỗi tùy ý làm chất độn và bật chúng sau. Điều này cho phép chúng tôi chỉ đặt một lượng nhỏ mã trên mỗi dòng (lúc đầu, nơi tích lũy nhỏ), sau đó là mở ". Nếu chúng tôi bắt đầu dòng tiếp theo "#, chúng tôi sẽ đóng và bật chuỗi mà không ảnh hưởng đến mã thực tế.

Ngoài ra, chúng tôi phải vượt qua ba trở ngại nhỏ:

  • Chuỗi dài trong mã ShapeScript là một mã thông báo duy nhất và chúng tôi sẽ không thể điều chỉnh nó trên một dòng.

    Chúng tôi sẽ đẩy chuỗi này trong khối ( '@', '1?', vv), mà chúng tôi sẽ tiếp nhau sau đó.

  • Điểm mã của _khá cao, và việc đẩy '_'sẽ có vấn đề.

    Tuy nhiên, chúng tôi sẽ có thể '_@'dễ dàng đẩy mạnh , theo sau là một người khác '@'để hoàn tác việc hoán đổi.

Mã ShapeScript Changeling của chúng tôi sẽ tạo ra trông như thế này 1 :

"0""
"#@"
"#"0"$"
"#"0"2"
"#*&"0""
"#@+"
"#0?"
"#_@"
"#@"
"#'@'"
"#'1?'"
"#'"0'"
"#'"$'"
"#'_@'"
"#'@'"
"#'8'"
"#'>'"
"#'"1'"
"#'"*+'"
"#'@'"
"#'1?'"
"#'"0'"
"#'"+$'"
"#'""&'"
"#"+"77"
"#+*!*"
"#!#"

Tôi đã tìm thấy mã Changeling bằng cách chạy mã ShapeScript ở trên thông qua trình chuyển đổi này . 2

Thông dịch viên (Python 3)

#!/usr/bin/env python3

import fileinput

def error(code):
  print("This shape is " + ["unpleasant", "unpurposed", "inadequate"][code - 1] + ".")
  exit(code)

def interpret(code):
  global stack
  stringing = 0
  for char in code:
    quote = (char == "'") + 2 * (char == '"')
    if quote or stringing:
      if not stringing:
        string = ""
        stringing = quote
      elif stringing == quote:
        stack.append(string)
        stringing = 0
      else:
        string += char
    elif char in "0123456789":
      stack.append(int(char))
    else:
      x = stack.pop()
      if char == '!':
        interpret(x)
      else:
        if char == '?':
          y = stack[~x]
        elif char == "_":
          y = len(x)
        else:
          y = stack.pop()
          if char == '@':
            stack.append(x)
          elif char == '$':
            y = y.split(x)
          elif char == '&':
            y = x.join(map(str, y))
          else:
            try:
              y = eval(repr(y) + char + repr(x))
            except SyntaxError:
              error(2)
        stack.append(y)

source = ""
lengths = []

for line in fileinput.input():
  if not line or sorted(line)[:2][-1] < " " or max(line) > "~":
    error(1)
  lengths.append(len(line))
  accumulator = 0
  for char in line:
    value = ord(char)
    try:
      source += chr(value ^ (accumulator >> 1))
    except:
      error(1)
    accumulator += value - 32

lengths.append(len(lengths) + 1)

if min(lengths) != max(lengths):
  error(1)

stack = ""

for line in fileinput.input("-"):
  stack += line

stack = [stack]

try:
  interpret(source)
except:
  error(3)

print("".join(map(str, stack)))

1 Mỗi dòng được đệm với rác ngẫu nhiên theo số lượng dòng và nguồn cấp dữ liệu thực sự không có mặt.
2 Các số ở dưới cùng cho biết điểm mã thấp nhất và cao nhất trong mã Changeling, phải nằm trong khoảng từ 32 đến 126.


1
-1 để sử dụng xor / biến đổi. Việc thay đổi chuyển đổi ShapeScript trông giống như mã hóa đối với tôi.
MegaTom

10
@MegaTom Bạn có thể bỏ phiếu khi bạn thấy phù hợp, nhưng các câu hỏi cau mày khi mã hóa vì nó lấy một khóa , một hằng số chỉ được biết bởi cảnh sát khiến kẻ cướp gặp bất lợi đáng kể. Việc chuyển đổi là một chuyển đổi không bị khóa .
Dennis

1
ShapeScript, 67 byte : 0"#002?'+'&'0'$'0?2?-@2?>*+00'&!++'1'*'0'+@1?$0?''&@_2-2?*@+@"3*!@#. Tôi đã từ bỏ việc tìm kiếm một Changeling cho nó, mặc dù. Ngay cả xen kẽ với các tuyên bố chủ yếu là vô dụng, tôi đã không thể nhận được hơn 20 byte.
Primo

2
@MegaTom Tôi thực sự khá thất vọng bởi giải pháp đã cho. Tôi đã mong đợi một cái gì đó thông minh hơn đáng kể so với mã vô dụng 92,9%.
primo

2
@primo Phải mất một chút mày mò, nhưng tôi thấy Changeling này cũng hoạt động với Python 2. Tôi không biết câu trả lời của mình thông minh đến mức nào , nhưng kế hoạch đăng một cảnh sát với một kẽ hở cần phải tìm ra để phá vỡ nó dường như đã có hiệu quả.
Dennis

30

Shuffle (viết bằng C ++), Cracked! bởi Martin

Chỉnh sửa Martin đã bẻ khóa nó. Để xem giải pháp của mình, nhấp vào liên kết. Giải pháp của tôi cũng đã được thêm vào.

Chỉnh sửa Fixed print-dlệnh để có thể xử lý cả hai thanh ghi và ngăn xếp. Vì đây là lệnh gỡ lỗi không được phép trong một giải pháp, nên điều này sẽ không ảnh hưởng đến bất kỳ ai sử dụng phiên bản trước đó của trình thông dịch

Tôi vẫn chưa quen với điều này, vì vậy nếu có gì sai với câu trả lời của tôi hoặc người phiên dịch, xin vui lòng cho tôi biết. Xin yêu cầu làm rõ nếu một cái gì đó không rõ ràng.

Tôi không tưởng tượng rằng điều này sẽ quá khó khăn, nhưng hy vọng nó sẽ mang đến một thách thức nào đó. Điều làm cho shuffle khá không sử dụng được là nó sẽ chỉ in khi mọi thứ ở đúng vị trí của chúng.

-> Cơ bản:

Có 24 ngăn xếp, chúng tôi gọi chúng stack1, ... stack24. Những ngăn xếp này sống trong một danh sách. Khi bắt đầu bất kỳ chương trình nào, các ngăn xếp này không được đẩy và chúng bắt đầu ở vị trí thích hợp của chúng, tức là ngăn xếp i ở vị trí thứ i trong danh sách (Lưu ý rằng chúng tôi sẽ lập chỉ mục bắt đầu từ 1, không giống như C ++). Trong quá trình của một chương trình, thứ tự của các ngăn xếp trong danh sách sẽ thay đổi. Điều này rất quan trọng vì những lý do sẽ được giải thích khi tôi thảo luận về các lệnh.

Có 5 thanh ghi có sẵn để sử dụng. Chúng được đặt tên Alberto, Gertrude, Hans, Leopold, ShabbySam. Mỗi trong số này được đặt thành 0 khi bắt đầu chương trình.

Vì vậy, khi bắt đầu bất kỳ chương trình nào, có 24 ngăn xếp, mỗi ngăn có số phù hợp với chỉ mục của nó trong danh sách ngăn xếp. Mỗi ngăn xếp có chính xác một số không trên đầu trang. Mỗi trong số năm thanh ghi được khởi tạo bằng không.

-> Lệnh và Cú pháp :

Có 13 lệnh (lệnh gỡ lỗi +1) có sẵn trong Shuffle. Họ là như sau

  • cinpushLệnh này không có đối số. Nó chờ đầu vào dòng lệnh theo kiểu được mô tả trong câu hỏi (đầu vào khác sẽ dẫn đến kết quả không xác định / không xác định). Sau đó, nó phân tách chuỗi đầu vào thành số nguyên, ví dụ 101011100-> 1,1,3. Đối với mỗi đầu vào nhận được, nó thực hiện như sau: (1) cho phép danh sách các ngăn xếp dựa trên giá trị. Đặt giá trị nguyên trong câu hỏi được gọi là a . Nếu a nhỏ hơn 10 thì hoán vị u . Nếu a nằm trong khoảng từ 9 đến 30 (không bao gồm) thì nó hoán vị d . Nếu không thì hoán vị r . (2) Sau đó đẩy mộtvào ngăn xếp đầu tiên trong danh sách. Lưu ý rằng tôi không có ý stack1(mặc dù nó có thể là trường hợp stack1đầu tiên trong danh sách). Các hoán vị được định nghĩa dưới đây. Vì cinpushlà cách duy nhất để có được đầu vào của người dùng , nó phải xuất hiện trong bất kỳ giải pháp nào.
  • mov value registerCác movlệnh cơ bản là biến chuyển nhượng. Nó gán valuecho register. valuecó thể có một số dạng: nó có thể là (1) một số nguyên, ví dụ 47 (2) tên của một thanh ghi khác, ví dụ Hans (3) chỉ mục của ngăn xếp theo sau là 's', vd 4s. Lưu ý rằng đây là chỉ mục trong danh sách, không phải số lượng ngăn xếp. Như vậy, số lượng không được vượt quá 24.

    Một số movví dụ:

    mov 4s Hans 
    mov Hans ShabbySam
    mov 9999 Gertrude
    
  • movfs index registerĐiều này là viết tắt của 'di chuyển từ ngăn xếp'. Nó tương tự như movlệnh. Nó tồn tại để bạn có thể truy cập ngăn xếp được lập chỉ mục bởi một thanh ghi. Ví dụ: nếu trước đó bạn đặt Hans bằng 4 ( mov 4 Hans) thì bạn có thể sử dụng movfs Hans Gertrudeđể đặt Gertrude bằng với đỉnh của ngăn xếp 4. Loại hành vi này không thể truy cập đơn giản bằng cách sử dụng mov.

  • inc register tăng giá trị đăng ký lên 1.
  • dec register giảm giá trị đăng ký đi 1.
  • compg value value registerĐiều này là viết tắt của 'so sánh lớn hơn'. Nó đặt thanh ghi bằng giá trị lớn hơn của hai giá trị. valuecó thể là một số nguyên, một thanh ghi hoặc một chỉ mục ngăn xếp theo sau là 's', như trên.
  • compl value value register 'so sánh nhỏ hơn' giống như trên, ngoại trừ lấy giá trị nhỏ hơn.
  • gte value1 value2 registerKiểm tra nếu value1 >= value2sau đó đặt giá trị Boolean (dưới dạng 1 hoặc 0) vào register.
  • POP!! indexbật ra khỏi đầu ngăn xếp được lập chỉ mục bởi indextrong danh sách ngăn xếp.
  • jmp labelnhảy vô điều kiện vào nhãn label. Đây là thời điểm tốt để nói về nhãn. Một nhãn là từ theo sau ':'. Trình thông dịch phân tích cú pháp trước cho các nhãn, vì vậy bạn có thể chuyển tiếp tới các nhãn cũng như quay lại.
  • jz value labelnhảy tới labelnếu valuebằng không.
  • jnz value labelnhảy đến labelnếu valuelà khác không.

    Ví dụ về nhãn và nhảy:

    this_is_my_label:
         jmp this_is_my_label
    
    mov 10 Hans
    jnz Hans infinite_loop
    
    infinite_loop:
         jmp infinite_loop
    
  • "shuffle" permutationĐây là lệnh xáo trộn. Điều này cho phép bạn hoán vị danh sách các ngăn xếp. Có ba hoán vị hợp lệ mà có thể được sử dụng như các đối số, l, f, và b.

  • print registerĐiều này kiểm tra xem tất cả các ngăn xếp có ở vị trí ban đầu của chúng không, tức là ngăn xếp i nằm ở chỉ mục i trong danh sách ngăn xếp. Nếu đây là trường hợp, nó in giá trị tại registerunary. Nếu không, nó in một lỗi khó chịu. Như bạn có thể thấy, để xuất ra bất cứ thứ gì, tất cả các ngăn xếp phải ở đúng vị trí.
  • done!Điều này nói với chương trình để thoát mà không có lỗi. Nếu chương trình kết thúc mà không có done!, nó sẽ in ra bàn điều khiển số trên đầu mỗi ngăn xếp theo sau là số của ngăn xếp. Thứ tự mà các ngăn xếp được in là thứ tự chúng xuất hiện trong danh sách ngăn xếp. Nếu một ngăn xếp trống, nó sẽ bị bỏ qua. Hành vi này là dành cho mục đích gỡ lỗi và có thể không được sử dụng trong một giải pháp.
  • print-d valuecái này in giá trị của ngăn xếp, thanh ghi hoặc số nguyên đã cho (để truy cập ngăn xếp i , chuyển qua islàm đối số, như đã giải thích ở trên). Đây là một công cụ gỡ lỗi và không phải là một phần của ngôn ngữ, vì vậy nó không được phép trong một giải pháp.

-> Đây là mã thông dịch viên

Tất cả các phân tích cú pháp xảy ra trong chức năng chính. Đây là nơi bạn sẽ tìm thấy nó phân tích cú pháp cho các lệnh cụ thể.

#include<fstream>
#include<iostream>
#include<string>
#include<stack>
#include<cmath>

using namespace std;

class superStack: public stack<long> {
    private:
        int m_place;
    public:
        static int s_index;


        superStack() {
            m_place = s_index;
            s_index++;
        }

        int place() {
            return m_place;
        }
};
int superStack::s_index=1;

superStack  stack1,stack2,stack3,stack4,stack5,stack6,stack7,stack8,stack9,stack10,stack11, \
            stack12,stack13,stack14,stack15,stack16,stack17,stack18,stack19,stack20,stack21,stack22,stack23,stack24;


superStack* stackptrs[]=    { \
                        &stack1,&stack2,&stack3,&stack4,&stack5,&stack6,&stack7,&stack8,&stack9,&stack10,&stack11, \
                        &stack12,&stack13,&stack14,&stack15,&stack16,&stack17,&stack18,&stack19,&stack20,&stack21,&stack22,&stack23,&stack24 \
                        };


long Gertrude=0;
long Hans=0;
long Alberto=0;
long ShabbySam=0;
long Leopold=0;


void SWAP( int i, int j) {    // 0 < i,j < 25

    i--;
    j--;


    superStack* tempptr = stackptrs[i];
    stackptrs[i]=stackptrs[j];
    stackptrs[j] = tempptr;



}

void u() {
    int list[3][4] = {
                        {1,9,6,13},
                        {2,10,5,14},
                        {17,19,20,18},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void d() {
    int list[3][4] = {
                        {3,11,8,15},
                        {4,12,7,16},
                        {22,24,23,21},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void r() {
    int list[3][4] = {
                        {2,17,8,24},
                        {4,18,6,23},
                        {9,10,12,11},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void l() {
    int list[3][4] = {
                        {1,19,7,22},
                        {3,20,5,21},
                        {14,13,15,16},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void f() {
    int list[3][4] = {
                        {20,9,24,16},
                        {18,11,22,14},
                        {1,2,4,3},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}
void b() {
    int list[3][4] = {
                        {19,10,23,15},
                        {17,12,21,13},
                        {5,6,8,7},
                    };

    for(int i=0;i<3;i++) {
        for(int j=1;j<4;j++) {
            SWAP( list[i][0], list[i][j] );         
        }
    }
}

void splitpush(string input) {
    long value=0;

    for(long i=0;i<input.size();i++) {

        if(input[i]=='1'){
            value++;
        }
        else if(input[i]=='0' && value!=0 ) {
            if(value<10) {
                u();
            }
            else if (value<30) {
                d();

            }
            else {
                r();
            }
            (*stackptrs[0]).push(value);
            value=0;

        }
        else {
            break;
        }

    }

}

long strToInt(string str) { // IF STRING HAS NON DIGITS, YOU WILL GET GARBAGE, BUT NO ERROR
    long j=str.size()-1;
    long value = 0;
    for(long i=0;i<str.size();i++) {
        long x = str[i]-48;

        value+=x*floor( pow(10,j) );
        j--;
    }
    return value;
}

bool isEmpty(superStack stk) {
    if( stk.size()>0){return false; }
    else {return true;}

}    

long getValue(string str) {
    if(str=="ShabbySam") {
        return ShabbySam;
    }
    else if(str=="Hans") {
        return Hans;
    }
    else if(str=="Gertrude") {
        return Gertrude;
    }
    else if(str=="Alberto") {
        return Alberto;
    }   
    else if(str=="Leopold") {
        return Leopold;
    }
    else if(str[ str.size()-1 ]=='s'){
        str.pop_back();

        long index = strToInt(str)-1;

        if( !isEmpty( (*stackptrs[index]) ) ) {
            return (*stackptrs[index]).top();
        }
        else {
            cerr<<"Bad Expression or Empty Stack";


        }   
    }
    else {
        return strToInt(str);
    }

}

void printUnary(long i) {
    while(i>0) {
        cout<<1;
        i--;
    }
}

int main(int argc, char**argv) {

    if(argc<2){std::cerr<<"No input file given"; return 1;}
    ifstream inf(argv[1]);
    if(!inf){std::cerr<<"File open failed";return 1;}

    for(int i=0;i<24;i++) { 
        (*stackptrs[i]).push(0);         // Pre push a zero on every stack
    }

    string labels[20];
    unsigned labelPoints[20];
    int index=0;



    string str;
    while(inf) { //  parse for labels
        inf>>str;
        //cout<<inf.tellg()<<" ";
        if(inf) {
            if(str[str.size()-1]==':') {
                str.pop_back();
                bool alreadyExists = false;
                for(int i=0; i<index;i++){
                    if(labels[i] == str ) { alreadyExists=true;}
                }
                if(!alreadyExists) {
                    labels[index]=str;
                    labelPoints[index]= inf.tellg();
                    index++;
                }
            }

        }

    }
    inf.clear();
    inf.seekg(0,inf.beg);

    while(inf) { // parse for other commands 
        inf>>str;

        if(inf) {


            if(str=="cinpush") {
                string input;
                cin>>input;
                splitpush(input);
            }

            else if(str=="mov") {
                inf>>str;
                long value = getValue(str);

                inf>>str;
                if(str=="Gertrude"){Gertrude=value;}
                else if(str=="Hans"){Hans=value;}
                else if(str=="ShabbySam"){ShabbySam=value;}
                else if(str=="Alberto"){Alberto=value;}
                else if(str=="Leopold"){Leopold=value;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="movfs") {
                inf>>str;
                long index = getValue(str);
                if(!isEmpty( *stackptrs[index-1] )) {
                    inf>>str;
                    long value = (*stackptrs[index-1]).top();
                    if(str=="Gertrude"){Gertrude=value;}
                    else if(str=="Hans"){Hans=value;}
                    else if(str=="ShabbySam"){ShabbySam=value;}
                    else if(str=="Alberto"){Alberto=value;}
                    else if(str=="Leopold"){Leopold=value;}
                    else {cerr<<"Bad register name.";}
                }
                else {
                    cerr<<"Empty Stack";
                }



            }

            else if(str=="inc") {
                inf>>str;
                if(str=="Gertrude"){Gertrude++;}
                else if(str=="Hans"){Hans++;}
                else if(str=="ShabbySam"){ShabbySam++;}
                else if(str=="Alberto"){Alberto++;}
                else if(str=="Leopold"){Leopold++;}
                else {cerr<<"Bad register name. ";}
            }
            else if(str=="dec") {
                inf>>str;
                if(str=="Gertrude"){Gertrude--;}
                else if(str=="Hans"){Hans--;}
                else if(str=="ShabbySam"){ShabbySam--;}
                else if(str=="Alberto"){Alberto--;}
                else if(str=="Leopold"){Leopold--;}
                else {cerr<<"Bad register name. ";}
            }


            else if(str=="compg") {
                inf>>str;
                long value1 = getValue(str);
                inf>>str;
                long value2 = getValue(str);
                inf>>str;
                long larger;
                if(value1>value2){larger = value1;}
                else {larger = value2;}

                if(str=="Gertrude"){Gertrude=larger;}
                else if(str=="Hans"){Hans=larger;}
                else if(str=="ShabbySam"){ShabbySam=larger;}
                else if(str=="Alberto"){Alberto=larger;}
                else if(str=="Leopold"){Leopold=larger;}
                else {cerr<<"Bad register name. ";}

            }
            else if(str=="compl") {
                inf>>str;
                long value1 = getValue(str);
                inf>>str;
                long value2 = getValue(str);
                inf>>str;
                long larger; //LARGER IS REALLY SMALLER HERE
                if(value1>value2){larger = value2;}
                else {larger = value1;}

                if(str=="Gertrude"){Gertrude=larger;}
                else if(str=="Hans"){Hans=larger;}
                else if(str=="ShabbySam"){ShabbySam=larger;}
                else if(str=="Alberto"){Alberto=larger;}
                else if(str=="Leopold"){Leopold=larger;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="gte") {
                inf>>str;
                long value1= getValue(str);
                inf>>str;
                long value2= getValue(str);
                inf>>str;
                bool torf = value1 >= value2;

                if(str=="Gertrude"){Gertrude=torf;}
                else if(str=="Hans"){Hans=torf;}
                else if(str=="ShabbySam"){ShabbySam=torf;}
                else if(str=="Alberto"){Alberto=torf;}
                else if(str=="Leopold"){Leopold=torf;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="lte") {
                inf>>str;
                long value1= getValue(str);
                inf>>str;
                long value2= getValue(str);
                inf>>str;
                bool torf = value1 <= value2;

                if(str=="Gertrude"){Gertrude=torf;}
                else if(str=="Hans"){Hans=torf;}
                else if(str=="ShabbySam"){ShabbySam=torf;}
                else if(str=="Alberto"){Alberto=torf;}
                else if(str=="Leopold"){Leopold=torf;}
                else {cerr<<"Bad register name. ";}

            }

            else if(str=="POP!!") {
                inf>>str;
                long index = getValue(str);
                index--; //because we (C++ and this interpreter) index differently
                if(!isEmpty( *stackptrs[index] )) {
                    (*stackptrs[index]).pop();
                }
                else {cerr<<"Can't POP!! from empty stack";}

            }

            else if(str=="push"){cerr<<" You can't. ";}

            /*
            else if(str[str.size()-1]==':') {
                str.pop_back();
                bool alreadyExists = false;
                for(int i=0; i<index;i++){
                    if(labels[i] == str ) { alreadyExists=true;}
                }
                if(!alreadyExists) {
                    labels[index]=str;
                    labelPoints[index]= inf.tellg();
                    index++;
                }
            }*/

            else if(str=="jmp") {
                inf>>str;
                for(int i=0;i<index;i++) {
                    if( labels[i]==str) {
                        inf.seekg( labelPoints[i], ios::beg);
                    }
                }
            }
            else if(str=="jz") {
                inf>>str;
                long value = getValue(str);

                if(value==0) {
                    inf>>str;
                    for(int i=0;i<index;i++) {
                        if( labels[i]==str) {
                            inf.seekg( labelPoints[i], ios::beg);
                        }
                    }
                }
            }

            else if(str=="jnz") {
                inf>>str;
                long value = getValue(str);

                if(value!=0) {
                    inf>>str;
                    for(int i=0;i<index;i++) {
                        if( labels[i]==str) {
                            inf.seekg( labelPoints[i], ios::beg);
                        }
                    }
                }
            }

            else if(str== "\"shuffle\"") {
                inf>>str;
                if(str=="l"){ l(); }
                else if(str=="f"){ f(); }
                else if(str=="b"){ b(); }
                else {cerr<<"Bad shuffle parameter";}

            }

            else if(str=="print") {

                for(int i=0;i<24;i++) {

                    if( (i+1) != (*stackptrs[i]).place() ) {
                        cerr<< "Sorry, your stacks are in the wrong place! You can't print unless you put them back! Exiting. ";
                        return 1;
                    }

                }
                inf>>str;
                if(str=="Gertrude"){printUnary(Gertrude);}
                else if(str=="Hans"){printUnary(Hans);}
                else if(str=="ShabbySam"){printUnary(ShabbySam);}
                else if(str=="Alberto"){printUnary(Alberto);}
                else if(str=="Leopold"){printUnary(Leopold);}
                else {cerr<<"Bad register name. ";}


            }

            else if(str=="done!") {return 0;}

            else if(str=="print-d" ){
                inf>>str;
                long value = getValue(str);
                cout<<str;
              }
        }

    }







    /*for(int i=1;i<25;i++) {
        (*(stackptrs[i-1])).push(i);
    }

    u();d();r();l();f();b();
    */

    cout<<"\n";
    for(int i=1;i<25;i++) {
        if( (*(stackptrs[i-1])).size()>0 ) {
            cout<<(*(stackptrs[i-1])).top()<<" "<<(*(stackptrs[i-1])).place()<<"\n";
            (*(stackptrs[i-1])).pop();
        }
    }
    /*
    for (int i=0;i<index;i++) {
        cout<<labels[i]<<": "<<labelPoints[i]<<"\n";
    }*/

    return 1;
}

-> Hoán vị Hoán vị hoán vị các yếu tố của danh sách ngăn xếp theo kiểu sau:

Nghĩa là ở đâu

(Chúng cũng xuất hiện trong mã trình thông dịch. Nếu có sự khác biệt, trình thông dịch là chính xác.)

-> Ví dụ đơn giản

Hai chương trình đơn giản này in các số từ 24 đến 1 (đơn nhất) không có khoảng trắng.

mov 24 Hans
start:
    print Hans
    dec Hans
    jnz Hans start
done!

hoặc là

mov 24 Hans start: print Hans dec Hans jnz Hans start done!

Họ là cùng một chương trình.

Giải thích và Giải pháp:

Martin có một lời giải thích tốt trong câu trả lời của mình là tốt.

Như Martin đã tìm ra, ngôn ngữ này được lấy cảm hứng từ khối lập phương bỏ túi (còn gọi là khối lập phương 2x2 Rubik). 24 ngăn xếp giống như 24 ô vuông riêng lẻ trên khối lập phương. Các hoán vị là các động tác cơ bản được phép: lên, xuống, phải, trái, trước, sau.

Cuộc đấu tranh chính ở đây là khi các giá trị được đẩy, chỉ có ba động tác được sử dụng: lên, xuống và phải. Tuy nhiên, bạn không có quyền truy cập vào các di chuyển này khi "xáo trộn" các ngăn xếp. Bạn chỉ có ba động tác khác.

Hóa ra, cả hai bộ ba di chuyển thực sự bao trùm toàn bộ nhóm (tức là máy phát điện), vì vậy vấn đề có thể giải quyết được. Điều này có nghĩa là bạn thực sự có thể giải bất kỳ khối Rubik 2x2 nào chỉ bằng 3 lần di chuyển.

Tất cả những gì còn lại phải làm là tìm ra cách hoàn tác các động tác lên, xuống và phải bằng cách sử dụng ba bước còn lại. Cuối cùng, tôi sử dụng một hệ thống đại số máy tính gọi là GAP .

Sau khi hoàn tác các hoán vị, việc tìm số lớn thứ ba là khá nhỏ.

cinpush
main:
    mov 1s ShabbySam
    POP!! 1
    jmp compare
    continue:
        gte 0 ShabbySam Leopold
        jnz Leopold end
        gte ShabbySam 9 Leopold
        jz Leopold uinverse
        gte ShabbySam 29 Leopold
        jz Leopold dinverse
        jnz Leopold rinverse
compare:
    gte ShabbySam Alberto Leopold
    jz Leopold skip
    mov Gertrude Hans
    mov Alberto Gertrude
    mov ShabbySam Alberto
    jmp continue
    skip:
        gte ShabbySam Gertrude Leopold
        jz Leopold skip_2
        mov Gertrude Hans
        mov ShabbySam Gertrude
        jmp continue
    skip_2:
        gte ShabbySam Hans Leopold
        jz Leopold continue
        mov ShabbySam Hans
        jmp continue
uinverse: 
    "shuffle" f
    "shuffle" f
    "shuffle" f
    "shuffle" l
    "shuffle" b
    "shuffle" l
    "shuffle" b
    "shuffle" b
    "shuffle" b
    "shuffle" l
    "shuffle" l
    "shuffle" l
    "shuffle" b
    "shuffle" b
    "shuffle" b
    "shuffle" l
    "shuffle" l
    "shuffle" l
    "shuffle" f
    jmp main
dinverse:
    "shuffle" f
    "shuffle" b
    "shuffle" l
    "shuffle" b
    "shuffle" b
    "shuffle" b
    "shuffle" f
    "shuffle" f
    "shuffle" f
    jmp main
rinverse: 
    "shuffle" b "shuffle" l "shuffle" f "shuffle" l "shuffle" b
    "shuffle" f "shuffle" f "shuffle" f "shuffle" b
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" b "shuffle" b "shuffle" b
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" f "shuffle" l "shuffle" f
    "shuffle" l "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l 
    "shuffle" f "shuffle" l "shuffle" l 
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" l "shuffle" l "shuffle" l "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    "shuffle" f "shuffle" f "shuffle" f
    "shuffle" l "shuffle" f "shuffle" l "shuffle" f "shuffle" l "shuffle" f
    "shuffle" l "shuffle" l "shuffle" l
    jmp main
end:
    print Hans
    done!

2
Nứt. :) Tôi thực sự ấn tượng bởi ngôn ngữ!
Martin Ender

Wow đó là nhanh hơn tôi mong đợi. Cảm ơn, tôi rất vui vì nó rất thú vị khi viết ra.
Liam

Tôi tò mò, liệu có khó hơn không nếu tôi đổi tên các hoán vị thành một cái gì đó ít rõ ràng hơn về các khối của Rubik?
Liam

Họ chắc chắn là một đầu mối, nhưng tôi nghĩ rằng nó sẽ không lấy đó lâu hơn nữa nếu họ đã có những cái tên khác nhau.
Martin Ender

Heh, có vẻ như GAP đã không đặc biệt thông minh về việc đảo ngược ba hoán vị đầu vào. ;)
Martin Ender

22

Brian & Chuck , Cracked bởi tông_box

Bây giờ tôi đã bị thu hút bởi ý tưởng về ngôn ngữ lập trình nơi hai chương trình tương tác với nhau (có thể lấy cảm hứng từ ROCB ). Thử thách này là một động lực tốt đẹp để giải quyết khái niệm này cuối cùng trong khi cố gắng giữ ngôn ngữ ở mức tối thiểu nhất có thể. Mục tiêu thiết kế là làm cho ngôn ngữ Turing hoàn thành trong khi từng bộ phận riêng lẻ không hoàn thành Turing. Hơn nữa, thậm chí cả hai cùng nhau không nên hoàn thành Turing mà không sử dụng thao tác mã nguồn. Tôi nghĩ rằng tôi đã thành công với điều đó, nhưng tôi chưa chứng minh được bất kỳ điều gì trong số đó. Vì vậy, không có thêm ado tôi trình bày cho bạn ...

Nhân vật chính

Brian và Chuck là hai chương trình giống như Brainfuck. Chỉ một trong số họ đang bị xử tử tại bất kỳ thời điểm nào, bắt đầu với Brian. Điều đáng chú ý là băng nhớ của Brian cũng là mã nguồn của Chuck. Và băng nhớ của Chuck cũng là mã nguồn của Brian. Hơn nữa, đầu băng của Brian cũng là con trỏ chỉ dẫn của Chuck và ngược lại. Các băng là bán vô hạn (tức là vô hạn ở bên phải) và có thể giữ các số nguyên chính xác tùy ý đã ký, được khởi tạo về 0 (trừ khi được quy định khác bởi mã nguồn).

Vì mã nguồn cũng là một băng nhớ, các lệnh được xác định về mặt kỹ thuật bởi các giá trị nguyên, nhưng chúng tương ứng với các ký tự hợp lý. Các lệnh sau tồn tại:

  • ,( 44): Đọc một byte từ STDIN vào ô nhớ hiện tại. Chỉ Brian có thể làm điều này. Lệnh này là không có đối với Chuck.
  • .( 46): Viết ô nhớ hiện tại, modulo 256, dưới dạng byte sang STDOUT. Chỉ Chuck có thể làm điều này. Lệnh này là không có đối với Brian.
  • +( 43): Tăng ô nhớ hiện tại.
  • -( 45): Giảm ô nhớ hiện tại.
  • ?( 63): Nếu ô nhớ hiện tại bằng 0, thì đây là no-op. Nếu không, điều khiển tay sang chương trình khác. Đầu băng trên chương trình sử dụng ?sẽ vẫn còn trên ?. Đầu băng của chương trình kia sẽ di chuyển một ô sang bên phải trước khi thực hiện lệnh đầu tiên (vì vậy ô được sử dụng làm thử nghiệm không được thực thi chính nó).
  • <( 60): Di chuyển đầu băng một ô sang trái. Đây là một no-op nếu đầu băng đã ở đầu bên trái của băng.
  • >( 62): Di chuyển đầu băng một ô sang phải.
  • {( 123): Liên tục di chuyển đầu băng sang trái cho đến khi ô hiện tại bằng 0 hoặc đầu bên trái của băng được chạm tới.
  • }( 125): Liên tục di chuyển đầu băng sang phải cho đến khi ô hiện tại bằng không.

Chương trình chấm dứt khi con trỏ lệnh của chương trình đang hoạt động đạt đến điểm không có thêm hướng dẫn nào ở bên phải.

Mã nguồn

Tệp nguồn được xử lý như sau:

  • Nếu tệp chứa chuỗi ```, tệp sẽ được chia thành hai phần xung quanh lần xuất hiện đầu tiên của chuỗi đó. Tất cả các khoảng trắng hàng đầu và dấu được loại bỏ và phần đầu tiên được sử dụng làm mã nguồn cho Brian và phần thứ hai cho Chuck.
  • Nếu tệp không chứa chuỗi này, dòng đầu tiên của tệp sẽ được sử dụng làm nguồn cho Brian và phần thứ hai cho Chuck (ngoài dòng mới phân định, sẽ không có khoảng trắng nào bị xóa).
  • Tất cả các lần xuất hiện _trong cả hai chương trình đều được thay thế bằng byte NULL.
  • Hai băng nhớ được khởi tạo với mã ký tự tương ứng với chuỗi kết quả.

Ví dụ, tập tin nguồn sau đây

  abc
```
0_1
23

Sẽ mang lại các băng ban đầu sau đây:

Brian: [97 98 99 0 0 0 0 ...]
Chuck: [48 0 49 10 50 51 0 0 0 0 ...]

Thông dịch viên

Thông dịch viên được viết bằng Ruby. Phải có hai cờ dòng lệnh không được sử dụng bởi bất kỳ giải pháp nào (vì chúng không phải là một phần của đặc tả ngôn ngữ thực tế):

  • -d: Với cờ này, Brian và Chuck hiểu thêm hai lệnh. !sẽ in một chuỗi đại diện của cả hai băng nhớ, chương trình đang hoạt động được liệt kê đầu tiên (a ^sẽ đánh dấu các đầu băng hiện tại). @cũng sẽ làm điều này, nhưng sau đó ngay lập tức chấm dứt chương trình. Bởi vì tôi lười biếng, cả hai đều không hoạt động nếu chúng là lệnh cuối cùng trong chương trình, vì vậy nếu bạn muốn sử dụng chúng ở đó, hãy lặp lại chúng hoặc viết no-op sau chúng.
  • -D: Đây là chế độ gỡ lỗi dài dòng. Nó sẽ in thông tin gỡ lỗi giống như !sau mỗi lần đánh dấu. @cũng hoạt động trong chế độ này.

Đây là mã:

# coding: utf-8

class Instance
    attr_accessor :tape, :code, :ip

    OPERATORS = {
        '+'.ord  => :inc,
        '-'.ord  => :dec,
        '>'.ord  => :right,
        '<'.ord  => :left,
        '}'.ord  => :scan_right,
        '{'.ord  => :scan_left,
        '?'.ord  => :toggle,
        ','.ord  => :input,
        '.'.ord  => :output,
        '!'.ord  => :debug,
        '@'.ord  => :debug_terminate
    }

    OPERATORS.default = :nop

    def initialize(src)
        @code = src.chars.map(&:ord)
        @code = [0] if code.empty?

        @ip = 0
    end

    def tick
        result = :continue
        case OPERATORS[@code[@ip]]
        when :inc
            @tape.set(@tape.get + 1)
        when :dec
            @tape.set(@tape.get - 1)
        when :right
            @tape.move_right
        when :left
            @tape.move_left
        when :scan_right
            @tape.move_right while @tape.get != 0
        when :scan_left
            @tape.move_left while @tape.ip > 0 && @tape.get != 0
        when :toggle
            if @tape.get != 0
                @tape.move_right
                result = :toggle
            end
        when :input
            input
        when :output
            output
        when :debug
            result = :debug
        when :debug_terminate
            result = :debug_terminate
        end

        return :terminate if result != :toggle && @ip == @code.size - 1

        move_right if result != :toggle

        return result
    end

    def move_right
        @ip += 1
        if @ip >= @code.size
            @code << 0
        end
    end

    def move_right
        @ip += 1
        if @ip >= @code.size
            @code << 0
        end
    end

    def move_left
        @ip -= 1 if @ip > 0
    end

    def get
        @code[@ip]
    end

    def set value
        @code[@ip] = value
    end

    def input() end
    def output() end

    def to_s
        str = self.class.name + ": \n"
        ip = @ip
        @code.map{|i|(i%256).chr}.join.lines.map do |l|
            str << l.chomp << $/
            str << ' '*ip << "^\n" if 0 <= ip && ip < l.size
            ip -= l.size
        end
        str
    end
end

class Brian < Instance
    def input
        byte = STDIN.read(1)
        @tape.set(byte ? byte.ord : -1)
    end
end

class Chuck < Instance
    def output
        $> << (@tape.get % 256).chr
    end
end

class BrianChuck

    class ProgramError < Exception; end

    def self.run(src, debug_level=0)
        new(src, debug_level).run
    end

    def initialize(src, debug_level=false)
        @debug_level = debug_level

        src.gsub!('_',"\0")

        if src[/```/]
            brian, chuck = src.split('```', 2).map(&:strip)
        else
            brian, chuck = src.lines.map(&:chomp)
        end

        chuck ||= ""

        brian = Brian.new(brian)
        chuck = Chuck.new(chuck)

        brian.tape = chuck
        chuck.tape = brian

        @instances = [brian, chuck]
    end

    def run
        (puts @instances; puts) if @debug_level > 1

        loop do
            result = current.tick

            toggle if result == :toggle

            (puts @instances; puts) if @debug_level > 1 || @debug_level > 0 && (result == :debug || result == :debug_terminate)

            break if result == :terminate || @debug_level > 0 && result == :debug_terminate
        end
    end

    private

    def current
        @instances[0]
    end

    def toggle
        @instances.reverse!
    end
end

case ARGV[0]
when "-d"
    debug_level = 1
when "-D"
    debug_level = 2
else
    debug_level = 0
end

if debug_level > 0
    ARGV.shift
end

BrianChuck.run(ARGF.read, debug_level)

Đây là giải pháp (viết tay) của riêng tôi cho vấn đề:

>}>}>
brace left: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
brace left: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>} Append a bunch of 1s as a dummy list element:
+>+>+>+>+>+>+>+>+>+
Append two 1s and some code to the list; the first is a marker for later; the latter will be the integer
1: >+
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1: >+
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow right: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_
{<<<<<<<<<{<{    Move back to the start
Read a character and subtract 48 to get actual 0 or 1
,------------------------------------------------
?   If 1 switch to Chuck otherwise continue
>}>}>>>>>>>>>}<<<<<<- Subtract 1 from the result to account for initial 1
?   If integer was positive switch to Chuck
@todo: process end
_
This code is run when reading 1:
}>}>>>>>>>>>}<<<<<<+ Move to the end of Chuck; skip one null cell; move to the end of the list
{<<<<<<<<<{<?   Move back to the code that resets this loop.
_
This code is run after finalising an integer:
change the code after the integer first
<<--<--<--}
Append two 1s and some code to the list; the first is a marker for later; the latter will be the integer
1: +
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1: >+
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow right: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
brace right: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
{<<<<<<<+<<{<{    Move back to the start; incrementing the list length
Read a character and subtract 48 to get actual 0 or 1
,------------------------------------------------
?   If 1 switch to Chuck otherwise continue
>}>}>>>>>>>>>}
Leave the resetting code, but remove the rest of the last list element:
<<<--<--<--
1: <-
question mk: <---------------------------------------------------------------
arrow left: <------------------------------------------------------------
brace right: <-----------------------------------------------------------------------------------------------------------------------------
1: <-
<{< Move back to the cell we reserved for the counter
<<<<<<-- decrement list size by two so we don't process the two largest elements
_

<{<<<<<<{<{<{<{<{>}>}>>>>>>> This is the beginning of the loop which decrements all list elements by 1
+ Add 1 to the running total
>>- Set marker of dummy list element to zero
_ Beginning of loop that is run for each list element
<{<<<<<<{<{<{<{<{>}>}>}>}+ set last marker back to 1
>>>>>>>>>> move to next marker
? Skip the next bit if we're not at the end of the list
<? Move back to the beginning of the loop
@ we should never get here
_
This is run when we're not at the end of the list
<<<- Set marker to 0 to remember current position
>>>>- Move to the current value and decrement it
? Move back to loop beginning if value is not zero yet
- Make element negative so it's skipped in the future
{<{<{>- Move to remaining list length and decrement it
? If it's nonzero switch to Chuck
>>>>>>>>->->->->->->->->->- Remove the dummy list to make some space for new code:
>}+ Fill in the marker in the list
{<<<<<<<<< Add some loop resetting code after the result:
brace left: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arrow left: >++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
question mk: >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_
This loop adds a print command to Chuck's code while decrementing the result
>>>>>>>>}>>>>>}>>>>>} Move to the very end of Chuck's code and leave some space to seek the 1
print: ++++++++++++++++++++++++++++++++++++++++++++++
{<<<<<{<<<<<{<<<<<<<{<
- decrement the result
? if nonzero run loop again
At this point we just need to make Chuck seek for the 1 at the end of this code print it often enough
>>}>>>>>>>}>>>>>}
arrow right: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<?1 Switch to Chuck which moves Brian's tape head onto the 1 and then prints it N times


```

_   Dummy cell to hold input
This code is run when reading a 1:
{<{<{<{ ensure we're at the start
}>}<? Skip 0 handling code and switch to Brian
_ This code is run after a 1 has been processed:
{<{<?

Mã này có thể chạy được, vì tất cả các chú thích đều sử dụng no-op và được bỏ qua bởi {}.

Ý tưởng cơ bản là:

  1. Thêm một phần tử zero mới vào danh sách (ở cuối băng Chuck) và tăng độ dài danh sách lên 1.
  2. Trong khi chúng ta đọc 1s, tăng phần tử đó.
  3. Khi chúng ta đọc một 0, làm một số dọn dẹp. Nếu số nguyên kết quả lớn hơn 0, quay lại 1.

    Bây giờ chúng tôi đã có một danh sách các số đầu vào ở cuối băng của Chuck và chúng tôi biết độ dài của danh sách.

  4. Trừ 2 từ độ dài của danh sách (vì vậy các bước sau bỏ qua hai phần tử lớn nhất), gọi đây N.

  5. Trong khi N > 0, tăng tổng số đang chạy và sau đó giảm tất cả các thành phần danh sách. Bất cứ khi nào một phần tử danh sách đạt 0, giảm N.

    Khi kết thúc điều này, tổng số đang chạy sẽ chứa số lớn thứ ba trong đầu vào , M.

  6. Viết các Mbản sao .đến cuối băng của Chuck.

  7. Trên Chuck, tìm kiếm 1trên băng của Brian và sau đó thực hiện những thứ được tạo .ở cuối.

Sau khi hoàn thành điều này tôi nhận ra rằng tôi có thể đơn giản hóa nó khá nhiều ở một số nơi. Chẳng hạn, thay vì theo dõi bộ đếm đó và ghi chúng .vào băng của Chuck, tôi chỉ có thể in 1ngay lập tức, mỗi lần trước khi tôi giảm tất cả các thành phần trong danh sách. Tuy nhiên, thực hiện các thay đổi đối với mã này là khá khó khăn, vì nó truyền bá các thay đổi khác ở mọi nơi, vì vậy tôi không buồn thực hiện thay đổi.

Điều thú vị là làm thế nào để theo dõi danh sách và cách lặp qua nó. Bạn không thể lưu trữ các số ngược lại trên băng của Chuck, vì sau đó nếu bạn muốn kiểm tra một điều kiện trên một trong các thành phần của danh sách, bạn có nguy cơ thực hiện phần còn lại của danh sách có thể chứa mã hợp lệ. Bạn cũng không biết danh sách này sẽ dài bao nhiêu, vì vậy bạn không thể dành một khoảng trống trước mã của Chuck.

Vấn đề tiếp theo là chúng ta cần phải rời khỏi danh sách để giảm dần Ntrong khi chúng ta xử lý nó và chúng ta cần quay lại cùng một nơi với chúng ta trước đây. Nhưng {}sẽ chỉ bỏ qua toàn bộ danh sách.

Vì vậy, chúng ta cần phải viết một số mã động lên Chuck. Trong thực tế, mỗi phần tử danh sách icó dạng:

[1 <Code A> i <Code B>]

1là một điểm đánh dấu mà chúng ta có thể đặt thành 0 để chỉ ra nơi chúng ta dừng xử lý danh sách. Mục đích của nó là để bắt {hoặc }sẽ chỉ vượt qua mã và i. Chúng tôi cũng sử dụng giá trị này để kiểm tra xem chúng tôi có ở cuối danh sách trong quá trình xử lý hay không, vì vậy trong khi chúng tôi thì không, điều này sẽ 1và điều kiện ?sẽ chuyển điều khiển sang Chuck. Code Ađược sử dụng để xử lý tình huống đó và di chuyển IP trên Brian cho phù hợp.

Bây giờ khi chúng ta giảm giá, ichúng ta cần kiểm tra xem iđã bằng không. Trong khi nó không, ?sẽ một lần nữa chuyển đổi điều khiển, vì vậy Code Bcó để xử lý đó.



@cardboard_box Đẹp!
mbomb007

15

HPR, được viết bằng Python 3 ( Được bẻ khóa bởi TheNumberOne )

HPR (tên có nghĩa là không có gì) là ngôn ngữ để xử lý danh sách các số nguyên. Nó được thiết kế tối giản , cực kỳ hạn chếkhông có giới hạn "nhân tạo" . Lập trình trong HPR rất khó, không phải vì bạn phải giải một câu đố để khiến người phiên dịch không hét vào mặt bạn, mà bởi vì thật khó để làm cho một chương trình làm bất cứ điều gì hữu ích. Tôi không biết liệu HPR có khả năng làm bất cứ điều gì thú vị hơn nhiều so với việc tính toán yếu tố lớn thứ ba trong danh sách hay không.

Đặc tả ngôn ngữ

Một chương trình HPR được chạy trong một môi trường , đó là một tập hợp các số nguyên không âm không trùng lặp và danh sách các số nguyên không âm. Ban đầu, môi trường chỉ chứa danh sách đầu vào (trình thông dịch phân tích cú pháp cho bạn). Có ba lệnh và hai toán tử "luồng điều khiển" sửa đổi môi trường:

  • *loại bỏ phần tử đầu tiên của mọi danh sách không trống trong môi trường và đặt nó vào môi trường. Danh sách trống không bị ảnh hưởng. Ví dụ, nó biến đổi

    {4,1,[0,2],[1,3],[]} -> {4,1,0,[2],[3],[]}
    
  • -giảm tất cả các số trong môi trường và sau đó loại bỏ các phần tử âm. Ví dụ, nó biến đổi

    {4,2,0,[0,2],[4,4,4]} -> {3,1,[0,2],[4,4,4]}
    
  • $xoay mọi danh sách trong môi trường một bước sang trái. Ví dụ, nó biến đổi

    {4,1,[0,2],[3,4,1]} -> {4,1,[2,0],[4,1,3]}
    
  • !(A)(B), ở đâu ABlà các chương trình, về cơ bản là một whilevòng lặp. Nó thực hiện "hành động" Acho đến khi "thử nghiệm" Bsẽ dẫn đến một môi trường trống. Điều này có thể tạo ra một vòng lặp vô hạn.
  • #(A)(B), ở đâu ABlà các chương trình, áp dụng ABcho môi trường hiện tại và lấy sự khác biệt đối xứng của kết quả.

Các lệnh được thực hiện từ trái sang phải. Cuối cùng, kích thước của môi trường được in bằng unary.

Thông dịch viên

Trình thông dịch này có lệnh gỡ lỗi ?, in ra môi trường mà không sửa đổi nó. Nó không nên xuất hiện trong bất kỳ giải pháp cho nhiệm vụ. Bất kỳ ký tự nào ngoại trừ *-$!#()?đơn giản là bị bỏ qua, vì vậy bạn có thể viết bình luận trực tiếp vào mã. Cuối cùng, trình thông dịch nhận ra thành ngữ !(A)(#(A)())là "thực hiện Acho đến khi kết quả không còn thay đổi" và tối ưu hóa nó để có thêm tốc độ (tôi cần nó để giải pháp của tôi kết thúc trong vòng một giờ trong trường hợp thử nghiệm cuối cùng).

import sys

def parse(prog):
    "Parse a prefix of a string into an AST. Return it and the remaining input."
    ret = []
    while prog:
        if prog[0] in "#!":
            sub1, prog1 = parse(prog[2:])
            sub2, prog2 = parse(prog1[1:])
            ret += [prog[0],sub1,sub2]
            prog = prog2
        elif prog[0] == ')':
            prog = prog[1:]
            break
        else:
            ret += [prog[0]]
            prog = prog[1:]
    return ret, prog

def intp(ast, L_env, N_env):
    "Interpret the AST on an environment, return the resulting environment."
    ptr = 0
    while ptr < len(ast):
        cmd = ast[ptr]
        if cmd == '*':
            N_env = N_env | set(L[0] for L in L_env if L)
            L_env = set(L[1:] for L in L_env)
            ptr += 1
        elif cmd == '-':
            N_env = set(N-1 for N in N_env if N>0)
            ptr += 1
        elif cmd == '$':
            L_env = set(L[1:]+L[:1] for L in L_env)
            ptr += 1
        elif cmd == '!':
            # Speed optimization
            cond = ast[ptr+2]
            if cond == ['#', ast[ptr+1], []]:
                L_next, N_next = intp(ast[ptr+1], L_env, N_env)
                while L_next != L_env or N_next != N_env:
                    L_env, N_env = L_next, N_next
                    L_next, N_next = intp(ast[ptr+1], L_env, N_env)
            else:
                while True:
                    L_test, N_test = intp(cond, L_env, N_env)
                    if not L_test and not N_test:
                        break
                    L_env, N_env = intp(ast[ptr+1], L_env, N_env)
            ptr += 3
        elif cmd == '#':
            L_env1, N_env1 = intp(ast[ptr+1], L_env, N_env)
            L_env2, N_env2 = intp(ast[ptr+2], L_env, N_env)
            L_env = L_env1 ^ L_env2
            N_env = N_env1 ^ N_env2
            ptr += 3
        elif cmd == '?':
            print(L_env | N_env)
            ptr += 1
        else:
            ptr += 1
    return L_env, N_env

def main(p, L):
    "Run the program on the input, return the output."
    # Parse the input list
    L = ''.join(c for c in L if c in "01")
    while "00" in L:
        L = L.replace("00","0")
    L = [-2] + [i for i in range(len(L)-1) if L[i:i+2] == "10"]
    L = tuple(b-a-1 for (a,b) in zip(L, L[1:]))
    # Parse the program
    ast = parse(p)[0]
    L_env, N_env = intp(ast, set([L]), set())
    return '1'*(len(L_env) + len(N_env))

if __name__ == "__main__":
    filename = sys.argv[1]
    f = open(filename, 'r')
    prog = f.read()
    f.close()
    L = input()
    print(main(prog, L))

Giải pháp của tôi

Giải pháp tham chiếu của tôi dài 484 byte, do đó khá ngắn so với chương trình 3271 byte của TheNumberOne. Điều này rất có thể là do hệ thống macro tinh vi và tuyệt vời TheNumberOne được phát triển để lập trình trong HPR. Ý tưởng chính trong cả hai chương trình của chúng tôi là tương tự nhau:

  1. Tìm hiểu làm thế nào để sản xuất các yếu tố tối đa của một danh sách.
  2. Để loại bỏ phần tử tối đa, xoay danh sách cho đến khi phần tử đầu tiên bằng giá trị tối đa, sau đó bật nó.
  3. Xóa tối đa hai lần, sau đó in phần tử tối đa mới.

Tuy nhiên, theo như tôi có thể nói, các chi tiết thực hiện chính xác là khá khác nhau. Đây là giải pháp của tôi:

!($)(!(-)(#(-)())#(!(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))(#(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))())#(-)(#(!(-)(#(-)()))()))(*)#(!(-)(#(-)()))())*!(-)(#(-)())!($)(!(-)(#(-)())#(!(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))(#(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))())#(-)(#(!(-)(#(-)()))()))(*)#(!(-)(#(-)()))())*!(-)(#(-)())!(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))(#(-#(!(*)(#(*)())#(!(-)(#(-)()))())(!(-)(#(-)())))())-#(!(-)(#(-)()))()

Và đây là chương trình Python nhận xét tạo ra nó (không có gì lạ mắt ở đây, chỉ là thao tác chuỗi cơ bản để thoát khỏi tất cả sự lặp lại):

# No numbers in the environment
no_nums = "#(-)()"
# No non-empty lists in the environment
no_lists = "#(*)()"
# Remove all numbers from the environment
del_nums = "!(-)(" + no_nums + ")"
# Remove all lists from the environment
del_lists = "#(" + del_nums + ")()"
# Splat the list to the environment:
#  pop the list until it's empty and delete the empty list,
#  then take symmetric difference with all numbers removed
splat = "#(!(*)(" + no_lists + ")" + del_lists + ")(" + del_nums + ")"
# Put into the environment all numbers up to list maximum:
#  decrement and splat until a fixed point is reached.
#  Without the speed optimization, this is very slow if the list elements are large.
splat_upto = "!(-" + splat + ")(#(-" + splat + ")())"
# Copy maximum element to environment:
#  take all elements up to maximum,
#  then take symmetric difference of decrement and list deletion
copy_max = splat_upto + "#(-)(" + del_lists + ")"
# Delete maximum element from list:
#  rotate until first element is maximal, then pop and delete it
del_max = "!($)(" + del_nums + "#(" + copy_max + ")(*)" + del_lists + ")*" + del_nums
# Full program:
#  remove the maximum twice, then produce all numbers up to maximum,
#  then decrement and remove lists. The environment will contain exactly
#  the integers from 0 to third largest - 1, and their number is printed.
main = del_max + del_max + splat_upto + "-" + del_lists
print(main)


@TheNumberOne Đã thêm giải pháp của tôi.
Zgarb

12

TKDYNS (Để giết rồng, bạn cần một thanh kiếm) - Bị bẻ khóa bởi Martin Büttner

EDIT: Tôi đã thêm giải pháp và giải thích của tôi bên dưới bài chính.

Lý lịch

Trong ngôn ngữ này, bạn điều khiển một chiến binh dũng cảm, người được giao nhiệm vụ giết một con rồng khủng khiếp. Con rồng sống trong một mê cung dưới lòng đất, đầy hiểm nguy và nguy hiểm, và cho đến bây giờ, không ai có thể vạch ra nó và sống sót. Điều này có nghĩa là bạn phải điều hướng theo cách của bạn đến con rồng trong bóng tối, không có gì ngoài trực giác và sự dũng cảm để hướng dẫn bạn.

...

Vâng, không hoàn toàn. Bạn đã mang theo một nguồn cung cấp tay sai dùng một lần gần như vô hạn và họ sẵn sàng đi trước bạn để khám phá con đường an toàn. Thật không may, tất cả chúng đều dày như hai tấm ván ngắn và sẽ chỉ làm chính xác những gì bạn bảo họ làm. Tùy thuộc vào bạn để đưa ra một phương pháp thông minh để đảm bảo rằng tay sai của bạn khám phá đúng đường dẫn.

Một số chi tiết

Hang ổ của rồng có dạng lưới 10x10. Giữa các điểm liền kề nhất định trong lưới điện, có một lối đi hẹp; giữa những người khác, có một khoảng cách sâu và cái chết nhất định. Một cách bố trí ví dụ cho lưới 4x4 có thể như sau:

 0---1   2---3
     |   |   |
 4---5---6   7
 |           |
 8   9--10  11
     |       |
12--13--14--15

Bạn biết rằng luôn có một cách để đi từ điểm này đến điểm khác, nhưng không nhiều hơn điều đó đã được tiết lộ cho bạn.

Để đánh bại rồng thành công, trước tiên bạn cần thu thập một số vật phẩm mà bạn sẽ có thể kết hợp với nhau để tạo ra một lưỡi kiếm giết rồng ma thuật. Thuận tiện, tất cả các mảnh cho vũ khí này đã được đặt rải rác xung quanh hang ổ của rồng. Bạn chỉ cần thu thập chúng.

Điều khó hiểu là mỗi phần của vũ khí đã bị vướng bẫy. Mỗi khi được thu thập, bố cục của lối đi thay đổi. Những con đường an toàn trước đây có thể dẫn đến cái chết nhất định và ngược lại.

Các lệnh

Chỉ có 5 lệnh hợp lệ.

  • < - Bước sang trái

  • > - Bước sang phải

  • ^ - Tiến lên một bước

  • v - Bước xuống dưới

  • c- Thu thập bất kỳ mục nào xảy ra để nằm xung quanh vị trí hiện tại của bạn. Nếu có vật phẩm, bố cục của hang ổ thay đổi. Với các vị trí được đánh số theo hàng như trên, hãy lấy modulo vị trí của bạn 10. Có 10 bố cục được mã hóa cứng vào trình thông dịch và bố cục thay đổi thành vị trí tương ứng. Ví dụ: nếu bạn ở vị trí 13, thì bố cục thay đổi thànhlayouts[3]

Các bố cục khi chúng xuất hiện trong interpeter đã được mã hóa thành các số nguyên theo cách sau:

  • Bố cục trống được mã hóa thành không.

  • Đối với mỗi cạnh trong bố cục, hãy xnhỏ hơn trong hai vị trí mà nó tham gia.

    • Nếu bước nằm ngang, hãy thêm 2^(2*x)vào mã hóa (đó là sức mạnh của, không phải XOR)

    • Nếu bước là dọc, thêm 2^(2*x + 1)vào mã hóa.

Luồng thực thi

Trình thông dịch được chạy với tên của tệp nguồn dưới dạng đối số dòng lệnh.

Khi trình thông dịch được chạy, nó sẽ nhắc người dùng cung cấp một nhiệm vụ. Đầu vào này phải ở dạng được mô tả trong câu hỏi và xác định vị trí trong hang ổ của các thành phần của vũ khí. Cụ thể, mỗi số nguyên đầu vào được lấy modulo 100 và được đặt tại vị trí tương ứng trong hang ổ.

Mỗi chương trình nguồn bao gồm một số dòng, mỗi dòng bao gồm một số chuỗi gồm 5 ký tự hợp lệ ở trên. Những dòng này đại diện cho tay sai của bạn. Bạn, chiến binh, theo dõi một chuỗi các hành động được biết là an toàn. Ban đầu, bạn không biết gì về hang ổ, vì vậy trình tự này trống rỗng. Lần lượt lấy từng minion, việc sau được thực hiện, bắt đầu từ vị trí 0:

  • Các minion được hướng dẫn thực hiện tất cả các hành động được biết là an toàn, theo sau là các hành động trong dòng mã riêng của nó.

    • Nếu minion chết tại bất kỳ thời điểm nào, bạn sẽ được thông báo về điều này và hang ổ đặt lại cấu hình ban đầu. Tất cả các mục được thay thế, và lối đi trở về vị trí bắt đầu của chúng.

    • Thay vào đó, nếu, thay vào đó, minion vẫn tồn tại, sau đó bạn sẽ bốc hơi nó - dù sao thì nó cũng chỉ là một minion. Như trước đây, điều này kích hoạt việc đặt lại hang ổ về trạng thái ban đầu, nhưng lần này, bạn nối các hành động từ dòng mã của minion vào chuỗi các hành động được biết là an toàn.

Khi tất cả các tay sai đã kiệt sức, bạn, chiến binh, thực hiện tất cả các hành động được biết là an toàn, một lần nữa bắt đầu từ vị trí 0. Có hai kết quả có thể xảy ra:

  • Bạn thu thập tất cả các mảnh của vũ khí - trong trường hợp này, bạn giết thành công con rồng và một thông điệp chiến thắng thú vị là đầu ra. Thông điệp chiến thắng này sẽ chứa, trong số các nhân vật khác, nnhững nhân vật, nlà số cao thứ ba được cung cấp làm đầu vào.

  • Bạn đã thất bại trong việc thu thập một số mảnh của vũ khí - trong trường hợp này, con rồng vẫn sống và bạn đã thất bại trong nhiệm vụ của mình.

Mã thông dịch viên (Python 2)

import collections
import copy
import sys

size = 10
layouts = [108705550239329248440770931020110627243913363144548922111951,108386637020100924277694952798729434993885646706222210602969,133885860318189115027934177821170081234850573770998325845482,102397795295522647918061101991513921833376565032742993744791,131948019244359669407266662537098175265242405785636894694611,127512068876349726396523358265982765442984953916545984706958,106817519055019354200334114020150263381328246524221867629943,33472343358375525802921815863230485208221126168622186265959,133909781123963725874096031069972704024813281938330035579187,132244654022332695610020359820518831299843076834682749020986]

class Interpreter(object):

    def __init__(self, source_file):
        self.source_filename = source_file
        self.layout = layouts[0]
        self.position = 0
        self.ingredients = []
        self.quest_items = {}
        self.attempts = collections.deque()

        self.safe_position = 0
        self.safe_ingredients = []
        self.safe_quest_items = {}
        self.safe_layout = layouts[0]

    def get_input(self):
        s = raw_input("Which quest would you like to embark on today?")
        ind = s.find('00')
        s = s[:ind]
        items = map(len, s.split('0'))
        for item in items:
            if item not in self.safe_quest_items:
                self.safe_quest_items[item] = 1
            else:
                self.safe_quest_items[item] += 1

    def parse_attempts(self):
        with open(self.source_filename, 'r') as source:
            for line in source:
                self.attempts.append(line.strip())

    def enc(self, x, y):
        x, y = min(x, y), max(x, y)
        if x < 0:
            return 0
        if y == x + 1:
            return 1 << (2*x)
        elif y == x + size:
            return 1 << (2*x + 1)
        else:
            return 0

    def exec_command(self, char):
        if char == '<':
            if self.enc(self.position, self.position-1) & self.layout:
                self.position -= 1
            else:
                return False
        elif char == '>':
            if self.enc(self.position, self.position+1) & self.layout:
                self.position += 1
            else:
                return False
        elif char == '^':
            if self.enc(self.position, self.position-size) & self.layout:
                self.position -= size
            else:
                return False
        elif char == 'v':
            if self.enc(self.position, self.position+size) & self.layout:
                self.position += size
            else:
                return False
        elif char == 'c':
            for item in xrange(self.position, 10**6, size*size):
                if item in self.quest_items:
                    self.ingredients += ([item] * self.quest_items.pop(item))
                    self.ingredients.sort()
                    self.layout = layouts[self.position % 10]
        else:
            raise ValueError

        return True

    def run_minion(self):
        minion = self.attempts.popleft()
        self.position = self.safe_position
        self.ingredients = copy.copy(self.safe_ingredients)
        self.quest_items = copy.copy(self.safe_quest_items)
        self.layout = self.safe_layout
        dead = False

        for cmd in minion:
            if not self.exec_command(cmd):
                dead = True

        if not dead:
            self.safe_position = self.position
            self.safe_ingredients = copy.copy(self.ingredients)
            self.safe_quest_items = copy.copy(self.quest_items)
            self.safe_layout = self.layout

    def process_minions(self):
        while self.attempts:
            self.run_minion()

    def final_run(self):
        if len(self.safe_quest_items) == 0:
            print "You killed the dragon" + "!!1" * self.safe_ingredients[-3]
        else:
            print "The dragon lives on. Better luck next time..."

    def interpret(self):
        self.get_input()
        self.parse_attempts()
        self.process_minions()
        self.final_run()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print "Wrong number of arguments"
    else:
        test = Interpreter(sys.argv[1])
        test.interpret()

Tốt nhất của may mắn, chiến binh dũng cảm.

Giải pháp và giải thích của tôi

Đây là tập lệnh Python tạo mã nguồn để giải quyết vấn đề. Để quan tâm, mã nguồn cuối cùng của Martin nhỏ hơn khoảng 5 lần so với mã được tạo bởi tập lệnh của tôi. Mặt khác, tập lệnh tạo mã của tôi nhỏ hơn khoảng 15 lần so với chương trình Mathicala của Martin ...

size = 10
layouts = [108705550239329248440770931020110627243913363144548922111951,108386637020100924277694952798729434993885646706222210602969,133885860318189115027934177821170081234850573770998325845482,102397795295522647918061101991513921833376565032742993744791,131948019244359669407266662537098175265242405785636894694611,127512068876349726396523358265982765442984953916545984706958,106817519055019354200334114020150263381328246524221867629943,33472343358375525802921815863230485208221126168622186265959,133909781123963725874096031069972704024813281938330035579187,132244654022332695610020359820518831299843076834682749020986]

def enc(edge):
    x,y = min(edge), max(edge)
    if x < 0:
        return 0
    if y == x + 1:
        return 1 << (2*x)
    elif y == x + size:
        return 1 << (2*x + 1)

def path(x, y, tree):
    stack = [(x,'')]
    while stack[-1][0] != y:
        vertex, path = stack.pop()
        if (enc((vertex, vertex+1))&tree) and ((not path) or path[-1] != '<'):
            stack.append((vertex+1, path+'>'))
        if (enc((vertex, vertex-1))&tree) and ((not path) or path[-1] != '>'):
            stack.append((vertex-1, path+'<'))
        if (enc((vertex, vertex+size))&tree) and ((not path) or path[-1] != '^'):
            stack.append((vertex+size, path+'v'))
        if (enc((vertex, vertex-size))&tree) and ((not path) or path[-1] != 'v'):
            stack.append((vertex-size, path+'^'))
    return stack[-1][1]

def reverse(path):
    rev = ''
    for i in xrange(len(path)-1, -1 ,-1):
        if path[i] == '<':
            rev += '>'
        if path[i] == '>':
            rev += '<'
        if path[i] == 'v':
            rev += '^'
        if path[i] == '^':
            rev += 'v'
    return rev

def assert_at(x, tree):
    path_ul = path(x, 0, tree)
    path_dr = path(x, size*size - 1, tree)
    return path_ul + reverse(path_ul) + path_dr + reverse(path_dr)

def safe_path(x, y, tree):
    return assert_at(x, tree) + path(x, y, tree)

with open('source.txt', 'w') as f:
    for x in xrange(size*size - 1):
        for tree in layouts:
            f.write(safe_path(x, x+1, tree) + 'c\n')

Cấu trúc cơ bản

Điều này tạo ra 990 dòng mã nguồn, thuộc nhóm 10. 10 dòng đầu tiên đều chứa các hướng dẫn cố gắng di chuyển một minion từ vị trí này 0sang vị trí khác 1, sau đó thu thập một mục - một bộ cho mỗi bố cục có thể. Tất cả 10 dòng tiếp theo đều chứa các hướng dẫn cố gắng di chuyển một minion từ vị trí này 1sang vị trí khác 2, sau đó thu thập một vật phẩm. Và cứ thế ... Những đường dẫn này được tính toán với pathhàm trong tập lệnh - nó chỉ thực hiện tìm kiếm theo chiều sâu đơn giản.

Nếu chúng ta có thể đảm bảo rằng, trong mỗi nhóm 10, chính xác một minion thành công, thì chúng ta sẽ giải quyết được vấn đề.

Vấn đề với cách tiếp cận

Không phải lúc nào cũng có thể chính xác là một minion trong nhóm 10 người thành công - ví dụ, một minion được thiết kế để đi từ vị trí này 0sang vị trí khác 1có thể vô tình thành công trong việc chuyển từ vị trí này 1sang vị trí khác 2(do sự tương đồng trong bố cục), và rằng tính toán sai lầm sẽ lan truyền qua, có khả năng gây ra thất bại.

Cách khắc phục

Cách khắc phục mà tôi đã sử dụng như sau:

Đối với mỗi minion đang cố gắng đi từ vị trí này nđến n+1, trước tiên hãy bắt anh ta đi từ vị trí này nsang vị trí khác 0(góc trên bên trái) và quay lại, sau đó từ vị trí này nsang vị trí khác 99(góc dưới bên phải) và quay lại. Các hướng dẫn này chỉ có thể được thực hiện một cách an toàn từ vị trí n- bất kỳ vị trí bắt đầu nào khác và minion sẽ đi ra khỏi rìa.

Do đó, các hướng dẫn bổ sung này sẽ ngăn các tay sai vô tình đi đâu đó mà họ không có ý định và điều này đảm bảo rằng chính xác một minion từ mỗi nhóm 10 thành công. Lưu ý rằng đó không nhất thiết là minion mà bạn mong đợi - đó có thể là trường hợp mà minion tin rằng anh ta ở bố cục 0 thành công, ngay cả khi chúng ta thực sự ở bố cục 7 - nhưng trong mọi trường hợp, thực tế là chúng ta có bây giờ thay đổi vị trí có nghĩa là tất cả các tay sai khác trong nhóm sẽ nhất thiết phải chết. Các bước bổ sung này được tính bởi assert_athàm và safe_pathhàm trả về một đường dẫn là sự kết hợp của các bước bổ sung này với đường dẫn thông thường.


1
Nứt. Điều này thật thú vị, nhưng tôi nghĩ nó mang đến một vấn đề với quy tắc "ngôn ngữ lập trình của bạn chỉ phải giải quyết một nhiệm vụ này", vì việc giải câu đố của bạn không liên quan gì đến việc thực sự giải quyết nhiệm vụ lập trình đó. ;)
Martin Ender

Tôi đã tạo ra câu trả lời này chủ yếu vì tôi nhận thấy vấn đề đó tồn tại - và dường như không có bất cứ điều gì ngăn cản ai đó sử dụng một vấn đề tính toán thực sự khó khăn ở vị trí của nó. Lệnh cấm 'không có tiền điện tử' dường như bị thu hẹp tùy tiện ...
Sam Cappleman-Lynes

thật. Tôi đoán đó là lý do tại sao đây là một cuộc thi phổ biến. Nếu vấn đề mang tính chất tính toán nhiều hơn và không được gói gọn trong một câu chuyện hay như nhiều phiếu bầu như một câu trả lời với một ngôn ngữ phù hợp (có khả năng hoàn thành Turing) thực sự rất khó sử dụng.
Martin Ender

Đã thêm giải pháp của tôi cho quan tâm. Cũng vì sở thích, ban đầu tôi đã thiết kế câu đố với lưới 1000x1000, nhưng vì lợi ích của việc không có bố cục được mã hóa thành ~ 600000 chữ số mà tôi đã chọn cho kích thước nhỏ hơn.
Sam Cappleman-Lynes

8

Firetype, Cracked bởi Martin Büttner

Một sự pha trộn thực sự kỳ lạ của BF và CJam. Và ai biết những gì khác! Khá chắc chắn rằng đây sẽ là một điều dễ dàng, nhưng dù sao nó cũng rất vui. FYI, cái tên đang ám chỉ Vermillion Fire từ Final Fantasy Type-0 .

LƯU Ý : Xin vui lòng tha thứ cho tôi cho bất kỳ sự mơ hồ trong mô tả này. Tôi không phải là nhà văn giỏi nhất ...: O

Martin đã phá vỡ điều này thực sự nhanh chóng! Đây là chương trình ban đầu của tôi để giải quyết vấn đề:

_
,
^
~
+
|
|
-
%
+
_
+
=











`
~
+
|
%

_
=

\
@
-
-
!
<
>
>
>

_
+
.
<
-
`
~
~
+
|
|
-
#
%

Cú pháp

Một kịch bản Firetype về cơ bản là một danh sách các dòng. Ký tự đầu tiên của mỗi dòng là lệnh chạy. Các dòng trống về cơ bản là NOP.

Ngữ nghĩa

Bạn có một mảng các số nguyên và một con trỏ (nghĩ BF). Bạn có thể di chuyển sang trái và phải hoặc "đẩy" các phần tử lên mảng.

Đẩy

Khi bạn "đẩy" một phần tử và bạn ở cuối mảng, một phần tử phụ sẽ được thêm vào cuối. Nếu bạn không ở cuối, phần tử tiếp theo sẽ bị ghi đè. Bất kể, con trỏ sẽ luôn được tăng lên.

Các lệnh

_

Đẩy một số không vào mảng.

+

Tăng phần tử tại con trỏ hiện tại.

-

Giảm phần tử tại con trỏ hiện tại.

*

Nhân đôi phần tử tại con trỏ hiện tại.

/

Giảm một phần tử tại con trỏ hiện tại.

%

Lấy phần tử ở con trỏ hiện tại và nhảy về phía trước nhiều dòng và di chuyển con trỏ sang trái. Nếu giá trị âm, thay vào đó hãy nhảy lùi lại.

=

Lấy phần tử tại con trỏ hiện tại và nhảy đến dòng đó + 1. Ví dụ, nếu phần tử hiện tại là 0, phần tử này sẽ nhảy đến dòng 1. Điều này cũng di chuyển con trỏ sang trái.

,

Đọc một ký tự từ đầu vào tiêu chuẩn và đẩy giá trị ASCII của nó.

^

Lấy phần tử tại con trỏ hiện tại, diễn giải nó thành giá trị ASCII cho một ký tự và chuyển đổi nó thành một số nguyên. Chẳng hạn, nếu giá trị hiện tại là 49 (giá trị ASCII 1), phần tử tại con trỏ hiện tại sẽ được đặt thành số nguyên 1.

.

Viết số hiện tại lên màn hình.

!

Lấy phần tử ở con trỏ hiện tại và lặp lại dòng tiếp theo nhiều lần. Cũng di chuyển con trỏ sang trái.

<

Di chuyển con trỏ sang trái. Nếu bạn đã ở đầu, một lỗi sẽ được đưa ra.

>

Di chuyển con trỏ sang phải. Nếu bạn đã ở cuối, một lỗi sẽ được đưa ra.

~

Nếu phần tử hiện tại là khác không, thay thế nó bằng 0; nếu không, thay thế nó bằng 1.

|

Bình phương phần tử hiện tại.

@

Đặt phần tử hiện tại theo chiều dài của mảng.

`

Nhân đôi yếu tố hiện tại.

\

Sắp xếp các phần tử tại và trước con trỏ.

#

Phủ định yếu tố hiện tại.

Thông dịch viên

Cũng có sẵn tại Github .

#!/usr/bin/env python

# The FireType interpreter.
# Runs under Python 2 and 3.
import sys

class Array(object):
    def __init__(self):
        self.array = []
        self.ptr = 0

    def push_zero(self):
        if self.ptr == len(self.array):
            self.array.append(0)
        else:
            self.array[self.ptr] = 0
        self.ptr += 1

    def left(self):
        self.ptr -= 1
        assert self.ptr >= 0 and self.array, 'pointer underflow (at %d)' % i

    def right(self):
        self.ptr += 1
        assert self.ptr <= len(self.array), 'pointer overflow (at %d)' % i

    def sort(self):
        lhs = self.array[:self.ptr-1]
        rhs = self.array[self.ptr-1:]
        lhs.sort()
        lhs.reverse()
        self.array = lhs + rhs

    def __call__(self, *args):
        args = list(args)
        assert self.array, 'out-of-bounds (at %d)' % i
        if not args:
            return self.array[self.ptr-1]
        self.array[self.ptr-1] = args.pop()
        assert not args

    def __len__(self):
        return len(self.array)

    def __str__(self):
        return 'Array(array=%s, ptr=%s)' % (self.array, self.ptr)

    def __repr__(self):
        return 'Array(array=%r, ptr=%r)' % (self.array, self.ptr)

def execute(lines):
    global i, array
    i = 0
    array = Array()
    rep = 0
    while i < len(lines):
        line = lines[i]
        if not line:
            i += 1
            continue
        line = line[0]
        if line == '_':
            array.push_zero()
        elif line == '+':
            array(array() + 1)
        elif line == '-':
            array(array() - 1)
        elif line == '*':
            array(array() * 2)
        elif line == '/':
            array(int(array() / 2))
        elif line == '%':
            i += array()
            array.left()
        elif line == '=':
            i = array()-1
            array.left()
        elif line == ',':
            array.push_zero()
            array(ord(sys.stdin.read(1)))
        elif line == '.':
            sys.stdout.write(str(array()))
        elif line == '!':
            rep = array()
            array.left()
            i += 1
        elif line == '<':
            array.left()
        elif line == '>':
            array.right()
        elif line == '^':
            array(int(chr(array())))
        elif line == '~':
            array(int(not array()))
        elif line == '|':
            array(array() ** 2)
        elif line == '@':
            array(len(array))
        elif line == '`':
            top = array()
            array.push_zero()
            array(top)
        elif line == '\\':
            array.sort()
        elif line == '#':
            array(-array())

        if rep:
            rep -= 1
        else:
            i += 1

def main(args):
    try:
        _, path = args
    except ValueError:
        sys.exit('usage: %s <file to run, - for stdin>')

    if path == '-':
        data = sys.stdin.read()
    else:
        with open(path) as f:
            data = f.read()

    try:
        execute(data.split('\n'))
    except:
        print('ERROR!')
        print('Array was %r' % array)
        print('Re-raising error...')
        raise

if __name__ == '__main__':
    main(sys.argv)

Tôi nghĩ rằng sự khẳng định cho giới hạn dưới của mảng là lỗi. Vì bạn đang sử dụng self.ptr-1để truy cập, có lẽ bạn nên kiểm tra self.ptr>0không >=0. (Điều này không nên làm mất hiệu lực bất kỳ chương trình hợp lệ nào nhưng có thể khiến một số chương trình vô tình hoạt động mà không nên.)
Martin Ender

Một điều nữa: mã nói =đặt giá trị thành array() - 1, tài liệu nói +1gì?
Martin Ender

Nứt. (Giả sử rằng trình thông dịch là quy phạm, không phải mô tả.)
Martin Ender

@ MartinBüttner Dang, nhanh hơn tôi nghĩ! : OI đã sửa các vấn đề doc mà bạn đề cập.
kirbyfan64sos

7

Acc !! (an toàn)

Đó là ...

nhập mô tả hình ảnh ở đây

... Và hy vọng chắc chắn sẽ có nhiều lỗ hổng hơn.

Tôi đã đọc Acc! thông số kỹ thuật Acc thế nào rồi !! khác nhau?

Trong Acc !! , các biến vòng lặp đi ra khỏi phạm vi khi vòng lặp thoát. Bạn chỉ có thể sử dụng chúng trong vòng lặp. Bên ngoài, bạn sẽ gặp lỗi "tên không được xác định". Khác với sự thay đổi này, các ngôn ngữ là giống hệt nhau.

Các câu lệnh

Các lệnh được phân tích cú pháp theo từng dòng. Có ba loại lệnh:

  1. Count <var> while <cond>

Đếm <var>từ 0 miễn <cond>là khác không, tương đương với C ++ for(int <var>=0; <cond>; <var>++). Bộ đếm vòng lặp có thể là bất kỳ chữ cái viết thường. Điều kiện có thể là bất kỳ biểu thức nào, không nhất thiết liên quan đến biến vòng lặp. Vòng lặp dừng khi giá trị của điều kiện trở thành 0.

Vòng lặp yêu cầu niềng răng kiểu K & R (đặc biệt là biến thể Stroustrup ):

Count i while i-5 {
 ...
}

Như đã đề cập ở trên, các biến vòng lặp chỉ có sẵn bên trong các vòng lặp của chúng ; cố gắng tham chiếu chúng sau đó gây ra lỗi.

  1. Write <charcode>

Xuất một ký tự đơn có giá trị ASCII / Unicode đã cho thành thiết bị xuất chuẩn. Mã có thể là bất kỳ biểu thức.

  1. Biểu hiện

Bất kỳ biểu thức nào đứng một mình được đánh giá và gán lại cho bộ tích lũy (có thể truy cập dưới dạng _). Do đó, ví dụ, 3là một câu lệnh đặt bộ tích lũy thành 3; _ + 1tăng tích lũy; và _ * Nđọc một ký tự và nhân số tích lũy bằng mã số của nó.

Lưu ý: bộ tích lũy là biến duy nhất có thể được gán trực tiếp; biến vòng lặp và Ncó thể được sử dụng trong tính toán nhưng không được sửa đổi.

Bộ tích lũy ban đầu là 0.

Biểu thức

Một biểu thức có thể bao gồm các số nguyên, các biến vòng lặp ( a-z), _cho bộ tích lũy và giá trị đặc biệt N, đọc một ký tự và đánh giá mã của nó mỗi khi nó được sử dụng. Lưu ý: điều này có nghĩa là bạn chỉ được bắn một lần để đọc từng ký tự; lần sau bạn sử dụng N, bạn sẽ đọc tiếp theo.

Các nhà khai thác là:

  • +, thêm vào
  • -, phép trừ; phủ định đơn phương
  • *, phép nhân
  • /, phép chia số nguyên
  • %, modulo
  • ^, lũy thừa

Dấu ngoặc đơn có thể được sử dụng để thực thi quyền ưu tiên của hoạt động. Bất kỳ ký tự nào khác trong một biểu thức là một lỗi cú pháp.

Khoảng trắng và bình luận

Khoảng trắng hàng đầu và dấu và dòng trống được bỏ qua. Khoảng trắng trong các tiêu đề vòng lặp phải chính xác như được hiển thị, với một khoảng trắng duy nhất giữa tiêu đề vòng lặp và mở dấu ngoặc nhọn. Khoảng trắng bên trong biểu thức là tùy chọn.

# bắt đầu một bình luận một dòng

Đầu ra đầu vào

Acc !! mong đợi một dòng ký tự làm đầu vào. Mỗi ký tự đầu vào có thể được truy xuất theo trình tự và mã số của nó được xử lý bằng cách sử dụng N. Cố gắng đọc qua ký tự cuối cùng của dòng gây ra lỗi. Một ký tự có thể được xuất ra bằng cách chuyển charcode của nó tới Writecâu lệnh.

Thông dịch viên

Trình thông dịch (viết bằng Python 3) dịch Acc !! mã vào Python và execs nó.

import re, sys

def main():
    if len(sys.argv) != 2:
        print("Please supply a filename on the command line.", file=sys.stderr)
        return
    codeFile = sys.argv[1]
    with open(codeFile) as f:
        code = f.readlines()
    code = translate(code)
    exec(code, {"inputStream": (ord(char) for char in input())})

def translate(accCode):
    indent = 0
    loopVars = []
    pyCode = ["_ = 0"]
    for lineNum, line in enumerate(accCode):
        if "#" in line:
            # Strip comments
            line = line[:line.index("#")]
        line = line.strip()
        if not line:
            continue
        lineNum += 1
        if line == "}":
            if indent:
                loopVar = loopVars.pop()
                pyCode.append(" "*indent + loopVar + " += 1")
                indent -= 1
                pyCode.append(" "*indent + "del " + loopVar)
            else:
                raise SyntaxError("Line %d: unmatched }" % lineNum)
        else:
            m = re.fullmatch(r"Count ([a-z]) while (.+) \{", line)
            if m:
                expression = validateExpression(m.group(2))
                if expression:
                    loopVar = m.group(1)
                    pyCode.append(" "*indent + loopVar + " = 0")
                    pyCode.append(" "*indent + "while " + expression + ":")
                    indent += 1
                    loopVars.append(loopVar)
                else:
                    raise SyntaxError("Line %d: invalid expression " % lineNum
                                      + m.group(2))
            else:
                m = re.fullmatch(r"Write (.+)", line)
                if m:
                    expression = validateExpression(m.group(1))
                    if expression:
                        pyCode.append(" "*indent
                                      + "print(chr(%s), end='')" % expression)
                    else:
                        raise SyntaxError("Line %d: invalid expression "
                                          % lineNum
                                          + m.group(1))
                else:
                    expression = validateExpression(line)
                    if expression:
                        pyCode.append(" "*indent + "_ = " + expression)
                    else:
                        raise SyntaxError("Line %d: invalid statement "
                                          % lineNum
                                          + line)
    return "\n".join(pyCode)

def validateExpression(expr):
    "Translates expr to Python expression or returns None if invalid."
    expr = expr.strip()
    if re.search(r"[^ 0-9a-z_N()*/%^+-]", expr):
        # Expression contains invalid characters
        return None
    elif re.search(r"[a-zN_]\w+", expr):
        # Expression contains multiple letters or underscores in a row
        return None
    else:
        # Not going to check validity of all identifiers or nesting of parens--
        # let the Python code throw an error if problems arise there
        # Replace short operators with their Python versions
        expr = expr.replace("^", "**")
        expr = expr.replace("/", "//")
        # Replace N with a call to get the next input character
        expr = expr.replace("N", "inputStream.send(None)")
        return expr

if __name__ == "__main__":
    main()

Giải pháp

Sự sụp đổ của Acc gốc ! là các biến vòng lặp tiếp tục có thể truy cập bên ngoài các vòng lặp của chúng. Điều này cho phép tiết kiệm các bản sao của bộ tích lũy, khiến cho giải pháp trở nên quá dễ dàng.

Ở đây, không có lỗ lặp này (xin lỗi), chúng tôi chỉ có bộ tích lũy để lưu trữ công cụ. Tuy nhiên, để giải quyết nhiệm vụ, chúng ta cần lưu trữ bốn giá trị lớn tùy ý. 1 Giải pháp: xen kẽ các bit của chúng và lưu trữ số kết hợp kết quả trong bộ tích lũy. Ví dụ: giá trị bộ tích lũy là 6437 sẽ lưu trữ dữ liệu sau (sử dụng bit thứ tự thấp nhất làm cờ một bit):

1100100100101  Binary representation of accumulator
321032103210F  Key

The flag is 1 and the four numbers are
Number 0: 010 = 2
Number 1: 001 = 1
Number 2: 100 = 4
Number 3: 110 = 6

Chúng ta có thể truy cập bất kỳ bit nào của bất kỳ số nào bằng cách chia bộ tích lũy cho công suất thích hợp là 2, mod 2. Điều này cũng cho phép cài đặt hoặc lật từng bit riêng lẻ.

Ở cấp độ vĩ mô, thuật toán lặp trên các số đơn nguyên trong đầu vào. Nó đọc một giá trị vào số 0, sau đó thực hiện một thuật toán sắp xếp bong bóng để đặt nó ở vị trí thích hợp so với ba số còn lại. Cuối cùng, nó loại bỏ giá trị còn lại ở số 0, vì nó là giá trị nhỏ nhất trong bốn giá trị hiện tại và không bao giờ có thể lớn thứ ba. Khi vòng lặp kết thúc, số 1 là lớn thứ ba, vì vậy chúng tôi loại bỏ những cái khác và xuất nó.

Phần khó nhất (và lý do tôi có các biến vòng lặp toàn cầu trong lần đầu tiên) là so sánh hai số để biết có nên hoán đổi chúng hay không. Để so sánh hai bit, chúng ta có thể biến một bảng chân lý thành một biểu thức toán học; bước đột phá của tôi cho Acc !! đã tìm thấy một thuật toán so sánh tiến hành từ các bit bậc thấp đến bậc cao, vì không có biến toàn cục, không có cách nào lặp lại các bit của một số từ trái sang phải. Bit thứ tự thấp nhất của bộ tích lũy lưu trữ một cờ báo hiệu có nên hoán đổi hai số hiện đang được xem xét hay không.

Tôi nghi ngờ rằng Acc !! là Turing-perfect, nhưng tôi không chắc mình muốn gặp rắc rối khi chứng minh điều đó.

Đây là giải pháp của tôi với ý kiến:

# Read and process numbers until the double 0

Count y while N-48 {
    # Read unary number and store it (as binary) in number 0
    # The above loop header consumed the first 1, so we need to add 1 to number 0

    _ + 2

    # Increment number 0 for each 1 in input until next 0

    Count z while N-48 {
        # In this context, the flag indicates a need to carry; set it to 1
        _ - _%2 + 1

        # While carry flag is set, increment the next bit in line
        Count i while _%2 {
            # Set carry flag to 1 if i'th bit is 1, 0 if it's 0
            # Set i'th bit to 1 if it was 0, 0 if it was 1
            _ - _%2 + _/2^(i*4+1)%2 + (-1)^(_/2^(i*4+1)%2)*2^(i*4+1)
        }
    }

    # Bubble number 0 upwards

    Count n while n-3 {
        # The flag (rightmost bit of accumulator) needs to become 1 if we want to swap
        # numbers (n) and (n+1) and 0 if we don't
        # Iterate over bit-groups of accumulator, RTL
        Count i while _/2^(i*4+1) {
            # Adjust the flag as follows:
            # _/2^(i*4+n+1)%4 is the current bit of number (n+1) followed by the current
            # bit of number (n), a value between 0 and 3
            # - If this quantity is 1 (01), number (n) is bigger so far; set flag to 1
            # - If this quantity is 2 (10), number (n+1) is bigger so far; set flag to 0
            # - If this quantity is 0 (00) or 3 (11), the two bits are the same; keep
            #   current value of flag
            _ + (_/2^(i*4+n+1)%4%3 + 1)/2*(_/2^(i*4+n+1)%4%3%2 - _%2)
        }

        # Now swap the two if the flag is 1
        Count i while (_%2)*(_/2^(i*4+1)) {
            # _/2^(i*4+n+1)%2 is the current bit of number (n)
            # _/2^(i*4+n+2)%2 is the current bit of number (n+1)
            _ - (_/2^(i*4+n+1)%2)*2^(i*4+n+1) - (_/2^(i*4+n+2)%2)*2^(i*4+n+2) + (_/2^(i*4+n+2)%2)*2^(i*4+n+1) + (_/2^(i*4+n+1)%2)*2^(i*4+n+2)
        }
    }

    # Discard number 0, setting it to all zeros for the next iteration
    Count i while _/2^(i*4+1) {
        _ - _/2^(i*4+1)%2*2^(i*4+1)
    }
}

# Once the loop is over, all input has been read and the result is in number 1
# Divide by 2 to get rid of flag bit

_ / 2

# Zero out numbers 2 and 3

Count i while _/2^(i*4) {
    _ - _/2^(i*4+2)%2*2^(i*4+2)
}

Count i while _/2^(i*4) {
    _ - _/2^(i*4+3)%2*2^(i*4+3)
}

# Move bits of number 1 down to their final locations

Count i while _/2^i {
    _ - _/2^(i*4+1)%2*2^(i*4+1) + _/2^(i*4+1)%2*2^i
}

# _ now contains the correct answer in decimal; to output in unary:

Count z while z-_ {
    Write 49
}

1 Theo thông số kỹ thuật, chỉ cần hỗ trợ các giá trị lên tới 1 triệu. Tôi rất vui vì không ai lợi dụng điều đó cho một giải pháp dễ dàng hơn - mặc dù tôi không hoàn toàn chắc chắn về cách bạn sẽ so sánh.


LOL @ the Bill the Cat
một spaghetto

7

Picofuck (an toàn)

Picofuck tương tự như Smallfuck . Nó hoạt động trên một băng nhị phân không bị ràng buộc ở bên phải, bị ràng buộc ở bên trái. Nó có các lệnh sau:

  • > di chuyển con trỏ sang phải

  • <di chuyển con trỏ sang trái. Nếu con trỏ rơi khỏi băng, chương trình sẽ chấm dứt.

  • * lật bit tại con trỏ

  • (nếu bit tại con trỏ là 0, nhảy sang tiếp theo)

  • )không làm gì cả - dấu ngoặc trong Picofuck là ifcác khối, không phải là whilecác vòng lặp.

  • .ghi vào stdout bit tại con trỏ dưới dạng ascii 0hoặc 1.

  • ,đọc từ stdin cho đến khi chúng ta bắt gặp một 0hoặc 1, và lưu trữ nó trong bit tại con trỏ.

Mã kết thúc mã Picofuck - khi kết thúc chương trình, nó sẽ tiếp tục từ đầu.

Ngoài ra, Picofuck không cho phép dấu ngoặc đơn lồng nhau. Các dấu ngoặc đơn xuất hiện trong chương trình Picofuck phải xen kẽ giữa (), bắt đầu bằng (và kết thúc bằng ).

Thông dịch viên

Viết bằng Python 2.7

sử dụng: python picofuck.py <source_file>

import sys

def interpret(code):
    # Ensure parentheses match and are not nested.
    in_if_block = False
    for c in code:
        if c == '(':
            if in_if_block:
                print "NESTED IFS DETECTED!!!!!"
                return
            in_if_block = True
        elif c == ')':
            if not in_if_block:
                print "Unmatched parenthesis"
                return
            in_if_block = False
    if in_if_block:
        print "Unmatched parenthesis"
        return


    code_ptr = 0
    array = [0]
    ptr = 0

    while 1:
        command = code[code_ptr]
        if command == '<':
            if ptr == 0:
                break
            ptr -= 1
        elif command == '>':
            ptr += 1
            if ptr == len(array):
                array.append(0)
        elif command == '*':
            array[ptr] = 1-array[ptr]
        elif command == '(':
            if array[ptr] == 0:
                while code[code_ptr] != ')':
                    code_ptr = (code_ptr + 1) % len(code)
        elif command == ',':
            while True:
                c = sys.stdin.read(1)
                if c in ['0','1']:
                    array[ptr] = int(c)
                    break
        elif command == '.':
            sys.stdout.write(str(array[ptr]))
        code_ptr = (code_ptr+1)%len(code)

if __name__ == "__main__":
    with open(sys.argv[1]) as source_file:
        code = source_file.read()
    interpret(code)

Giải pháp

Chương trình python 2.7 sau đây đưa ra giải pháp của tôi có thể tìm thấy ở đây

Tôi có thể chỉnh sửa bài đăng này sau với lời giải thích kỹ lưỡng hơn về cách thức hoạt động của nó, nhưng hóa ra Picofuck đã hoàn thành.

states = {
    "SETUP":(
        "*>",
        "GET_NUMBER",
        "GET_NUMBER"
    ),

    "GET_NUMBER":(
        ",",
        "CHANGE_A",
        "PREPARE_PRINT_C"
    ),

    "GET_DIGIT":(
        ",",
        "CHANGE_A",
        "GOT_DIGIT"
    ),

    "GOT_DIGIT":(
        ">>>>",
        "GO_BACK",
        "GO_BACK"
    ),

    "CHANGE_A":(
        ">",
        "CHANGE_B",
        "SET_A"
    ),

    "SET_A":(
        "*>>>>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "CHANGE_B":(
        ">",
        "CHANGE_C",
        "SET_B"
    ),

    "SET_B":(
        "*>>>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "CHANGE_C":(
        ">",
        "CONTINUE_GET_NUMBER",
        "SET_C"
    ),

    "SET_C":(
        "*>>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "CONTINUE_GET_NUMBER":(
        ">>",
        "GET_DIGIT",
        "GET_DIGIT"
    ),

    "GO_BACK":(
        "<<<<<",
        "FINISH_GO_BACK",
        "GO_BACK"
    ),

    "FINISH_GO_BACK":(
        ">",
        "GET_NUMBER",
        "GET_NUMBER"
    ),

    "PREPARE_PRINT_C":(
        ">>>",
        "PRINT_C",
        "PRINT_C"
    ),

    "PRINT_C":(
        ".>>>>>",
        "PRINT_C",
        "TERMINATE"
    ),

    "TERMINATE":(
        "<",
        "TERMINATE",
        "TERMINATE"
    ),
}

def states_to_nanofuck(states,start_state):
    state_list = list(states)
    state_list.remove(start_state)
    state_list.insert(0,start_state)

    states_by_index = []
    for i,state in enumerate(state_list):
        commands, next1, next0 = states[state]
        states_by_index.append((commands,state_list.index(next1),state_list.index(next0)))


    # setup first state
    fragments = ['*(*>>>>>*<<<<<)>>>>>']

    # reset states that don't match
    for index in range(len(states_by_index)):
        for bool in range(2):
            # at state_0_0
            state_dist = index*3 + bool
            # move state to curstate
            fragments.append('>'*state_dist)
            fragments.append('(*')
            fragments.append(  '<'*state_dist)
            fragments.append(  '<<*>>')
            fragments.append(  '>'*state_dist)
            fragments.append(')')
            fragments.append('<'*state_dist)

            # go to arr
            fragments.append('<<<')

            if bool == 0:
                #flip arr
                fragments.append('*')

            # compute match = arr & curstate
            fragments.append('(>)(>*<)<(<)>')

            if bool == 0:
                #flip arr
                fragments.append('*')

            # reset curstate
            fragments.append('>(*)')

            # move match to matchstate, go back to state_0_0
            matchstate_dist = index*3 + 2
            fragments.append('>(*>')
            fragments.append(  '>'*matchstate_dist)
            fragments.append(  '*')
            fragments.append(  '<'*matchstate_dist)
            fragments.append('<)>')

    #fragments.append('|')

    # do the commands of the matching state
    for index,state in enumerate(states_by_index):
        for bool in range(2):
            # at state_0_0
            matchstate_dist = index*3 + 2
            # move matchstate to curstate
            fragments.append('>'*matchstate_dist)
            fragments.append('(*')
            fragments.append(  '<'*matchstate_dist)
            fragments.append(  '<<*>>')
            fragments.append(  '>'*matchstate_dist)
            fragments.append(')')
            fragments.append('<'*matchstate_dist)

            # if curstate, reset curstate
            fragments.append('<<(*')

            # go to arr
            fragments.append('<')

            # do commands
            commands,next1,next0 = state
            for c in commands:
                if c in '<>':
                    fragments.append(c*(3*len(states_by_index)+5))
                else:
                    fragments.append(c)

            # go to state_0_0
            fragments.append('>>>')

            # set next states
            for dist in [next0*3, next1*3+1]:
                fragments.append('>'*dist)
                fragments.append('*')
                fragments.append('<'*dist)

            # go to curstate
            fragments.append('<<')

            # end if
            fragments.append(')')

            # go to state_0_0
            fragments.append('>>')


    # go back to setup and set it
    fragments.append('<<<<<*')

    code = ''.join(fragments)

    return code



print states_to_nanofuck(states, "SETUP")

2
Chờ gần 2 năm Có phải sau này chưa?
Máy

Chỉ cần FYI, liên kết bạn cung cấp hiện đã chết. '
Conor O'Brien

Liên kết yêu cầu tải xuống và tôi quá lười biếng. Hãy sửa chữa.
Máy

@CalculatorFeline Đó là 13KB, việc tải xuống dễ dàng hơn nhiều.
mbomb007

7

PQRS - An toàn! / Giải pháp được cung cấp

Khái niệm cơ bản

Tất cả các hướng dẫn ngụ ý có bốn toán hạng địa chỉ bộ nhớ:

P₀ Q₀ R₀ S₀
P₁ Q₁ R₁ S₁
...
Pᵥ₋₁ Qᵥ₋₁ Rᵥ₋₁ Sᵥ₋₁

Đâu vlà kích thước của bộ nhớ trong bộ tứ.

Pᵢ, Qᵢ, Rᵢ, SᵢĐang ký số nguyên kích thước mẹ đẻ của máy tính của bạn (ví dụ như 16, 32 hoặc 64 bit) mà chúng tôi sẽ đề cập đến như lời nói.

Đối với mỗi bộ tứ i, hoạt động ngụ ý, với []biểu thị sự gián tiếp, là:

if (([Pᵢ] ← [Qᵢ] − [Rᵢ]) ≤ 0) go to Sᵢ else go to Pᵢ₊₁

Lưu ý rằng Subleq là tập con của PQRS .

Subleq đã được chứng minh là hoàn chỉnh, vì vậy PQRS cũng nên được hoàn thành!

Cấu trúc chương trình

PQRS định nghĩa một tiêu đề ban đầu như sau:

H₀ H₁ H₂ H₃ H₄ H₅ H₆ H₇

H₀ H₁ H₂ H₃luôn luôn là hướng dẫn đầu tiên P₀ Q₀ R₀ S₀. H₀để H₃cần phải được xác định tại thời gian tải.

PQRS có I / O thô sơ, nhưng đủ cho thử thách.

H₄ H₅: khi bắt đầu chương trình, nó đọc tối đa các H₅ký tự ASCII từ đầu vào tiêu chuẩn và lưu dưới dạng các từ ở chỉ mục H₄trở đi. H₄H₅cần được xác định tại thời điểm tải. Sau khi đọc, H₅sẽ được đặt thành số lượng ký tự đã đọc (và các từ được lưu).

H₆ H₇: khi kết thúc chương trình, bắt đầu từ chỉ mục H₆, nó sẽ in tất cả các byte chứa các H₇từ thành đầu ra tiêu chuẩn dưới dạng các ký tự ASCII. H₆H₇cần được xác định trước khi chương trình kết thúc. Null byte '\0'trong đầu ra sẽ được bỏ qua.

Chấm dứt

Chấm dứt được thực hiện bằng cách đặt Sᵢra ngoài giới hạn i < 0hoặc i ≥ v.

Thủ thuật

Bộ tứ Pᵢ Qᵢ Rᵢ Sᵢkhông cần phải được căn chỉnh hoặc tuần tự, phân nhánh được cho phép trong khoảng thời gian của bộ tứ phụ.

PQRS có sự quyết định vì vậy, không giống như Subleq, có đủ sự linh hoạt để thực hiện các chương trình con.

Mã có thể tự sửa đổi!

Thông dịch viên

Trình thông dịch được viết bằng C:

#include <stdlib.h>
#include <stdio.h>

// The "architecture"
enum {P, Q, R, S, START_OF_INPUT, LENGTH_OF_INPUT, START_OF_OUTPUT, LENGTH_OF_OUTPUT};
const int NEXT = S + 1;

// Recommend optimized!
#define OPTIMIZED

#ifdef PER_SPECS
// This may not work on all OSes and architectures - just too much memory needed!
const int K = 1002000200;
#else // OPTIMIZED
// This provides enough space to run the test cases with challenge-optimized.pqrs
const int K = 5200;
#endif

int main(int argc, char *argv[])
{
    int i, *M;
    char *p;
    FILE *program;

    // Allocate enough memory
    M = calloc(K, sizeof(int));

    // Load the program
    for (i = 0, program = fopen(argv[1], "r"); i < K && !feof(program); i++)
        if (!fscanf(program, "%d", M + i))
            break;
    fclose(program);

    // Read in the input
    for (i = 0; i < M[LENGTH_OF_INPUT] && !feof(stdin); i++)
    {
        int c = fgetc(stdin);
        if (c != EOF)
            M[M[START_OF_INPUT] + i] = c;
        else
            break;
    }
    M[LENGTH_OF_INPUT] = i;

    // Execute until terminated
    for (i = 0; i >= 0 && i < K; )
        i = (M[M[P + i]] = M[M[Q + i]] - M[M[R + i]]) <= 0? M[S + i]: i + NEXT;

    // Write the output
    for (i = 0, p = (char *)(M + M[START_OF_OUTPUT]); i < M[LENGTH_OF_OUTPUT] * sizeof(int); i++)
        // Ignore '\0'
        if (p[i])
            fputc(p[i], stdout);

    // Done
    free(M);

    return 0;
}

Để sử dụng, hãy lưu ở trên dưới dạng pqrs.c sau đó biên dịch:

gcc -o pqrs pqrs.c

Chương trình mẫu

Echos nhập tối đa 40 ký tự, trước 'PQRS-'.

8 8 8 -1 14 40 9 45 0 80 81 82 83 45

Để chạy, lưu như trên echo.pqrs, sau đó:

$ ./prqs echo.pqrs
greetings[enter]
[ctrl-D]
PQRS-greetings

Chạy các trường hợp thử nghiệm:

$ ./pqrs challenge-optimized.pqrs < test-1.txt
1

$ ./pqrs challenge-optimized.pqrs < test-2.txt
11111111111111111

$ ./pqrs challenge-optimized.pqrs < test-3.txt
[lots of ones!]

$ ./pqrs challenge-optimized.pqrs < test-3.txt | wc
0       1     773

Tất cả các trường hợp thử nghiệm chạy rất nhanh, ví dụ <500 ms.

Các thách thức

PQRS có thể được coi là ổn định, do đó, thử thách bắt đầu 2015-10-31 13:00 và kết thúc 2015-11-08 13:00, lần trong UTC.

Chúc may mắn!

Giải pháp

Ngôn ngữ này khá giống với ngôn ngữ được sử dụng trong "Baby" - máy kỹ thuật số điện tử được lưu trữ chương trình đầu tiên trên thế giới. Trên trang đó là một chương trình tìm hệ số cao nhất của một số nguyên trong ít hơn 32 từ bộ nhớ (CRT!)!

Tôi thấy việc viết giải pháp phù hợp với thông số kỹ thuật không tương thích với HĐH và máy tôi đang sử dụng (một dẫn xuất Ubuntu của Linux trên phần cứng cũ hơn một chút). Nó chỉ yêu cầu nhiều bộ nhớ hơn khả dụng và bán phá giá cốt lõi. Trên các hệ điều hành có quản lý bộ nhớ ảo tiên tiến hoặc máy có bộ nhớ ít nhất 8 GB, bạn có thể có thể chạy giải pháp cho mỗi thông số kỹ thuật. Tôi đã cung cấp cả hai giải pháp.

Rất khó để viết mã trực tiếp trong PQRS , gần giống với việc viết ngôn ngữ máy, thậm chí có thể là microcode. Thay vào đó, dễ dàng hơn để viết bằng một loại ngôn ngữ lắp ráp sau đó "biên dịch" nó. Dưới đây là ngôn ngữ lắp ráp có chú thích cho giải pháp được tối ưu hóa để chạy các trường hợp thử nghiệm:

; ANNOTATED PQRS ASSEMBLER CODE
; MINIMAL SIZED BUFFERS TO RUN THE TEST CASES

;OFFSET   LABEL       P-OP        Q-OP        R-OP        S-OP
0                     TEMP        ZERO        ZERO        L1

; INPUT AND OUTPUT LOCATIONS AND SIZES
4                     INPUT       4000        
6         L0:         OUTPUT      1000        

; GET CURRENT INPUT
8         L1:         TEMP        INPUT       ASCII_ZERO  L2
12                    SUM         SUM         INC         IGNORE
16                    L1+1        L1+1        INC         IGNORE
20                    TEMP        ZERO        ZERO        L1

; CHECK IF END OF NUMBERS
24        L2:         NUMBERS     SUM         ZERO        L3

; ADVANCE TO NEXT NUMBER
28                    L2          L2          INC         IGNORE

; ADVANCE COUNT
32                    COUNT       COUNT       INC         IGNORE
36                    L1+1        L1+1        INC         IGNORE

; CLEAR SUM AND GO BACK
40                    SUM         ZERO        ZERO        L1

; SORT NUMBERS                
44        L3:         P           NUMBERS     ZERO        LA
48        L4:         Q           NUMBERS+1   ZERO        L9

; COMPARE                
52                    TEMP        Q           P           L8

; SWAP IF OUT OF ORDER
56                    L5+1        L3+1        ZERO        IGNORE
60                    L6          L3+1        ZERO        IGNORE
64                    L6+1        L4+1        ZERO        IGNORE
68                    L7          L4+1        ZERO        IGNORE
72        L5:         TEMP        P           ZERO        IGNORE
76        L6:         P           Q           ZERO        IGNORE
80        L7:         Q           TEMP        ZERO        IGNORE

; INCREMENT INNER INDEX
84        L8:         L4+1        L4+1        INC         IGNORE
88                    TEMP        ZERO        ZERO        L3

; INCREMENT OUTER INDEX AND RESET INNER INDEX
92        L9:         L3+1        L3+1        INC         IGNORE
96                    L4+1        L3+1        INC         IGNORE
100                   TEMP        ZERO        ZERO        L3

; OUTPUT THIRD LARGEST NUMBER
104                   L0+1        NUMBERS+2   ZERO        IGNORE
108       LA:         TEMP        NUMBERS+2   ZERO        EXIT
112       LB:         OUTPUT      ASCII_ONE   ZERO        IGNORE
116                   LB          LB          INC         IGNORE
120                   NUMBERS+2   NUMBERS+2   DEC         EXIT
124                   TEMP        ZERO        ZERO        LA

; SAFETY LOOP – JUST IN CASE IGNORE DOESN'T WORK AS PLANNED!
128       IGNORE:     TEMP        ZERO        ZERO        IGNORE

; CONSTANTS
132       ZERO:        0
133       INC:        -1
134       DEC:         1
135       ASCII_ZERO: 48
136       ASCII_ONE:  49

; VARIABLES
137       TEMP:       [1]
138       SUM:        [1]
139       COUNT:      [1]
140       P:          [1]
141       Q:          [1]

; WORKING SPACE
142       NUMBERS:    [10]

; I/O SPACE
152       INPUT:      [4000]
4152      OUTPUT:     [1000]
5152

Những gì nó làm là phân tích đầu vào, chuyển đổi unary thành nhị phân, sau đó sắp xếp bong bóng trên các số với các giá trị theo thứ tự giảm dần, cuối cùng xuất ra giá trị lớn thứ ba bằng cách chuyển đổi nhị phân trở lại unary.

Lưu ý rằng INC(tăng) là âm và DEC(giảm) là dương! Nơi mà nó đang sử dụng L#hoặc L#+1P-hoặc Q-OPs, những gì đang xảy ra là nó đang cập nhật con trỏ: incrementing, decrementing, trao đổi, vv lắp ráp được tay biên soạn để PQRS bằng cách thay thế các nhãn với các độ lệch. Dưới đây là giải pháp tối ưu hóa PQRS :

137 132 132 8
152 4000
4152 1000
137 152 135 24
138 138 133 128
9 9 133 128
137 132 132 8
142 138 132 44
24 24 133 128
139 139 133 128
9 9 133 128
138 132 132 8
140 142 132 108
141 143 132 92
137 141 140 84
73 45 132 128
76 45 132 128
77 49 132 128
80 49 132 128
137 140 132 128
140 141 132 128
141 137 132 128
49 49 133 128
137 132 132 44
45 45 133 128
49 45 133 128
137 132 132 44
7 144 132 128
137 144 132 -1
4152 136 132 128
112 112 133 128
144 144 134 -1
137 132 132 108
137 132 132 128
0
-1
1
48
49

Mã ở trên có thể được lưu như challenge-optimized.pqrsđể chạy các trường hợp thử nghiệm.

Để đầy đủ, đây là nguồn cho mỗi thông số kỹ thuật:

; ANNOTATED PQRS ASSEMBLER CODE
; FULL SIZED BUFFERS TO RUN ACCORDING TO SPECS

;OFFSET   LABEL       P-OP        Q-OP        R-OP        S-OP
0                     TEMP        ZERO        ZERO        L1

; INPUT AND OUTPUT LOCATIONS AND SIZES
4                     INPUT       10^9        
6         L0:         OUTPUT      10^6        

; GET CURRENT INPUT
8         L1:         TEMP        INPUT       ASCII_ZERO  L2
12                    SUM         SUM         INC         IGNORE
16                    L1+1        L1+1        INC         IGNORE
20                    TEMP        ZERO        ZERO        L1

; CHECK IF END OF NUMBERS
24        L2:         NUMBERS     SUM         ZERO        L3

; ADVANCE TO NEXT NUMBER
28                    L2          L2          INC         IGNORE

; ADVANCE COUNT
32                    COUNT       COUNT       INC         IGNORE
36                    L1+1        L1+1        INC         IGNORE

; CLEAR SUM AND GO BACK
40                    SUM         ZERO        ZERO        L1

; SORT NUMBERS                
44        L3:         P           NUMBERS     ZERO        LA
48        L4:         Q           NUMBERS+1   ZERO        L9

; COMPARE                
52                    TEMP        Q           P           L8

; SWAP IF OUT OF ORDER
56                    L5+1        L3+1        ZERO        IGNORE
60                    L6          L3+1        ZERO        IGNORE
64                    L6+1        L4+1        ZERO        IGNORE
68                    L7          L4+1        ZERO        IGNORE
72        L5:         TEMP        P           ZERO        IGNORE
76        L6:         P           Q           ZERO        IGNORE
80        L7:         Q           TEMP        ZERO        IGNORE

; INCREMENT INNER INDEX
84        L8:         L4+1        L4+1        INC         IGNORE
88                    TEMP        ZERO        ZERO        L3

; INCREMENT OUTER INDEX AND RESET INNER INDEX
92        L9:         L3+1        L3+1        INC         IGNORE
96                    L4+1        L3+1        INC         IGNORE
100                   TEMP        ZERO        ZERO        L3

; OUTPUT THIRD LARGEST NUMBER
104                   L0+1        NUMBERS+2   ZERO        IGNORE
108       LA:         TEMP        NUMBERS+2   ZERO        EXIT
112       LB:         OUTPUT      ASCII_ONE   ZERO        IGNORE
116                   LB          LB          INC         IGNORE
120                   NUMBERS+2   NUMBERS+2   DEC         EXIT
124                   TEMP        ZERO        ZERO        LA

; SAFETY LOOP – JUST IN CASE IGNORE DOESN'T WORK AS PLANNED!
128       IGNORE:     TEMP        ZERO        ZERO        IGNORE

; CONSTANTS
132       ZERO:        0
133       INC:        -1
134       DEC:         1
135       ASCII_ZERO: 48
136       ASCII_ONE:  49

; VARIABLES
137       TEMP:       [1]
138       SUM:        [1]
139       COUNT:      [1]
140       P:          [1]
141       Q:          [1]

; WORKING SPACE
142       NUMBERS:    [10^6]

; I/O SPACE
1000142   INPUT:      [10^9]
1001000142 OUTPUT:    [10^6]
1002000142

Và giải pháp:

137 132 132 8
1000142 1000000000
1001000142 1000000
137 1000142 135 24
138 138 133 128
9 9 133 128
137 132 132 8
142 138 132 44
24 24 133 128
139 139 133 128
9 9 133 128
138 132 132 8
140 142 132 108
141 143 132 92
137 141 140 84
73 45 132 128
76 45 132 128
77 49 132 128
80 49 132 128
137 140 132 128
140 141 132 128
141 137 132 128
49 49 133 128
137 132 132 44
45 45 133 128
49 45 133 128
137 132 132 44
7 144 132 128
137 144 132 -1
1001000142 136 132 128
112 112 133 128
144 144 134 -1
137 132 132 108
137 132 132 128
0
-1
1
48
49

Để chạy ở trên, bạn sẽ cần phải nhận xét ra #define OPTIMIZEDvà thêm #define PER_SPECSvào pqrs.cvà biên dịch lại.

Đây là một thử thách lớn - thực sự rất thích tập luyện tinh thần! Đưa tôi trở lại với trình biên dịch 6502 cũ của tôi ...

Nếu tôi triển khai PQRS như một ngôn ngữ lập trình Real Real, tôi có thể sẽ thêm các chế độ bổ sung để truy cập trực tiếp và gián tiếp ngoài truy cập gián tiếp, cũng như vị trí tuyệt đối và vị trí, cả hai đều có tùy chọn truy cập gián tiếp cho việc phân nhánh!


3
Bạn nên có một giải pháp chuẩn bị trước khi đăng.
frageum

1
Vâng, giải pháp đã sẵn sàng. Tôi hiểu rằng có một số nghi ngờ vì ngôn ngữ thực sự khá khó để làm việc. Đối với những người muốn xem trước lén, tôi có thể gửi nó cho bạn, miễn là bạn hứa sẽ không tiết lộ nó trước khi kết thúc thử thách!

6

Kẽm, nứt nẻ! bởi @Zgarb

Cũng có sẵn tại GitHub .

Bạn cần có Dart 1.12 và Pub. Chỉ cần chạy pub getđể tải xuống phụ thuộc duy nhất, một thư viện phân tích cú pháp.

Đây là hy vọng điều này kéo dài hơn 30 phút! : O

Ngôn ngữ

Kẽm được định hướng xung quanh các nhà khai thác xác định lại. Bạn có thể xác định lại tất cả các toán tử trong ngôn ngữ một cách dễ dàng!

Cấu trúc của một chương trình Kẽm điển hình trông giống như:

let
<operator overrides>
in <expression>

Chỉ có hai loại dữ liệu: số nguyên và bộ. Không có thứ gọi là một tập hợp theo nghĩa đen và các tập hợp trống không được phép.

Biểu thức

Sau đây là các biểu thức hợp lệ trong Kẽm:

Văn học

Kẽm hỗ trợ tất cả các số nguyên bình thường, như 1-2.

Biến

Kẽm có các biến (giống như hầu hết các ngôn ngữ). Để tham khảo chúng, chỉ cần sử dụng tên. Một lần nữa giống như hầu hết các ngôn ngữ!

Tuy nhiên, có một biến đặc biệt gọi là Shành vi giống như của Pyth Q. Khi bạn lần đầu tiên sử dụng nó, nó sẽ đọc một dòng từ đầu vào tiêu chuẩn và diễn giải nó dưới dạng một tập hợp số. Chẳng hạn, dòng đầu vào 1234231sẽ biến thành tập hợp {1, 2, 3, 4, 3, 2, 1}.

LƯU Ý QUAN TRỌNG!!! Trong một số trường hợp, một chữ ở cuối ghi đè toán tử được phân tích cú pháp không chính xác, do đó bạn phải bao quanh nó bằng dấu ngoặc đơn.

Hoạt động nhị phân

Các hoạt động nhị phân sau đây được hỗ trợ:

  • Ngoài ra thông qua +: 1+1.
  • Phép trừ thông qua -: 1-1.
  • Nhân thông qua *: 2*2.
  • Phân chia qua /: 4/2.
  • Bình đẳng với =: 3=3.

Ngoài ra, hoạt động đơn nguyên sau đây cũng được hỗ trợ:

  • Độ dài với #: #x.

Ưu tiên luôn luôn đúng. Bạn có thể sử dụng dấu ngoặc đơn để ghi đè lên điều này.

Chỉ có sự bình đẳng và độ dài làm việc trên bộ. Khi bạn cố gắng lấy độ dài của một số nguyên, bạn sẽ nhận được số chữ số trong biểu diễn chuỗi của nó.

Đặt hiểu

Để thao túng các bộ, Zinc đã thiết lập sự hiểu biết. Họ trông như thế này:

{<variable>:<set><clause>}

Mệnh đề là mệnh đề khi hoặc mệnh đề sắp xếp.

Một mệnh đề khi trông như thế nào ^<expression>. Biểu thức theo dấu mũ phải dẫn đến một số nguyên. Sử dụng mệnh đề khi sẽ chỉ lấy các phần tử trong tập hợp expressionkhác không. Trong biểu thức, biến _sẽ được đặt thành chỉ mục hiện tại trong tập hợp. Nó gần tương đương với Python này:

[<variable> for _, <variable> in enumerate(<set>) when <expression> != 0]

Một mệnh đề sắp xếp , trông giống như $<expression>, sắp xếp tập hợp giảm dần theo giá trị của <expression>. Nó tương đương với Python này:

sorted(<set>, key=lambda <variable>: <expression>)[::-1]

Dưới đây là một số ví dụ hiểu:

  • Chỉ lấy các phần tử của tập hợp sbằng 5:

    {x:s^x=5}
    
  • Sắp xếp tập hợp stheo giá trị nếu các phần tử của nó bình phương:

    {x:s$x*x}
    

Ghi đè

Ghi đè toán tử cho phép bạn xác định lại toán tử. Họ trông như thế này:

<operator>=<operator>

hoặc là:

<variable><operator><variable>=<expression>

Trong trường hợp đầu tiên, bạn có thể định nghĩa một toán tử bằng với một toán tử khác. Chẳng hạn, tôi có thể định nghĩa +để thực sự trừ qua:

+=-

Khi bạn làm điều này, bạn có thể xác định lại một toán tử để trở thành một toán tử ma thuật . Có hai toán tử ma thuật:

  • joinlấy một tập hợp và một số nguyên và tham gia nội dung của tập hợp. Ví dụ, tham gia {1, 2, 3}với 4sẽ dẫn đến số nguyên 14243.

  • cutcũng lấy một tập hợp và một số nguyên và sẽ phân vùng tập hợp ở mọi lần xuất hiện của số nguyên. Sử dụng cuttrên {1, 3, 9, 4, 3, 2}3sẽ tạo {{1}, {9, 4}, {2}}... NHƯNG bất kỳ bộ phần tử đơn nào đều được làm phẳng, vì vậy kết quả sẽ thực sự là {1, {9, 4}, 2}.

Đây là một ví dụ xác định lại +toán tử có nghĩa là join:

+=join

Đối với trường hợp sau, bạn có thể xác định lại một toán tử cho biểu thức đã cho. Ví dụ, điều này xác định thao tác cộng để thêm các giá trị và sau đó thêm 1:

x+y=1+:x+:y

Nhưng cái gì +:? Bạn có thể nối dấu hai chấm :cho một toán tử để luôn sử dụng phiên bản dựng sẵn. Ví dụ này sử dụng nội dung +thông qua +:để cộng các số lại với nhau, sau đó thêm 1 (hãy nhớ rằng, mọi thứ đều liên kết đúng).

Ghi đè toán tử độ dài trông giống như:

#x=<expression>

Lưu ý rằng hầu hết tất cả các hoạt động dựng sẵn (ngoại trừ đẳng thức) sẽ sử dụng toán tử độ dài này để xác định độ dài của tập hợp. Nếu bạn xác định nó là:

#x=1

mọi phần của Kẽm hoạt động trên các bộ ngoại trừ =sẽ chỉ hoạt động trên phần tử đầu tiên của bộ được cung cấp.

Nhiều ghi đè

Bạn có thể ghi đè nhiều toán tử bằng cách tách chúng bằng dấu phẩy:

let
+=-,
*=/
in 1+2*3

In ấn

Bạn không thể in trực tiếp bất cứ thứ gì bằng Kẽm. Kết quả của biểu thức sau đây insẽ được in. Các giá trị của một tập hợp sẽ được nối với dấu phân cách. Ví dụ: lấy cái này:

let
...
in expr

Nếu exprlà tập hợp {1, 3, {2, 4}}, 1324sẽ được in ra màn hình sau khi chương trình kết thúc.

Để tất cả chúng cùng nhau

Đây là một chương trình Kẽm đơn giản xuất hiện để thêm 2+2nhưng kết quả là 5:

let
x+y=1+:x+:y
in 1+2

Thông dịch viên

Điều này đi vào bin/zinc.dart:

import 'package:parsers/parsers.dart';
import 'dart:io';

// An error.
class Error implements Exception {
  String cause;
  Error(this.cause);
  String toString() => 'error in Zinc script: $cause';
}


// AST.
class Node {
  Obj interpret(ZincInterpreter interp) => null;
}

// Identifier.
class Id extends Node {
  final String id;
  Id(this.id);
  String toString() => 'Id($id)';
  Obj interpret(ZincInterpreter interp) => interp.getv(id);
}

// Integer literal.
class IntLiteral extends Node {
  final int value;
  IntLiteral(this.value);
  String toString() => 'IntLiteral($value)';
  Obj interpret(ZincInterpreter interp) => new IntObj(value);
}

// Any kind of operator.
class Anyop extends Node {
  void set(ZincInterpreter interp, OpFuncType func) {}
}

// Operator.
class Op extends Anyop {
  final String op;
  final bool orig;
  Op(this.op, [this.orig = false]);
  String toString() => 'Op($op, $orig)';
  OpFuncType get(ZincInterpreter interp) =>
    this.orig ? interp.op0[op] : interp.op1[op];
  void set(ZincInterpreter interp, OpFuncType func) { interp.op1[op] = func; }
}

// Unary operator (len).
class Lenop extends Anyop {
  final bool orig;
  Lenop([this.orig = false]);
  String toString() => 'Lenop($orig)';
  OpFuncType get(ZincInterpreter interp) =>
    this.orig ? interp.op0['#'] : interp.op1['#'];
  void set(ZincInterpreter interp, OpFuncType func) { interp.op1['#'] = func; }
}

// Magic operator.
class Magicop extends Anyop {
  final String op;
  Magicop(this.op);
  String toString() => 'Magicop($op)';
  Obj interpret_with(ZincInterpreter interp, Obj x, Obj y) {
    if (op == 'cut') {
      if (y is! IntObj) { throw new Error('cannot cut int with non-int'); }
      if (x is IntObj) {
        return new SetObj(x.value.toString().split(y.value.toString()).map(
          int.parse));
      } else {
        assert(x is SetObj);
        List<List<Obj>> res = [[]];
        for (Obj obj in x.vals(interp)) {
          if (obj == y) { res.add([]); }
          else { res.last.add(obj); }
        }
        return new SetObj(new List.from(res.map((l) =>
          l.length == 1 ? l[0] : new SetObj(l))));
      }
    } else if (op == 'join') {
      if (x is! SetObj) { throw new Error('can only join set'); }
      if (y is! IntObj) { throw new Error('can only join set with int'); }
      String res = '';
      for (Obj obj in x.vals(interp)) {
        if (obj is! IntObj) { throw new Error('joining set must contain ints'); }
        res += obj.value.toString();
      }
      return new IntObj(int.parse(res));
    }
  }
}

// Unary operator (len) expression.
class Len extends Node {
  final Lenop op;
  final Node value;
  Len(this.op, this.value);
  String toString() => 'Len($op, $value)';
  Obj interpret(ZincInterpreter interp) =>
    op.get(interp)(interp, value.interpret(interp), null);
}

// Binary operator expression.
class Binop extends Node {
  final Node lhs, rhs;
  final Op op;
  Binop(this.lhs, this.op, this.rhs);
  String toString() => 'Binop($lhs, $op, $rhs)';
  Obj interpret(ZincInterpreter interp) =>
    op.get(interp)(interp, lhs.interpret(interp), rhs.interpret(interp));
}

// Clause.
enum ClauseKind { Where, Sort }
class Clause extends Node {
  final ClauseKind kind;
  final Node expr;
  Clause(this.kind, this.expr);
  String toString() => 'Clause($kind, $expr)';
  Obj interpret_with(ZincInterpreter interp, SetObj set, Id id) {
    List<Obj> res = [];
    List<Obj> values = set.vals(interp);
    switch (kind) {
    case ClauseKind.Where:
      for (int i=0; i<values.length; i++) {
        Obj obj = values[i];
        interp.push_scope();
        interp.setv(id.id, obj);
        interp.setv('_', new IntObj(i));
        Obj x = expr.interpret(interp);
        interp.pop_scope();
        if (x is IntObj) {
          if (x.value != 0) { res.add(obj); }
        } else { throw new Error('where clause condition must be an integer'); }
      }
      break;
    case ClauseKind.Sort:
      res = values;
      res.sort((x, y) {
        interp.push_scope();
        interp.setv(id.id, x);
        Obj x_by = expr.interpret(interp);
        interp.setv(id.id, y);
        Obj y_by = expr.interpret(interp);
        interp.pop_scope();
        if (x_by is IntObj && y_by is IntObj) {
          return x_by.value.compareTo(y_by.value);
        } else { throw new Error('sort clause result must be an integer'); }
      });
      break;
    }
    return new SetObj(new List.from(res.reversed));
  }
}

// Set comprehension.
class SetComp extends Node {
  final Id id;
  final Node set;
  final Clause clause;
  SetComp(this.id, this.set, this.clause);
  String toString() => 'SetComp($id, $set, $clause)';
  Obj interpret(ZincInterpreter interp) {
    Obj setobj = set.interpret(interp);
    if (setobj is SetObj) {
      return clause.interpret_with(interp, setobj, id);
    } else { throw new Error('set comprehension rhs must be set type'); }
  }
}

// Operator rewrite.
class OpRewrite extends Node {
  final Anyop op;
  final Node value;
  final Id lid, rid; // Can be null!
  OpRewrite(this.op, this.value, [this.lid, this.rid]);
  String toString() => 'OpRewrite($lid, $op, $rid, $value)';
  Obj interpret(ZincInterpreter interp) {
    if (lid != null) {
      // Not bare.
      op.set(interp, (interp,x,y) {
        interp.push_scope();
        interp.setv(lid.id, x);
        if (rid == null) { assert(y == null); }
        else { interp.setv(rid.id, y); }
        Obj res = value.interpret(interp);
        interp.pop_scope();
        return res;
      });
    } else {
      // Bare.
      if (value is Magicop) {
        op.set(interp, (interp,x,y) => value.interpret_with(interp, x, y));
      } else {
        op.set(interp, (interp,x,y) => (value as Anyop).get(interp)(x, y));
      }
    }
    return null;
  }
}

class Program extends Node {
  final List<OpRewrite> rws;
  final Node expr;
  Program(this.rws, this.expr);
  String toString() => 'Program($rws, $expr)';
  Obj interpret(ZincInterpreter interp) {
    rws.forEach((n) => n.interpret(interp));
    return expr.interpret(interp);
  }
}


// Runtime objects.
typedef Obj OpFuncType(ZincInterpreter interp, Obj x, Obj y);

class Obj {}

class IntObj extends Obj {
  final int value;
  IntObj(this.value);
  String toString() => 'IntObj($value)';
  bool operator==(Obj rhs) => rhs is IntObj && value == rhs.value;
  String dump() => value.toString();
}

class SetObj extends Obj {
  final List<Obj> values;
  SetObj(this.values) {
    if (values.length == 0) { throw new Error('set cannot be empty'); }
  }
  String toString() => 'SetObj($values)';
  bool operator==(Obj rhs) => rhs is SetObj && values == rhs.values;
  String dump() => values.map((x) => x.dump()).reduce((x,y) => x+y);
  List<Obj> vals(ZincInterpreter interp) {
    Obj lenobj = interp.op1['#'](interp, this, null);
    int len;
    if (lenobj is! IntObj) { throw new Error('# operator must return an int'); }
    len = lenobj.value;
    if (len < 0) { throw new Error('result of # operator must be positive'); }
    return new List<Obj>.from(values.getRange(0, len));
  }
}


// Parser.
class ZincParser extends LanguageParsers {
  ZincParser(): super(reservedNames: ['let', 'in', 'join', 'cut']);
  get start => prog().between(spaces, eof);
  get comma => char(',') < spaces;
  get lp => symbol('(');
  get rp => symbol(')');
  get lb => symbol('{');
  get rb => symbol('}');
  get colon => symbol(':');
  get plus => symbol('+');
  get minus => symbol('-');
  get star => symbol('*');
  get slash => symbol('/');
  get eq => symbol('=');
  get len => symbol('#');
  get in_ => char(':');
  get where => char('^');
  get sort => char('\$');

  prog() => reserved['let'] + oprw().sepBy(comma) + reserved['in'] + expr() ^
            (_1,o,_2,x) => new Program(o,x);
  oprw() => oprw1() | oprw2() | oprw3();
  oprw1() => (basicop() | lenop()) + eq + (magicop() | op()) ^
             (o,_,r) => new OpRewrite(o,r);
  oprw2() => (id() + op() + id()).list + eq + expr() ^
             (l,_,x) => new OpRewrite(l[1], x, l[0], l[2]);
  oprw3() => lenop() + id() + eq + expr() ^ (o,a,_,x) => new OpRewrite(o, x, a);
  magicop() => (reserved['join'] | reserved['cut']) ^ (s) => new Magicop(s);
  basicop() => (plus | minus | star | slash | eq) ^ (op) => new Op(op);
  op() => (basicop() + colon ^ (op,_) => new Op(op.op, true)) | basicop();
  lenop() => (len + colon ^ (_1,_2) => new Lenop(true)) |
             len ^ (_) => new Lenop();
  expr() => setcomp() | unop() | binop() | prim();
  setcomp() => lb + id() + in_ + rec(expr) + clause() + rb ^
               (_1,i,_2,x,c,_3) => new SetComp(i,x,c);
  clausekind() => (where ^ (_) => ClauseKind.Where) |
                  (sort  ^ (_) => ClauseKind.Sort);
  clause() => clausekind() + rec(expr) ^ (k,x) => new Clause(k,x);
  unop() => lenop() + rec(expr) ^ (o,x) => new Len(o,x);
  binop() => prim() + op() + rec(expr) ^ (l,o,r) => new Binop(l,o,r);
  prim() => id() | intlit() | parens(rec(expr));
  id() => identifier ^ (i) => new Id(i);
  intlit() => intLiteral ^ (i) => new IntLiteral(i);
}


// Interpreter.
class ZincInterpreter {
  Map<String, OpFuncType> op0, op1;
  List<Map<String, Obj>> scopes;
  ZincInterpreter() {
    var beInt = (v) {
      if (v is IntObj) { return v.value; }
      else { throw new Error('argument to binary operator must be integer'); }
    };
    op0 = {
      '+': (_,x,y) => new IntObj(beInt(x)+beInt(y)),
      '-': (_,x,y) => new IntObj(beInt(x)-beInt(y)),
      '*': (_,x,y) => new IntObj(beInt(x)*beInt(y)),
      '/': (_,x,y) => new IntObj(beInt(x)/beInt(y)),
      '=': (_,x,y) => new IntObj(x == y ? 1 : 0),
      '#': (i,x,_2) =>
        new IntObj(x is IntObj ? x.value.toString().length : x.values.length)
    };
    op1 = new Map<String, OpFuncType>.from(op0);
    scopes = [{}];
  }

  void push_scope() { scopes.add({}); }
  void pop_scope() { scopes.removeLast(); }
  void setv(String name, Obj value) { scopes[scopes.length-1][name] = value; }
  Obj getv(String name) {
    for (var scope in scopes.reversed) {
      if (scope[name] != null) { return scope[name]; }
    }
    if (name == 'S') {
      var input = stdin.readLineSync() ?? '';
      var list = new List.from(input.codeUnits.map((c) =>
        new IntObj(int.parse(new String.fromCharCodes([c])))));
      setv('S', new SetObj(list));
      return getv('S');
    } else throw new Error('undefined variable $name');
  }
}


void main(List<String> args) {
  if (args.length != 1) {
    print('usage: ${Platform.script.toFilePath()} <file to run>');
    return;
  }
  var file = new File(args[0]);
  if (!file.existsSync()) {
    print('cannot open ${args[0]}');
    return;
  }
  Program root = new ZincParser().start.parse(file.readAsStringSync());
  ZincInterpreter interp = new ZincInterpreter();
  var res = root.interpret(interp);
  print(res.dump());
}

Và điều này đi vào pubspec.yaml:

name: zinc
dependencies:
  parsers: any

Giải pháp dự định

let
#x=((x=S)*(-2))+#:x,
/=cut
in {y:{x:S/0$#:x}^_=2}

1
Tôi có hiểu chính xác rằng các bộ được sắp xếp và có thể có các bản sao, vì vậy về cơ bản chúng là các danh sách không? Ngoài ra, nếu tôi joinmột bộ hỗn hợp như thế {1,{3,2}}, sẽ có một lỗi? Tôi không thể cài đặt Dart ngay bây giờ, vì vậy tôi không thể tự kiểm tra.
Zgarb

@Zgarb Có, bộ cơ bản là danh sách trong trường hợp này. Tham gia các bộ hỗn hợp sẽ là một lỗi, nhưng trình thông dịch thực sự gặp sự cố ATM ...
kirbyfan64sos

Làm thế nào để tôi chạy trình thông dịch? Nếu tôi thử, dart bin/zinc.dart test.znctôi gặp lỗi cú pháp: 'file:///D:/Development/languages/zinc/bin/zinc.dart': error: line 323 pos 41: unexpected token '?'...var input = stdin.readLineSync() ?? '';
Martin Ender


1
@Zgarb Hãy nhớ khi nào, trong thông số kỹ thuật, tôi đã nói tất cả các hoạt động dựng sẵn ngoại trừ sử dụng toán tử độ dài? Tôi áp đảo nó để trở lại -2+#:Skhi được đưa ra S, điều này đã cắt đứt hai số không. Đó là cách tôi đã hy vọng nó sẽ được giải quyết. Và ^không phải đảo ngược tập hợp ... đó là một lỗi ...
kirbyfan64sos

5

La bàn Soup ( bị nứt bởi các tông_box )

Phiên dịch: C ++

La bàn Soup giống như một cỗ máy Turing với băng 2 chiều vô hạn. Cái bắt chính là bộ nhớ lệnh và bộ nhớ dữ liệu nằm trong cùng một không gian và đầu ra của chương trình là toàn bộ nội dung của không gian đó.

nhập mô tả hình ảnh ở đây

Làm thế nào nó hoạt động

Một chương trình là một khối văn bản 2 chiều. Không gian chương trình bắt đầu với toàn bộ mã nguồn được đặt với ký tự đầu tiên tại (0,0). Phần còn lại của không gian chương trình là vô hạn và được khởi tạo với các ký tự null (ASCII 0).

Có hai con trỏ có thể di chuyển xung quanh không gian chương trình:

  • Con trỏ thực thi có vị trí và hướng (Bắc, Nam, Đông hoặc Tây). Mỗi đánh dấu, lệnh dưới con trỏ thực thi được thực thi, sau đó con trỏ thực thi di chuyển theo hướng hiện tại của nó. Con trỏ thực thi bắt đầu di chuyển về phía đông (dương x), tại vị trí của !ký tự hoặc tại (0,0) nếu điều đó không tồn tại.
  • Con trỏ dữ liệu chỉ có một vị trí. Nó được di chuyển với các hướng dẫn x, X, y, và Y. Nó bắt đầu tại vị trí của @ký tự hoặc tại (0,0) nếu điều đó không tồn tại.

Đầu vào

Nội dung của stdin được in vào không gian chương trình bắt đầu từ vị trí của >ký tự hoặc tại (0,0) nếu điều đó không tồn tại.

Đầu ra

Chương trình chấm dứt khi con trỏ thực thi chạy không giới hạn ngoài giới hạn. Đầu ra là toàn bộ nội dung của không gian chương trình tại thời điểm đó. Nó được gửi tới thiết bị xuất chuẩn và 'result.txt'.

Hướng dẫn

  • n - chuyển hướng con trỏ thực thi North (y âm)
  • e - chuyển hướng con trỏ thực thi East (dương x)
  • s - chuyển hướng con trỏ thực thi Nam (dương y)
  • w - chuyển hướng con trỏ thực thi West (âm x)
  • y - di chuyển con trỏ dữ liệu về phía Bắc (âm y)
  • X - di chuyển con trỏ dữ liệu Đông (dương x)
  • Y - di chuyển con trỏ dữ liệu về phía Nam (dương y)
  • x - di chuyển con trỏ dữ liệu West (âm x)
  • p- ghi ký tự tiếp theo gặp phải bởi con trỏ thực thi tại con trỏ dữ liệu. Nhân vật đó không được thực hiện như một chỉ dẫn.
  • j- kiểm tra ký tự tiếp theo mà con trỏ thực hiện gặp phải đối với ký tự bên dưới con trỏ dữ liệu. Nhân vật đó không được thực hiện như một chỉ dẫn. Nếu chúng giống nhau, con trỏ thực thi sẽ nhảy qua ký tự tiếp theo.
  • c - viết ký tự null tại con trỏ dữ liệu.
  • * - breakpoint - chỉ khiến trình thông dịch bị hỏng.

Tất cả các ký tự khác được bỏ qua bởi con trỏ thực thi.

Thông dịch viên

Trình thông dịch lấy tệp nguồn làm đối số và nhập vào stdin. Nó có một trình gỡ lỗi có thể bước, mà bạn có thể gọi bằng một lệnh breakpoint trong mã ( *). Khi bị hỏng, con trỏ thực thi được hiển thị là ASCII 178 (khối được tô đậm hơn) và con trỏ dữ liệu được hiển thị là ASCII 177 (khối được tô sáng hơn).

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>

// Compass Soup programming language interpreter
// created by Brian MacIntosh (BMacZero)
// for https://codegolf.stackexchange.com/questions/61804/create-a-programming-language-that-only-appears-to-be-unusable
//
// 31 October 2015

struct Point
{
    int x, y;
    Point(int ix, int iy) { x = ix; y = iy; };
    bool operator==(const Point &other) const
    {
        return other.x == x && other.y == y;
    }
    bool operator!=(const Point &other) const
    {
        return other.x != x || other.y != y;
    }
};

struct Bounds
{
    int xMin, xMax, yMin, yMax;
    Bounds(int xmin, int ymin, int xmax, int ymax)
    {
        xMin = xmin; yMin = ymin; xMax = xmax; yMax = ymax;
    }
    bool contains(Point pt)
    {
        return pt.x >= xMin && pt.x <= xMax && pt.y >= yMin && pt.y <= yMax;
    }
    int getWidth() { return xMax - xMin + 1; }
    int getHeight() { return yMax - yMin + 1; }
    bool operator==(const Bounds &other) const
    {
        return other.xMin == xMin && other.xMax == xMax && other.yMin == yMin && other.yMax == yMax;
    }
    bool operator!=(const Bounds &other) const
    {
        return other.xMin != xMin || other.xMax != xMax || other.yMin != yMin || other.yMax != yMax;
    }
};

int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }

Bounds hull(Point a, Bounds b)
{
    return Bounds(min(a.x, b.xMin), min(a.y, b.yMin), max(a.x, b.xMax), max(a.y, b.yMax));
}

Bounds hull(Bounds a, Bounds b)
{
    return Bounds(min(a.xMin, b.xMin), min(a.yMin, b.yMin), max(a.xMax, b.xMax), max(a.yMax, b.yMax));
}

Bounds programBounds(0,0,0,0);
char** programSpace;

Point execPtr(0,0);
Point execPtrDir(1,0);
Point dataPtr(0,0);
Point stdInPos(0,0);

bool breakpointHit = false;
char breakOn = 0;

/// reads the character from the specified position
char read(Point pt)
{
    if (programBounds.contains(pt))
        return programSpace[pt.x - programBounds.xMin][pt.y - programBounds.yMin];
    else
        return 0;
}

/// read the character at the data pointer
char readData()
{
    return read(dataPtr);
}

/// read the character at the execution pointer
char readProgram()
{
    return read(execPtr);
}

/// gets the bounds of the actual content of the program space
Bounds getTightBounds(bool debug)
{
    Bounds tight(0,0,0,0);
    for (int x = programBounds.xMin; x <= programBounds.xMax; x++)
    {
        for (int y = programBounds.yMin; y <= programBounds.yMax; y++)
        {
            if (read(Point(x, y)) != 0)
            {
                tight = hull(Point(x, y), tight);
            }
        }
    }
    if (debug)
    {
        tight = hull(dataPtr, tight);
        tight = hull(execPtr, tight);
    }
    return tight;
}

/// ensure that the program space encompasses the specified rectangle
void fitProgramSpace(Bounds bounds)
{
    Bounds newBounds = hull(bounds, programBounds);

    if (newBounds == programBounds) return;

    // allocate new space
    char** newSpace = new char*[newBounds.getWidth()];

    // copy content
    for (int x = 0; x < newBounds.getWidth(); x++)
    {
        newSpace[x] = new char[newBounds.getHeight()];
        for (int y = 0; y < newBounds.getHeight(); y++)
        {
            Point newWorldPos(x + newBounds.xMin, y + newBounds.yMin);
            newSpace[x][y] = read(newWorldPos);
        }
    }

    // destroy old space
    for (int x = 0; x < programBounds.getWidth(); x++)
    {
        delete[] programSpace[x];
    }
    delete[] programSpace;

    programSpace = newSpace;
    programBounds = newBounds;
}

/// outputs the current program space to a file
void outputToStream(std::ostream &stream, bool debug)
{
    Bounds tight = getTightBounds(debug);
    for (int y = tight.yMin; y <= tight.yMax; y++)
    {
        for (int x = tight.xMin; x <= tight.xMax; x++)
        {
            char at = read(Point(x, y));
            if (debug && x == execPtr.x && y == execPtr.y)
                stream << (char)178;
            else if (debug && x == dataPtr.x && y == dataPtr.y)
                stream << (char)177;
            else if (at == 0)
                stream << ' ';
            else
                stream << at;
        }
        stream << std::endl;
    }
}

/// writes a character at the specified position
void write(Point pt, char ch)
{
    fitProgramSpace(hull(pt, programBounds));
    programSpace[pt.x - programBounds.xMin][pt.y - programBounds.yMin] = ch;
}

/// writes a character at the data pointer
void write(char ch)
{
    write(dataPtr, ch);
}

/// writes a line of text horizontally, starting at the specified position
void writeLine(Point loc, std::string str, bool isSource)
{
    fitProgramSpace(Bounds(loc.x, loc.y, loc.x + str.size(), loc.y));
    for (unsigned int x = 0; x < str.size(); x++)
    {
        programSpace[x + loc.x][loc.y] = str[x];

        // record locations of things
        if (isSource)
        {
            switch (str[x])
            {
            case '>':
                stdInPos = Point(loc.x + x, loc.y);
                break;
            case '!':
                execPtr = Point(loc.x + x, loc.y);
                break;
            case '@':
                dataPtr = Point(loc.x + x, loc.y);
                break;
            }
        }
    }
}

void advanceExecPtr()
{
    execPtr.x += execPtrDir.x;
    execPtr.y += execPtrDir.y;
}

void breakpoint()
{
    breakpointHit = true;
    outputToStream(std::cout, true);
    std::cout << "[Return]: step | [Space+Return]: continue | [<char>+Return]: continue to <char>" << std::endl;
    while (true)
    {
        std::string input;
        std::getline(std::cin, input);
        if (input.size() == 0)
        {
            break;
        }
        else if (input.size() == 1)
        {
            if (input[0] == ' ')
            {
                breakpointHit = false;
                break;
            }
            else
            {
                breakOn = input[0];
                breakpointHit = false;
                break;
            }
        }
    }
}

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        printf("Usage: CompassSoup <source-file>");
        return 1;
    }

    // open source file
    std::ifstream sourceIn(argv[1]);

    if (!sourceIn.is_open())
    {
        printf("Error reading source file.");
        return 1;
    }

    programSpace = new char*[1];
    programSpace[0] = new char[1];
    programSpace[0][0] = 0;

    // read starting configuration
    std::string line;
    int currentLine = 0;
    while (std::getline(sourceIn, line))
    {
        writeLine(Point(0, currentLine), line, true);
        currentLine++;
    }

    sourceIn.close();

    // take stdin
    std::string input;
    std::cout << ">";
    std::cin >> input;
    std::cin.ignore();
    writeLine(stdInPos, input, false);

    // execute
    while (programBounds.contains(execPtr))
    {
        if (execPtrDir.x == 0 && execPtrDir.y == 0)
        {
            printf("Implementation error: execPtr is stuck.");
            break;
        }

        advanceExecPtr();

        char command = readProgram();

        // breakpoint control code
        if (breakpointHit || (breakOn != 0 && command == breakOn))
        {
            breakOn = 0;
            breakpoint();
        }

        switch (command)
        {
        case 'n':
            execPtrDir = Point(0,-1);
            break;
        case 'e':
            execPtrDir = Point(1,0);
            break;
        case 's':
            execPtrDir = Point(0,1);
            break;
        case 'w':
            execPtrDir = Point(-1,0);
            break;
        case 'x':
            dataPtr.x--;
            break;
        case 'X':
            dataPtr.x++;
            break;
        case 'y':
            dataPtr.y--;
            break;
        case 'Y':
            dataPtr.y++;
            break;
        case 'p':
            advanceExecPtr();
            write(readProgram());
            break;
        case 'j':
            advanceExecPtr();
            if (readData() == readProgram())
            {
                advanceExecPtr();
            }
            break;
        case 'c':
            write(0);
            break;
        case '*':
            breakpoint();
            break;
        }
    }

    std::ofstream outputFile("result.txt");
    outputToStream(outputFile, false);
    outputToStream(std::cout, false);
    outputFile.close();
}

Ví dụ

Chào thế giới

Hello, World!

Con mèo

>

Tính chẵn lẻ: chấp nhận một chuỗi các ký tự được kết thúc bằng 0 ('0'). Đầu ra yestrên dòng đầu tiên của đầu ra nếu số 1s trong đầu vào là số lẻ, nếu không thì đầu ra |.

|>
!--eXj1s-c-eXj0s-c-exj|s-pyXpeXps
   c   |   c   |   |   |
  cn0j-w---n1j-w   n---w

Lời khuyên

Bạn nên sử dụng trình chỉnh sửa văn bản tốt và sử dụng hợp lý chức năng của phím 'Chèn' và sử dụng 'Alt-Drag' để thêm hoặc xóa văn bản trên nhiều hàng cùng một lúc.

Giải pháp

Đây là giải pháp của tôi. Nó không đẹp như các tông_box vì tôi phải tự xóa mã nguồn. Tôi cũng hy vọng tôi có thể tìm cách xóa tất cả mã và chỉ để lại câu trả lời, nhưng tôi không thể.

Cách tiếp cận của tôi là chia các chuỗi 1khác nhau thành các dòng khác nhau, sau đó sắp xếp chúng bằng cách cho 1tất cả các chữ "rơi" cho đến khi chúng chạm vào một dòng khác 1, và cuối cùng xóa mọi thứ trừ dòng thứ ba sau đầu vào.

  • Khối lớn ở phía dưới bên phải của #A#đọc 1s và sao chép chúng vào dòng cuối cùng của phân chia cho đến khi 0đọc được.
  • #B#kiểm tra một giây 0và đi về phía bắc để #D#có một. Nếu không, #C#bắt đầu một dòng phân chia mới bằng cách đặt |sau dòng cuối cùng và quay lại #A#.
  • Khối ở và trên #F#là mã trọng lực. Nó đi đến hàng cuối cùng 1của hàng đầu tiên và di chuyển nó lên cho đến khi chạm 1hoặc -. Nếu nó không thể làm điều đó, nó đánh dấu hàng là kết thúc bằng cách đặt +trước nó.
  • #G#đang xóa tất cả các phần tách không cần thiết và #H#đang xóa stdin và tất cả các mã giữa các dấu ngoặc đơn.

Mã số:

 s-----------------------w
 s-c-w  s-c-w  s---w    e+-
 eXj)nc-eXj)nc-exj(ncyj(nn
(n-----------------------------------------w                      ))
(  #H#                             s---w   |                      ))
(                                  exj+ncyxn                      ))
(                                  |                              ))
(                      s---w   s-c-+w                             ))
(                      exj+ncy-eXj1nn                             ))
(                      |                                          ))
(         s---w    s-c-+w    s+pxw                                ))
(         eyj-n-YY-eXj1nn    |  sn1jX--w           e----s         ))
(         |                  Y  x     e+---s e---s ns1jyw         ))
(      ej+n------s           j  |     nn+jYw-n+jxw-Yw   |         ))
(      Y   ec----s      e---s|  |                       1         ))
(      c   ns1jX-wYcYYY-n-jyww  |                       p         ))
(      ns+jxw      #G#       e--s                       Y         ))
(       e---n                   |               s------w|         ))
(                               |               |   ej-nn         ))
(             s--w              e----s   exc----eyj1n---n         ))
(#A#          p e+---s   s---w       |#F#|                        ))
(e----se---s  1 ||   |   exj|n----p+YeXj1ns                       ))
(ns-jXwn-jyw--w-nn1jXw   c #D#       n----w                       ))
( |        |         |   |                                        ))
( |        n---------+---+-------------|pw            s---w s---w ))
( |                  |   |     exp)XYs   |            eyj-nYeXj0ns)
( |         s---ws---+w  n-----+-----+---+------------+----------w))
( |         |   ||   ||  e--yp)n     e---+--s         |           )
( |     e-c-exj|neYj|nn  |     #C#       |  |         p           ))
( |     |                |     s---w s---+w s---w s---+w          ))
( |     |          #B#  e+s    |   | |   || |   | |   ||          ))
(!ep-Yj0n-c----------Xj0nne----exj|n-eYj|nn exj|n-eYj|nn          ))
(-@
 |>


Chết tiệt, thật gần! Tôi sẽ chia sẻ giải pháp của tôi khi tôi về nhà tối nay.
BMac

Tôi không thể làm cho chương trình tương đương hoạt động. Có phải là một hướng dẫn gỡ lỗi lúc đầu? Nếu tôi bước qua nó bị mắc kẹt trong một vòng lặp vô hạn, Bạn có biết mình đang làm gì sai không?
frageum

Có vẻ như có thêm một cphần đầu mà không nên có ở đó. Tôi sửa nó rồi. Cũng thêm giải pháp của tôi cho vấn đề.
BMac

4

Acc! , Cracc'd bởi ppperry

Ngôn ngữ này có một cấu trúc vòng lặp, toán học số nguyên cơ bản, I / O ký tự và bộ tích lũy (do đó là tên). Chỉ cần một tích lũy. Như vậy, tên.

Các câu lệnh

Các lệnh được phân tích cú pháp theo từng dòng. Có ba loại lệnh:

  1. Count <var> while <cond>

Đếm <var>từ 0 chừng nào <cond>là nonzero, tương đương với C-phong cách for(<var>=0; <cond>; <var>++). Bộ đếm vòng lặp có thể là bất kỳ chữ cái viết thường. Điều kiện có thể là bất kỳ biểu thức nào, không nhất thiết liên quan đến biến vòng lặp. Vòng lặp dừng khi giá trị của điều kiện trở thành 0.

Vòng lặp yêu cầu niềng răng kiểu K & R (đặc biệt là biến thể Stroustrup ):

Count i while i-5 {
 ...
}
  1. Write <charcode>

Xuất một ký tự đơn có giá trị ASCII / Unicode đã cho thành thiết bị xuất chuẩn. Mã có thể là bất kỳ biểu thức.

  1. Biểu hiện

Bất kỳ biểu thức nào đứng một mình được đánh giá và gán lại cho bộ tích lũy (có thể truy cập dưới dạng _). Do đó, ví dụ, 3là một câu lệnh đặt bộ tích lũy thành 3; _ + 1tăng tích lũy; và _ * Nđọc một ký tự và nhân số tích lũy bằng mã số của nó.

Lưu ý: bộ tích lũy là biến duy nhất có thể được gán trực tiếp; biến vòng lặp và Ncó thể được sử dụng trong tính toán nhưng không được sửa đổi.

Bộ tích lũy ban đầu là 0.

Biểu thức

Một biểu thức có thể bao gồm các số nguyên, các biến vòng lặp ( a-z), _cho bộ tích lũy và giá trị đặc biệt N, đọc một ký tự và đánh giá mã của nó mỗi khi nó được sử dụng. Lưu ý: điều này có nghĩa là bạn chỉ được bắn một lần để đọc từng ký tự; lần sau bạn sử dụng N, bạn sẽ đọc tiếp theo.

Các nhà khai thác là:

  • +, thêm vào
  • -, phép trừ; phủ định đơn phương
  • *, phép nhân
  • /, phép chia số nguyên
  • %, modulo
  • ^, lũy thừa

Dấu ngoặc đơn có thể được sử dụng để thực thi quyền ưu tiên của hoạt động. Bất kỳ ký tự nào khác trong một biểu thức là một lỗi cú pháp.

Khoảng trắng và bình luận

Khoảng trắng hàng đầu và dấu và dòng trống được bỏ qua. Khoảng trắng trong các tiêu đề vòng lặp phải chính xác như được hiển thị, với một khoảng trắng duy nhất giữa tiêu đề vòng lặp và mở dấu ngoặc nhọn. Khoảng trắng bên trong biểu thức là tùy chọn.

# bắt đầu một bình luận một dòng

Đầu ra đầu vào

Acc! mong đợi một dòng ký tự làm đầu vào. Mỗi ký tự đầu vào có thể được truy xuất theo trình tự và mã số của nó được xử lý bằng cách sử dụng N. Cố gắng đọc qua ký tự cuối cùng của dòng gây ra lỗi. Một ký tự có thể được xuất ra bằng cách chuyển charcode của nó tới Writecâu lệnh.

Thông dịch viên

Trình thông dịch (viết bằng Python 3) dịch Acc! mã vào Python và execs nó.

import re, sys

def main():
    if len(sys.argv) != 2:
        print("Please supply a filename on the command line.", file=sys.stderr)
        return
    codeFile = sys.argv[1]
    with open(codeFile) as f:
        code = f.readlines()
    code = translate(code)
    exec(code, {"inputStream": (ord(char) for char in input())})

def translate(accCode):
    indent = 0
    loopVars = []
    pyCode = ["_ = 0"]
    for lineNum, line in enumerate(accCode):
        if "#" in line:
            # Strip comments
            line = line[:line.index("#")]
        line = line.strip()
        if not line:
            continue
        lineNum += 1
        if line == "}":
            if indent:
                loopVar = loopVars.pop()
                if loopVar is not None:
                    pyCode.append(" "*indent + loopVar + " += 1")
                indent -= 1
            else:
                raise SyntaxError("Line %d: unmatched }" % lineNum)
        else:
            m = re.fullmatch(r"Count ([a-z]) while (.+) \{", line)
            if m:
                expression = validateExpression(m.group(2))
                if expression:
                    loopVar = m.group(1)
                    pyCode.append(" "*indent + loopVar + " = 0")
                    pyCode.append(" "*indent + "while " + expression + ":")
                    indent += 1
                    loopVars.append(loopVar)
                else:
                    raise SyntaxError("Line %d: invalid expression " % lineNum
                                      + m.group(2))
            else:
                m = re.fullmatch(r"Write (.+)", line)
                if m:
                    expression = validateExpression(m.group(1))
                    if expression:
                        pyCode.append(" "*indent
                                      + "print(chr(%s), end='')" % expression)
                    else:
                        raise SyntaxError("Line %d: invalid expression "
                                          % lineNum
                                          + m.group(1))
                else:
                    expression = validateExpression(line)
                    if expression:
                        pyCode.append(" "*indent + "_ = " + expression)
                    else:
                        raise SyntaxError("Line %d: invalid statement "
                                          % lineNum
                                          + line)
    return "\n".join(pyCode)

def validateExpression(expr):
    "Translates expr to Python expression or returns None if invalid."
    expr = expr.strip()
    if re.search(r"[^ 0-9a-z_N()*/%^+-]", expr):
        # Expression contains invalid characters
        return None
    elif re.search(r"[a-zN_]\w+", expr):
        # Expression contains multiple letters or underscores in a row
        return None
    else:
        # Not going to check validity of all identifiers or nesting of parens--
        # let the Python code throw an error if problems arise there
        # Replace short operators with their Python versions
        expr = expr.replace("^", "**")
        expr = expr.replace("/", "//")
        # Replace N with a call to get the next input character
        expr = expr.replace("N", "inputStream.send(None)")
        return expr

if __name__ == "__main__":
    main()


3

GoToTape (An toàn)

(Trước đây gọi là Simp-plex.)

Ngôn ngữ này đơn giản. Kiểm soát dòng chảy chính là goto, hình thức kiểm soát tự nhiên và hữu ích nhất.

Đặc tả ngôn ngữ

Dữ liệu được lưu trữ trên một băng và trong một bộ tích lũy. Nó hoạt động hoàn toàn với tích hợp không dấu. Mỗi nhân vật là mệnh lệnh. Sau đây là tất cả các lệnh:

  • Chữ cái: a- zlà các câu lệnh goto, đi đến A- Z, tương ứng.
  • :: đặt bộ tích lũy thành giá trị ASCII thành char từ đầu vào.
  • ~: xuất char cho giá trị ASCII trong bộ tích lũy.
  • &: trừ một từ bộ tích lũy nếu nó là 1 hoặc nhiều hơn, nếu không thêm một.
  • |: thêm một vào bộ tích lũy.
  • <: đặt con trỏ dữ liệu về 0.
  • +: tăng ô dữ liệu tại con trỏ dữ liệu; di chuyển con trỏ +1.
  • -: trừ một từ ô dữ liệu tại con trỏ dữ liệu nếu nó dương; di chuyển con trỏ +1.
  • [...]: chạy mã n lần trong đó n là số trên băng ở con trỏ dữ liệu (không thể lồng nhau).
  • /: bỏ qua hướng dẫn tiếp theo nếu bộ tích lũy bằng 0.

Phiên dịch viên (C ++)

#include <iostream>
#include <memory.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int serch(char* str,char ch){
    for(int i = 0;str[i];i++){
        if(str[i]==ch)
            return i;
    }
    return -1;
}

void runCode(char* code){
    int i = 0;
    char c;
    unsigned int* tape;
    tape = new unsigned int[1000003];
    memset(tape,0,1000003*sizeof(int));
    unsigned int p=0;
    unsigned int a=0;
    unsigned int n;
    unsigned int s;

    while(c=code[i]){
        if('A'<=c && c<='Z');
        if('a'<=c && c<='z')i=serch(code, c+'A'-'a');
        if(':'==c)a=cin.get();
        if('+'==c)tape[p++]++;
        if('-'==c)tape[p++] += tape[p]?-1:0;
        if('|'==c)a++;
        if('&'==c)a=a?a-1:1;
        if('<'==c)p=0;
        if('['==c){if(tape[p]){n=tape[p];s=i;}else i+=serch(code+i,']');};
        if(']'==c)i=--n?i:s;
        if('~'==c)cout<<(char)a;
        if('/'==c)i+=a?0:1;
        if('$'==c)p=a;
        i++;
    }
    delete[](tape);
}

int main(int argc, char* argv[]) {
    if(argc == 2){

        ifstream sorceFile (argv[1]);
        string code(static_cast<stringstream const&>(stringstream() << sorceFile.rdbuf()).str());
        runCode((char*)code.c_str());
    }else
        cout << "Code file must be included as a command-line argument \n";
    return 0;
}

Chúc vui vẻ!

Giải pháp

A:+&&&&&&&&&&/gbG&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/a<aB<[|]C[&]-[|]/c<[|]D[&]-[|]/d<[|]+E[&]|||||||||||||||||||||||||||||||||||||||||||||||||~X&/x-[|]/e


2
Mã hóa C ++ của bạn đang giết chết tôi! Có lý do nào bạn sử dụng callocthay vì new char, đã viết một vòng lặp kiểu C, sử dụng quản lý bộ nhớ kiểu C, khiến chúng tôi biên dịch lại tệp C ++ mỗi khi chúng tôi thay đổi mã và sử dụng 20 if thay vì a switch? Tôi không phàn nàn, nhưng mắt tôi đang chảy máu ngay bây giờ ...: O
kirbyfan64sos

3
Tôi đã cố định các đốm để thịt thông dịch viên.
MegaTom

@ kirbyfan64sos Mã này rất tệ. Tôi nhanh chóng kết hợp nó và có thể không làm tốt như tôi nên làm. chức năng chính có thể được thay đổi để lấy mã làm đầu vào. thực tế tôi nghĩ rằng tôi sẽ làm điều đó ngay bây giờ ...
MegaTom

1
Câu hỏi nói rằng các thông dịch viên nên lấy một tên tệp trên dòng lệnh cho chương trình .
Dennis

Dưới đây là một số cách ngắn để đọc một tập tin thành một chuỗi . Sau đó gọi str.c_str()để lấy a char*.
frageum

0

Đây là một ý tưởng tồi vì hầu như tất cả các ngôn ngữ bí truyền đều không thể đọc được (nhìn vào Jelly).
Nhưng ở đây đi:

Pylongolf2 beta6

Đẩy vào ngăn xếp

Đẩy vào ngăn xếp hoạt động khác nhau mà trong các ngôn ngữ khác.
78đẩy 78vào ngăn xếp, tuy nhiên trong Pylongolf, nó đẩy 78.
Trong Pylongolf2, điều này là dễ hiểu Ü.

Các lệnh

) Print the stack.
Ü Toggle the method Pylongolf2 uses for pushing to stack.
a The useless command, removes and adds the selected item in the same place.
c Ask for input.
n Convert string to a number.
" Toggle string mode for pushing text to the stack.
s Convert a number to a string. ╨ Encode the selected item (it must be a string).
_ Duplicate the selected item next to itself.
b Swap places between the selected item and the one before.
d Set the selected item to the last one.
m Move the selected item to the end of the stack.
@ Select an item. (Number required after this command as an argument).
w Wait a specified amount of time (the time is taken from the stack).
= Compare the selected item to the one before. (WARNING. THIS DELETES THE 2 ITEMS AND PLACES A true OR A false) (V2 beta)
~ Print the stack nicely. (V2 beta)
² Square a number. (V3 beta)
| Split a string to an array by the character after |. (V4 beta)
♀ Pop the array. (the contents are left in the stack) (V4 beta)
> Begin a while statement. (V5 beta)
< Loop back to the beginning of the while statement. (V5 beta)
! Break out of the while statements. (V5 beta)
? An if statement, does nothing if the selected item is a `true` boolean. (V6 beta)
¿ If an if statement is `false`, the interpreter skips everything to this character. (V6 beta)

Nối chuỗi và xóa mẫu Regex khỏi chuỗi

Biểu tượng + nối chuỗi.
Bạn có thể sử dụng biểu tượng - để xóa các ký tự theo mẫu biểu thức chính quy khỏi chuỗi:

c╨2"[^a-zA-Z]"-~

Mã này nhận đầu vào và loại bỏ tất cả các ký tự không theo thứ tự chữ cái bằng cách loại bỏ tất cả các mẫu khớp [^a-zA-Z].
Mục được chọn phải là regex và mục trước phải là chuỗi cần chỉnh sửa.

Nếu báo cáo

Để thực hiện nếu câu lệnh, hãy đặt một =để so sánh mục đã chọn và mục sau.
Nơi này hoặc một truehoặc một falsetrong vị trí của nó.
Lệnh ?kiểm tra boolean này.
Nếu đó là truekhông có gì và thông dịch viên tiếp tục.
Nếu đó là falsetrình thông dịch bỏ qua ¿ký tự gần nhất .

Lấy từ trang Github.

Thông dịch viên cho Pylongolf2 (Java):

package org.midnightas.pylongolf2;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Pylongolf {

    public static final void main(String[] args) throws Exception {
        String content = new String(Files.readAllBytes(Paths.get(new File(args[0]).toURI()))) + " ";
        boolean fullreadMode = true;
        List<Object> stack = new ArrayList<Object>();
        List<Integer> whileStatements = new ArrayList<Integer>();
        HashMap<String, Object> vars = new HashMap<String, Object>();
        int ifStatements = 0;
        Scanner scanner = new Scanner(new UnclosableDecorator(System.in));
        int selectedIndex = 0;
        for (int cl = 0; cl < content.length(); cl++) {
            char c = content.charAt(cl);
            if (isNumber(c)) {
                if (!fullreadMode) {
                    stack.add(Double.parseDouble(c + ""));
                } else {
                    String number = "";
                    for (int cl0 = cl; cl0 < content.length(); cl0++) {
                        if (isNumber(content.charAt(cl0))) {
                            number += content.charAt(cl0);
                        } else {
                            cl = cl0 - 1;
                            stack.add(Double.parseDouble(number));
                            break;
                        }
                    }
                }
            } else if (c == ')') {
                System.out.println(Arrays.toString(stack.toArray()));
            } else if (c == 'Ü') {
                fullreadMode = !fullreadMode;
            } else if (c == '+') {
                if (stack.get(selectedIndex) instanceof Object[]) {
                    Object[] obj = (Object[]) stack.remove(selectedIndex);
                    Double dbl = new Double(0d);
                    for (Object o : obj) {
                        dbl += (Double) o;
                    }
                    stack.add(selectedIndex, dbl);
                } else {
                    Object obj0 = stack.remove(selectedIndex);
                    Object obj1 = stack.remove(selectedIndex);
                    if (obj0 instanceof Number && obj1 instanceof Number)
                        stack.add(((Number) obj0).doubleValue() + ((Number) obj1).doubleValue());
                    else if (obj0 instanceof String) {
                        stack.add(obj0.toString() + obj1.toString());
                    }
                }
            } else if (c == '-') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                if (obj0 instanceof Number && obj1 instanceof Number)
                    stack.add(((Number) obj0).doubleValue() - ((Number) obj1).doubleValue());
                else if (obj0 instanceof String && obj1 instanceof String) {
                    stack.add(obj0.toString().replaceAll(obj1.toString(), ""));
                }
            } else if (c == '*') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                if (obj0 instanceof Number && obj1 instanceof Number)
                    stack.add(((Number) obj0).doubleValue() * ((Number) obj1).doubleValue());
            } else if (c == '/') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                if (obj0 instanceof Number && obj1 instanceof Number)
                    stack.add(((Number) obj0).doubleValue() / ((Number) obj1).doubleValue());
            } else if (c == 'a') {
                stack.add(selectedIndex, stack.remove(selectedIndex));
            } else if (c == 'c') {
                stack.add(scanner.nextLine());
            } else if (c == 'n') {
                if (stack.get(selectedIndex) instanceof String) {
                    stack.add(selectedIndex, Double.parseDouble(stack.remove(selectedIndex).toString()));
                } else if (stack.get(selectedIndex) instanceof Object[]) {
                    Object[] oldArray = (Object[]) stack.remove(selectedIndex);
                    Object[] newArray = new Object[oldArray.length];
                    for (int i = 0; i < oldArray.length; i++) {
                        newArray[i] = Double.parseDouble(oldArray[i].toString());
                    }
                    stack.add(selectedIndex, newArray);
                }
            } else if (c == '"') {
                String string = "\"";
                for (int cl0 = cl + 1; cl0 < content.length(); cl0++) {
                    string = string + content.charAt(cl0);
                    if (content.charAt(cl0) == '"') {
                        stack.add(string.substring(1, string.length() - 1));
                        cl = cl0;
                        break;
                    }
                }
            } else if (c == 's') {
                Object obj = stack.remove(selectedIndex);
                if (obj instanceof Double) {
                    Double dbl = (Double) obj;
                    if (dbl.doubleValue() == Math.floor(dbl)) {
                        stack.add(selectedIndex, "" + dbl.intValue() + "");
                    } else {
                        stack.add(selectedIndex, "" + dbl + "");
                    }
                }
            } else if (c == '╨') {
                cl++;
                char editmode = content.charAt(cl);
                if (editmode == '0') {
                    stack.add(selectedIndex, rot13(stack.remove(selectedIndex).toString()));
                } else if (editmode == '1') {
                    stack.add(selectedIndex,
                            new StringBuilder(stack.remove(selectedIndex).toString()).reverse().toString());
                } else if (editmode == '2') {
                    stack.add(selectedIndex, stack.remove(selectedIndex).toString().toLowerCase());
                } else if (editmode == '3') {
                    stack.add(selectedIndex, stack.remove(selectedIndex).toString().toUpperCase());
                }
            } else if (c == '_') {
                stack.add(selectedIndex, stack.get(selectedIndex));
            } else if (c == 'b') {
                stack.add(selectedIndex + 1, stack.remove(selectedIndex));
            } else if (c == 'd') {
                selectedIndex = stack.size() == 0 ? 0 : stack.size() - 1;
            } else if (c == 'm') {
                stack.add(stack.remove(selectedIndex));
            } else if (c == '@') {
                String number = "";
                for (int cl0 = cl + 1; cl0 < content.length(); cl0++) {
                    if (isNumber(content.charAt(cl0)))
                        number += content.charAt(cl0);
                    else {
                        cl = cl0 - 1;
                        selectedIndex = Integer.parseInt(number);
                        break;
                    }
                }
            } else if (c == 'w') {
                String number = "";
                for (int cl0 = cl + 1; cl0 < content.length(); cl0++) {
                    if (isNumber(content.charAt(cl0)))
                        number += content.charAt(cl0);
                    else {
                        cl = cl0 - 1;
                        Thread.sleep(Long.parseLong(number));
                        break;
                    }
                }
            } else if (c == '=') {
                Object obj0 = stack.remove(selectedIndex);
                Object obj1 = stack.remove(selectedIndex);
                stack.add(new Boolean(obj0.equals(obj1)));
            } else if (c == '~') {
                for (Object o : stack)
                    System.out.print(o);
                System.out.println();
            } else if (c == '²') {
                if (stack.get(selectedIndex) instanceof Double) {
                    Double dbl = (Double) stack.remove(selectedIndex);
                    stack.add(selectedIndex, dbl * dbl);
                } else if (stack.get(selectedIndex) instanceof Object[]) {
                    Object[] obj = (Object[]) stack.remove(selectedIndex);
                    Object[] newArray = new Object[obj.length];
                    for (int i = 0; i < obj.length; i++) {
                        newArray[i] = Math.pow((Double) obj[i], 2);
                    }
                    stack.add((Object[]) newArray);
                }
            } else if (c == '|') {
                String string = (String) stack.remove(selectedIndex);
                cl++;
                char splitChar = content.charAt(cl);
                stack.add((Object[]) string.split(splitChar + ""));
            } else if (c == '♀') {
                for (Object obj : (Object[]) stack.remove(selectedIndex)) {
                    stack.add(selectedIndex, obj);
                }
            } else if (c == '>') {
                whileStatements.add(new Integer(cl));
            } else if (c == '<') {
                cl = whileStatements.get(whileStatements.size() - 1);
            } else if (c == '!') {
                whileStatements.remove(whileStatements.size() - 1);
            } else if (c == '?') {
                if (stack.get(selectedIndex) instanceof Boolean) {
                    Boolean bool = (Boolean) stack.remove(selectedIndex);
                    if (bool == false) {
                        ifStatements++;
                        for (int cl0 = cl; cl0 < content.length(); cl0++) {
                            if (content.charAt(cl0) == '¿') {
                                ifStatements--;
                                cl = cl0;
                            }
                        }
                    }
                }
            } else if (c == 't') {
                break;
            } else if (c == '(') {
                stack.remove(selectedIndex);
            } else if (c == ':') {
                cl++;
                char charToVar = content.charAt(cl);
                vars.put(charToVar + "", stack.remove(selectedIndex));
            } else if (c >= 'A' && c <= 'Z') {
                stack.add(vars.get(c + ""));
            } else if (c == 'r') {
                stack.add(selectedIndex,
                        (double) new Random().nextInt(((Double) stack.remove(selectedIndex)).intValue() + 1));
            }
        }
        scanner.close();
    }

    public static String rot13(String input) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c >= 'a' && c <= 'm')
                c += 13;
            else if (c >= 'A' && c <= 'M')
                c += 13;
            else if (c >= 'n' && c <= 'z')
                c -= 13;
            else if (c >= 'N' && c <= 'Z')
                c -= 13;
            sb.append(c);
        }
        return sb.toString();
    }

    public static boolean isNumber(char c) {
        return c >= '0' && c <= '9';
    }

}

Đây có phải là khó sử dụng? : /
Máy

0

Rainbow (Lưu ý: Phiên dịch sắp ra mắt)

Tôi biết thử thách này đã hết hạn.

Cầu vồng là sự pha trộn của ... rất nhiều thứ.

Rainbow là ngôn ngữ dựa trên ngăn xếp 2D với hai ngăn xếp (Giống như Brain-Flak) và 8 hướng ( N NE E SE S SW W NW). Có 8 lệnh:

  • 1, +, *, "Thực hiện chính xác những gì họ làm trong 1+.
  • ! bật tắt ngăn xếp hoạt động.
  • > xoay IP theo chiều kim đồng hồ.
  • , nhập một ký tự và đẩy nó.
  • . bật và xuất một ký tự.

Tuy nhiên, các ký tự trong mã nguồn không được thực thi ngay lập tức. Thay vào đó, [The Character in the source code]^[Top Of Stack]được đưa vào điều phỏng đoán Collatz và số bước cần thiết để đạt 1 được chuyển đổi thành một ký tự bằng bảng ASCII. Nhân vật này sau đó được thực thi.

  • Nếu phải mất hơn 127 bước để đạt 1, tổng số bước được chia cho 127, hãy thực hiện nhắc nhở và sau đó thêm lời nhắc vào thương số.

Khi bắt đầu chương trình, mã nguồn (ngoại trừ ký tự cuối cùng) được đẩy lên ngăn xếp.

Khi IP đạt đến cạnh của mã nguồn, nó sẽ chấm dứt.

tận thế

n và m là hai thanh ghi. Khi một >lệnh được thực thi, m được tăng lên. Ngày tận thế chỉ được kích hoạt nếu m vượt quá n. Khi ngày tận thế xảy ra, nó:

  • Xoay ngược chiều kim đồng hồ thay vì theo chiều kim đồng hồ.
  • m trở thành 0.
  • n trở thành đỉnh của ngăn xếp. Và sau đó, ngăn xếp được bật lên.

m ban đầu là 0 và n ban đầu là ký tự cuối cùng của mã nguồn.

Mã hóa

Sau khi thực hiện bất kỳ thực thi, mã nguồn được mã hóa. ASCII của ký tự thứ 1 được tăng thêm một, ký tự thứ 2 giảm một, ký tự thứ ba tăng hai, ký tự thứ 4 giảm hai, v.v.


1
khá chắc chắn rằng bạn cần một thông dịch viên để có câu trả lời hợp lệ này ...
Conor O'Brien

@ ConorO'Brien Vì thử thách này đã hết hạn, đây chỉ là để giải trí. Tôi sẽ cung cấp thông dịch viên, mặc dù.
HighRiereactive

@HighlyRadioactive ... bạn đã nói gần một tháng trước.
pppery
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.