使用できないように見えるプログラミング言語を作成する


85

強盗の挑戦スレッドはこちらです。

Copsの課題:プログラミングには使用できないように見えるが、何らかの非自明なメカニズムを通じて計算(または少なくともタスクの完了)を許可するプログラミング言語を設計します。

入力ファイルからコードを読み取り、次に何かを行う単純なプログラミング言語を設計する必要があります。インタプリタで実行するときに、入力で3番目に大きい数を見つけるソリューションプログラムを準備する必要があります。強盗がソリューションプログラムを見つけるのを可能な限り難しくする必要があります。強盗は、あなたが考えていたものだけでなく、タスクを達成するあらゆるソリューションを投稿できることに注意してください。

これは人気コンテストです。警官の目標は、通訳を投稿してから8日間、クラックされることなく生き残りながら、できるだけ多くの票を得ることです。そのためには、次のプラクティスが役立ちます。

  • 言語のセマンティクスを正確に説明する
  • 読みやすいコードを書く

次の戦術は強く推奨されません。

  • 暗号化、ハッシュ、またはその他の暗号化方法を使用します。RSA暗号化を採用している言語、またはそのSHA-3ハッシュが0x1936206392306に等しくない限りプログラムの実行を拒否する言語を見つけた場合は、遠慮なく投票してください。

強盗の挑戦:警官のインタープリターで実行されたときに、入力で3番目に大きい整数を見つけるプログラムを作成します。

これは比較的簡単です。警官の答えを解読するには、インタープリターで実行されたときにタスクを完了するプログラムを作成する必要があります。回答をクラックするときは、投稿にリンクしている警官の回答に「クラック」というコメントを投稿します。最も警官を割った者は、強盗のスレッドに勝ちます。

I / Oルール

  • 通訳者は、プログラムのコマンドラインでファイル名を取得し、実行時に標準入出力を使用する必要があります。
  • 入力は単項で与えられ、文字01(ASCIIでは48および49)のみで構成されます。数値Nは、Nに 1s続いてa としてエンコードされ0ます。0ファイルの終わりの前に追加があります。例:シーケンス(3、3、1、14)の場合、入力は11101110101111111111111100です。
  • 入力には、少なくとも3つの数字が含まれることが保証されています。すべての数値は正の整数です。
  • 出力は1、プログラムが停止する前に印刷されたの数によって判断されます。他の文字は無視されます。

次の例では、最初の行は10進形式の入力です。2番目は実際のプログラム入力です。3番目はサンプル出力です。

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>

警官の答えの退屈なルール:

  • あいまいさによるセキュリティを防止するために、インタープリターはこのTIOBEインデックスのトップ100の言語で記述され、自由に利用できるコンパイラー/インタープリターを持つ必要があります。
  • 通訳者は、このチャレンジの前に発行された言語を解釈してはなりません。
  • 通訳者は投稿に適合し、外部でホストされるべきではありません。
  • 通訳者は決定論的である必要があります
  • 通訳者は移植性があり、独自の言語の標準に従う必要があります。未定義の動作やバグを使用しないでください
  • ソリューションプログラムが長すぎて答えに収まらない場合は、それを生成するプログラムを投稿する必要があります。
  • ソリューションプログラムは、印刷可能なASCIIと改行のみで構成する必要があります。
  • 上記の各入力例について、自分のコンピューターで1時間以内にソリューションプログラムを実行する必要があります。
  • プログラムは、合計入力長が10 9未満であれば、10 6未満の整数、および10 6未満の整数(1時間未満である必要はない)の任意の数で動作するはずです。
  • 安全になるためには、警官は8日が経過した後、解答プログラムを編集して解答にしなければなりません。

得点

最高のスコアと正のスコアで安全になった警官がこの質問に勝ちます。


あなたはこれを明示的に述べていませんが、警官が実際に彼らの答えに通訳を書いて投稿しなければならないと仮定するのは正しいですか?
ブルー

@muddyfishはい、通訳は警官の回答の内容でなければなりません。
feersum

1
@ kirbyfan64sos 出力は、プログラムが停止する前に印刷される1の数によって判断されます。他の文字は無視されます。
mbomb007


20
私はこの挑戦をオタクにした。プログラミング言語を作成し、そのタスクをプログラミングするのに何時間も費やして、その言語が実際使用できないことを見つけるためだけに機能するかどうかを確認しました。
-Sanchises

回答:


24

チェンジリング(安全)

ShapeScript

ShapeScriptは、自然に発生するプログラミング言語です。シェイプシフター(または呼ばれることを好むChangelings)は、データを処理できるようにする命令セットに変換できます。

ShapeScriptは、比較的単純な構文を持つスタックベースの言語です。当然のことながら、その組み込みのほとんどは、文字列の形状の変更を処理します。次のように、文字ごとに解釈されます。

  • '"文字列リテラルを開始します。

    一致する引用符がソースコードで見つかるまで、これらの一致する引用符の間のすべての文字は文字列に収集され、スタックにプッシュされます。

  • 0する9整数押して0を9スタック上。2つの整数を10プッシュすることに注意してください。

  • ! スタックから文字列をポップし、ShapeScriptとして評価しようとします。

  • ? スタックから整数をポップし、そのインデックスでスタックアイテムのコピーをプッシュします。

    インデックス0は一番上のスタック項目(LIFO)に対応し、インデックス-1は一番下のスタック項目に対応します。

  • _ スタックからイテラブルをポップし、その長さをプッシュします。

  • @ スタックから2つのアイテムをポップし、それらを逆の順序でプッシュします。

  • $スタックから2つの文字列をポップし、一番上の文字列の出現時に一番下の文字列を分割します。結果のリストが返されます。

  • &文字列(最上位)とイテレート可能オブジェクトをスタックからポップし、文字列をセパレータとして使用してイテレート可能オブジェクトを結合します。結果の文字列が返されます。

  • 私たちの惑星でShapeScriptが使用されている場合、PythonはChangelingsが地球上で最も近い親sinceであるため、他のすべての文字cはスタックから2つのアイテムxおよびy(最上位)をポップし、Pythonコードを評価しようとしますx c y

    たとえば、文字のシーケンス23+が評価され2+3、文字のシーケンス"one"3*が評価され'one'*3、文字のシーケンス1''Aが評価され1A''ます。

    最後のケースでは、結果が有効なPythonではないため、Changelingは、現在の形状が有効なShapeScriptではないため、現在の形状が意図されていない(構文エラー)と文句を言います。

コードを実行する前に、インタープリターはユーザー入力全体を文字列形式でスタックに配置します。ソースコードの実行後、インタープリターはスタック上のすべてのアイテムを印刷します。中間のいずれかの部分に障害が発生した場合、チェンジリングは、現在の形状が不適切であると不平を言います(実行時エラー)。

形状シフト

自然な状態では、ChangelingsはShapeScriptの形を取りません。ただし、それらの一部は、1つの潜在的なソースコードに変換できます(必ずしも有用ではなく、構文的にも有効ではありません)。

すべての適格なチェンジリングの自然な形は次のとおりです。

  • すべての行は同じ文字数でなければなりません。

  • すべての行は、印刷可能なASCII文字とそれに続く単一の改行で構成する必要があります。

  • 行数は、1行あたりの印刷可能文字数と一致する必要があります。

たとえば、バイトシーケンスab\ncd\nは適格なチェンジリングです。

シェイプスクリプトへの移行を試みて、チェンジリングは次の変換を受けます。

  • 最初は、ソースコードはありません。

  • 各行について、次のことが起こります。

    • Changelingのアキュムレーターはゼロに設定されます。

    • 行の各文字c(末尾の改行を含む)について、cのコードポイントはアキュムレータを2で割ったものとXORされ、結果のコードポイントに対応するUnicode文字がソースコードに追加されます。

      その後、cのコードポイントとスペースのコードポイント(32)の差がアキュムレータに追加されます。

上記のいずれかの部分が失敗した場合、チェンジリングは現在の形状が不快であると不平を言います。

すべての行が処理された後、Changelingの(おそらく有効な)ShapeScriptへの変換が完了し、結果のコードが実行されます。

ソリューション(ShapeScript)

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

実際、ShapeScriptは非常に使いやすいことが判明しました。素数テスト(証明)も実行できるため、プログラミング言語の定義を満たします。

GitHubShapeScriptを再公開しました。構文を少し変更し、I / Oを改善しました。

コードは次のことを行います。

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

ソリューション(チェンジリング)

"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=....................
""# !......................

すべてのChangelingプログラムと同様に、このコードには末尾の改行があります。

ShapeScriptは、理解できない文字についてはすぐにエラーになりますが、任意の文字列をフィラーとしてプッシュし、後でポップすることができます。これにより、各行に少量のコード(最初はアキュムレータが小さい場合)を配置し、その後に開始することができ"ます。次の行をで始める"#と、実際のコードに影響を与えずに文字列を閉じてポップします。

さらに、3つの小さな障害を克服する必要があります。

  • ShapeScriptコードの長い文字列は単一のトークンであり、1行に収めることはできません。

    私たちは、チャンク(この文字列をプッシュします'@''1?'我々は後に連結するだろう、など)を、。

  • のコードポイント_はかなり高く、プッシュ'_'は問題があります。

    ただし、'_@'簡単にプッシュでき、その後'@'にスワップを元に戻すことができます。

Changelingが生成するShapeScriptコードは次のようになります1

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

このコンバーターを介して上記のShapeScriptコードを実行すると、Changelingコードが見つかりました。2

通訳(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 各行には、行の量に合わせてランダ​​ムなゴミが埋め込まれますが、実際には改行は存在しません。
2 下部の数字は、Changelingコードの最低および最高のコードポイントを示し、32〜126の間に留まる必要があります。


1
xor / transformationsを使用する場合は-1。ShapeScript変換への変更は、私にとって暗号化のように見えます。
メガトム

10
@MegaTomあなたは適切に投票することができますが、キーを取得するため、質問は暗号化で眉をひそめます。変換はキーなしの変換です。
デニス

1
ShapeScript、67バイト:0"#002?'+'&'0'$'0?2?-@2?>*+00'&!++'1'*'0'+@1?$0?''&@_2-2?*@+@"3*!@#。しかし、私はそのためにチェンジリングを見つけるのをあきらめました。ほとんど役に立たない文が散在していても、20バイト以上を取得することができませんでした。
primo15年

2
@MegaTom私は実際、与えられたソリューションにかなり失望しています。92.9%の無駄なコードよりもはるかに賢い何かを期待していました。
primo

2
@primo少し手を加えましたが、このChangelingはPython 2でも動作することがわかりました。私の答えがどれほど賢いかわからないが、それをクラックするために見つけなければならなかった抜け穴のある警官を投稿する私の計画はうまくいったようだ。
デニス

30

シャッフル(C ++で記述)、ひび割れ!マーティン

編集 マーティンはそれをクラックしました。彼の解決策を見るには、リンクをクリックしてください。私のソリューションも追加されました。

固定コマンドを編集して、レジスタとスタックの両方を処理できるようにします。これはソリューションで許可されていないデバッグコマンドであるため、インタープリターの以前のバージョンを使用しているユーザーには影響しません。print-d

私はまだこれに慣れていないので、答えや通訳に何か問題がある場合はお知らせください。不明な点がある場合は、説明を求めてください。

これがあまりにも難しいとは思いませんが、うまくいけばある種の挑戦を提供できるでしょう。シャッフルがかなり使用できない理由は、シャッフルが適切な場所にある場合にのみ印刷されることです。

->基本:

スタックは24個あり、それらを呼び出しますstack1, ... stack24。これらのスタックはリストに存在します。任意のプログラムの開始時に、これらのスタックがゼロでプッシュしているし、彼らは適切な場所に開始、すなわちスタックIにおけるi番目のリスト内の位置(私たちはインデックスはC ++とは異なり、1から始まることに注意してください)。プログラムのコース中に、リスト内のスタックの順序が変わります。これは、コマンドについて説明するときに説明する理由のために重要です。

使用可能なレジスタは5つあります。彼らの名前はAlbertoGertrudeHansLeopoldShabbySam。これらはそれぞれ、プログラムの開始時にゼロに設定されます。

そのため、プログラムの開始時には24のスタックがあり、各スタックの番号はスタックリストのインデックスと一致しています。すべてのスタックの一番上にゼロが1つだけあります。5つのレジスタはそれぞれゼロに初期化されます。

->コマンドと構文

Shuffleで使用できるコマンドは13個(+1デバッグコマンド)です。それらは次のとおりです

  • cinpushこのコマンドは引数を取りません。質問で説明されている方法でコマンドライン入力を待機します(他の入力は未指定/未定義の結果につながります)。次に、入力文字列を整数に分割します(例:101011100->)1,1,3。受信した各入力に対して、次の処理を実行します。(1)値に基づいてスタックのリストを並べ替えます。問題の整数値をaと呼びますaが10未満の場合順列uを実行します。aが9〜30(非包括的)の場合順列dを実行します。それ以外の場合、置換rを実行します。(2)その後リストの最初にあるスタックに。私が意味するものではないことに注意してくださいstack1(ただしstack1、リストの最初にある場合もあります)。以下に順列を定義します。以来cinpush、ユーザー入力を取得する唯一の方法である、それは任意の溶液中で現れなければなりません。
  • mov value registermovコマンドは、基本的には変数の代入です。これは、割り当てvalueregistervalueいくつかの形態を取ることができる:することができる(1)の整数、例えば、47 (2)例えば、異なるレジスタ名、Hans (3)スタックのインデックスは、例えば、「S」が続きます4s。これはリスト内のインデックスであり、スタックの番号ではないことに注意してください。そのため、この数は24を超えてはなりません。

    いくつかのmov例:

    mov 4s Hans 
    mov Hans ShabbySam
    mov 9999 Gertrude
    
  • movfs index registerこれは「スタックから移動」の略です。movコマンドに似ています。レジスタによってインデックス付けされたスタックにアクセスできるようにするために存在します。たとえば、以前にHansを4(mov 4 Hansmovfs Hans Gertrudeに設定した場合、を使用してGertrudeをスタック4の最上部に等しく設定できます。このタイプの動作は、単にを使用してアクセスできませんmov

  • inc register レジスタ値を1増やします。
  • dec register レジスタ値を1減らします。
  • compg value value registerこれは「比較する」の略です。レジスタを2つの値の大きい方に設定します。value上記のように、整数、レジスタ、またはスタックインデックスの後に 's'を指定できます。
  • compl value value register 「より小さいと比較」は上記と同じですが、より小さい値を使用します。
  • gte value1 value2 registervalue1 >= value2ブール値(1または0)をに入れるかどうかを確認しregisterます。
  • POP!! indexindexスタックリストでインデックスが付けられたスタックの最上部からポップします。
  • jmp labelラベルに無条件にジャンプしlabelます。これは、ラベルについて話す良い機会です。ラベルは、単語の後に「:」が続きます。インタプリタはラベルの事前解析を行うため、ラベルを前後にジャンプできます。
  • jz value labellabelif valueがゼロにジャンプします。
  • jnz value labellabelif valueがゼロ以外の場合にジャンプします。

    ラベルとジャンプの例:

    this_is_my_label:
         jmp this_is_my_label
    
    mov 10 Hans
    jnz Hans infinite_loop
    
    infinite_loop:
         jmp infinite_loop
    
  • "shuffle" permutationシャッフルコマンドを次に示します。これにより、スタックのリストを並べ替えることができます。引数として使用することができる3個の有効な順列がありlfとはb

  • print registerこれにより、すべてのスタックが初期位置にあるかどうか、つまりスタックiがスタックリストのインデックスiにあるかどうかがチェックされます。この場合、値をregister単項で出力します。そうでなければ、厄介なエラーを出力します。ご覧のとおり、何かを出力するには、すべてのスタックが正しい場所になければなりません。
  • done!これは、エラーなしで終了するようにプログラムに指示します。プログラムがなしdone!で終了した場合、各スタックの先頭にある番号に続いてスタックの番号がコンソールに出力されます。スタックが印刷される順序は、スタックリストに表示される順序です。スタックが空の場合、省略されます。この動作はデバッグを目的としたものであり、ソリューションで使用することはできません。
  • print-d valueこれは、指定されたスタック、レジスタ、または整数の値を出力します(上記で説明したように、スタックiにアクセスするにisは引数として渡します)。これはデバッグツールであり、言語の一部ではないため、ソリューションでは許可されていません。

->これがインタープリターのコードです

すべての解析はメイン関数で行われます。これは、特定のコマンドを解析する場所です。

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

->順列順列 は、スタックリストの要素を次のように並べ替えます。

それはどういう意味ですか

(これらはインタプリタコードにも表示されます。矛盾がある場合、インタプリタは正しいです。)

->簡単な例

これらの2つの単純なプログラムは、空白なしで24から1の数値(単項)を出力します。

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

または

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

それらは同じプログラムです。

説明と解決策:

マーティンは彼の答えにも良い説明があります。

マーティンが理解したように、この言語はポケットキューブ(別名2x2ルービックキューブ)に触発されました。24個のスタックは、キューブ上の24個の個々の正方形のようなものです。順列は、許可されている基本的な動きです:上、下、右、左、前、後ろ。

ここでの主な問題は、値がプッシュされると、上下左右の3つの動きのみが使用されることです。ただし、スタックを「シャッフル」するときは、これらの移動にアクセスできません。他の3つの動きのみがあります。

結局のところ、3つの動きの両方のセットは実際にはグループ全体に及ぶ(つまりジェネレーターである)ため、問題は解決可能です。つまり、実際には3つの動きを使用して2x2ルービックキューブを解くことができます。

あとは、他の3つを使用して上下左右の動きを元に戻す方法を見つけるだけです。この目的のために、GAPと呼ばれるコンピューター代数システムを採用しました。

順列を元に戻した後、3番目に大きい数を見つけるのは非常に簡単です。

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
割れた。:)私は言語に本当に感銘を受けました!
マーティンエンダー

すごい速かった。おかげで、書くことと同じくらい理解するのが楽しかったです。
リアム

好奇心が強いのですが、順列の名前をルービックキューブについてそれほど明白ではない名前に変更した場合、それはかなり難しくなったでしょうか。
リアム

彼らは間違いなく手がかりでしたが、別の名前があったとしてそれほど長くはかからなかったと思います。
マーティンエンダー

へえ、GAPは3つの入力順列を逆にすることについて特に賢明ではなかったようです。;)
マーティンエンダー

22

ブライアン&チャックcardboard_boxによるクラック

私は、2つのプログラムが互いに対話するプログラミング言語(おそらくROCBに触発された)のアイデアに興味をそそられています。このチャレンジは、言語を可能な限り最小限に抑えようとしながら、ついにこの概念に取り組む良い動機となりました。設計目標は、各部分を個別にチューリング完全ではなく、言語をチューリング完全にすることでした。さらに、ソースコード操作を使用せずに、それらの両方をチューリング完全にすることはできません。私それで成功したと思いますが、私はまだこれらのことを正式には証明していません。苦労せずにあなたにプレゼントします...

主人公

ブライアンとチャックは2つのBrainfuckのようなプログラムです。ブライアンから始めて、一度に実行されるのはそのうちの1つだけです。欠点は、ブライアンのメモリテープがチャックのソースコードでもあるということです。また、チャックのメモリテープもブライアンのソースコードです。さらに、ブライアンのテープヘッドはチャックの指示ポインターでもあり、その逆も同様です。テープは半無限(つまり、右に無限)であり、ゼロに初期化された符号付き任意精度整数を保持できます(ソースコードで特に指定されていない限り)。

ソースコードもメモリテープであるため、コマンドは技術的には整数値で定義されますが、適切な文字に対応しています。次のコマンドが存在します。

  • ,44):STDINから現在のメモリセルにバイトを読み取ります。これができるのはブライアンだけです。このコマンドは、チャックの操作なしです。
  • .46):256を法とする現在のメモリセルをバイトとしてSTDOUTに書き込みます。チャックだけがこれを行うことができます。このコマンドは、ブライアンにとっては何もしません。
  • +43):現在のメモリセルをインクリメントします。
  • -45):現在のメモリセルをデクリメントします。
  • ?63):現在のメモリセルがゼロの場合、これはノーオペレーションです。それ以外の場合は、他のプログラムに制御を渡します。使用?するプログラムのテープヘッドはに残り?ます。他のプログラムのテープヘッドは、最初のコマンドを実行する前に1つのセルを右に移動します(そのため、テストとして使用されるセル自体は実行されません)。
  • <60):テープヘッドを1セル左に移動します。テープヘッドがすでにテープの左端にある場合、これはノーオペレーションです。
  • >62):テープヘッドを1セル右に移動します。
  • {123):現在のセルがゼロになるか、テープの左端に達するまで、テープヘッドを左に繰り返し移動します。
  • }125):現在のセルがゼロになるまで、テープヘッドを右に繰り返し移動します。

アクティブなプログラムの命令ポインターが、右側にこれ以上命令がないポイントに達すると、プログラムは終了します。

ソースコード

ソースファイルは次のように処理されます。

  • ファイルに文字列が含まれている場合、ファイルはその文字列```の最初の出現の前後で2つの部分に分割されます。先頭と末尾の空白はすべて削除され、最初の部分はBrianのソースコードとして使用され、2番目の部分はChuckのソースコードとして使用されます。
  • ファイルにこの文字列が含まれていない場合、ファイルの最初の行はBrianのソースとして使用され、Chuckの2番目の部分として使用されます(区切り改行を除き、空白は削除されません)。
  • _両方のプログラムでのすべての出現は、NULLバイトに置き換えられます。
  • 2つのメモリテープは、結果の文字列に対応する文字コードで初期化されます。

例として、次のソースファイル

  abc
```
0_1
23

次の初期テープが生成されます。

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

通訳

インタプリタはRubyで書かれています。(実際の言語仕様の一部ではないため)ソリューションで使用してはならない 2つのコマンドラインフラグを使用します。

  • -d:このフラグを使用すると、BrianとChuckはさらに2つのコマンドを理解します。!両方のメモリテープの文字列表現を出力します。アクティブなプログラムが最初にリストされます(a ^は現在のテープヘッドをマークします)。@これも実行しますが、その後すぐにプログラムを終了します。私は怠け者なので、それらがプログラムの最後のコマンドである場合はどちらも機能しないため、そこで使用したい場合は、それらを繰り返すか、それらの後にノーオペレーションを記述します。
  • -D:これは冗長デバッグモードです。!ティックごとに同じデバッグ情報を出力します。@このモードでも機能します。

コードは次のとおりです。

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

ここに問題に対する私自身の(手書きの)ソリューションがあります:

>}>}>
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:
{<{<?

であるように、すべての注釈が何-OPSを使用していないとでスキップされるため、このコードは、実行可能である{}

基本的な考え方は次のとおりです。

  1. 新しいゼロ要素をリストに追加し(チャックのテープの最後)、リストの長さを1増やします。
  2. 1s を読んでいる間に、その要素を増やします。
  3. を読んでいるときに0、いくつかのクリーンアップを行います。結果の整数がゼロより大きい場合、1に戻ります。

    これで、チャックのテープの最後に入力番号のリストができました。リストの長さはわかっています。

  4. リストの長さから2を引く(したがって、次の手順では最大の2つの要素を無視する)ため、これを呼び出しますN

  5. 一方N > 0、現在の合計をインクリメントしてから、すべてのリスト要素をデクリメントします。リスト要素がゼロになるたびに、decrement N

    これが終了すると、現在の合計には、入力で3番目に大きい数が含まれますM

  6. Mコピーを.チャックのテープの最後に書きます。

  7. チャックで、1ブライアンのテープを検索.し、最後に生成されたテープを実行します。

これを終えた後、いくつかの場所でかなり単純化できることに気づきました。たとえば、そのカウンターを追跡.してチャックのテープに書き込むのではなく1、リスト要素をすべて減らす前に、すぐに印刷することができます。ただし、このコードに変更を加えると、他の変更がすべての場所に伝播するため、非常に苦痛になります。

興味深いのは、リストを追跡する方法と、リストを反復処理する方法です。チャックのテープに数字を連続して保存することはできません。リスト要素の1つで条件を確認したい場合、有効なコードを含む可能性のあるリストの残りの部分を実行するリスクがあるためです。リストの長さもわからないため、Chuckのコードの前にスペースを確保することはできません。

次の問題は、N処理中にリストを減少させる必要があり、以前と同じ場所に戻る必要があるということです。しかし{}リスト全体をスキップします。

したがって、Chuckにいくつかのコードを動的に書き込む必要があります。実際、各リスト要素iの形式は次のとおりです。

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

1リストの処理を中断した場所を示すためにゼロに設定できるマーカーです。その目的は、キャッチすることです{か、}これだけのコードと上を通過しますi。また、この値を使用して、処理中にリストの最後にいるかどうかを確認します。そうでない場合は、これが実行さ1れ、条件付きで?コントロールがチャックに切り替わります。Code Aその状況を処理し、それに応じてブライアンのIPを移動するために使用されます。

デクリメントiするとき、iすでにゼロかどうかを確認する必要があります。そうでない間、?再び制御を切り替えるので、それCode Bを処理するためにあります。



@cardboard_boxいいね!
mbomb007

15

Python 3で記述されたHPR(TheNumberOneによりクラック

HPR(名前は何も意味しない)は、整数のリストを処理するための言語です。ミニマルで非常に制限され「人工」の制限がないように設計されています。HPRでのプログラミングは、通訳があなたに向かって叫ばないようにするためにパズルを解かなければならないからではなく、プログラムに何か有用なことをさせるのが難しいからです。HPRがリストの3番目に大きい要素を計算するよりも実質的に興味深いものを実行できるかどうかはわかりません。

言語仕様

HPRプログラムは、環境で実行されます。これは、非負整数の非順序重複なしセットおよび非負整数のリストです。最初は、環境には入力リストのみが含まれています(インタープリターがそれを解析します)。環境を変更する3つのコマンドと2つの「制御フロー」演算子があります。

  • *環境内のすべての空でないリストの最初の要素を削除し、環境に配置します。空のリストは影響を受けません。たとえば、変換します

    {4,1,[0,2],[1,3],[]} -> {4,1,0,[2],[3],[]}
    
  • -環境内のすべての数値をデクリメントしてから、負の要素を削除します。たとえば、変換します

    {4,2,0,[0,2],[4,4,4]} -> {3,1,[0,2],[4,4,4]}
    
  • $環境内のすべてのリストを1ステップ左に回転します。たとえば、変換します

    {4,1,[0,2],[3,4,1]} -> {4,1,[2,0],[4,1,3]}
    
  • !(A)(B)Aand Bはプログラムで、基本的にはwhileループです。A「テスト」のB結果が空の環境になるまで、「アクション」を実行します。これにより、無限ループが発生する場合があります。
  • #(A)(B)ここでAおよびBはプログラムでAありB、現在の環境に適用され、結果の対称的な差を取ります。

コマンドは左から右に実行されます。最後に、環境のサイズが単項で出力されます。

通訳

このインタープリターは、?環境を変更せずに出力するdebugコマンドを備えています。タスクのソリューションには表示されません。以外の文字はすべて*-$!#()?無視されるため、コードに直接コメントを書くことができます。最後に、インタプリタはイディオム!(A)(#(A)())を「A結果が変化しなくなるまで実行する」と認識し、速度を最適化するように最適化します(最後のテストケースでソリューションを1時間以内に完了する必要がありました)。

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

私の解決策

私の参照ソリューションは484バイトの長さなので、TheNumberOneの3271バイトのプログラムに比べてかなり短いです。これは、HPRでのプログラミング用に開発されたTheNumberOneの洗練された素晴らしいマクロシステムが原因である可能性が最も高くなります。両方のプログラムの主なアイデアは似ています:

  1. リストの最大要素を作成する方法を見つけてください。
  2. 最大の要素を削除するには、最初の要素が最大になるまでリストを回転させてから、ポップします。
  3. 最大値を2回削除してから、新しい最大要素を印刷します。

ただし、私が知る限り、実装の詳細はまったく異なります。私のソリューションは次のとおりです。

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

そして、ここにそれを生成するコメント付きのPythonプログラムがあります(ここでは空想はありません。すべての繰り返しを取り除くための基本的な文字列操作です)。

# 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は私のソリューションを追加しました。
ズガルブ

12

TKDYNS(ドラゴンを殺すには剣が必要)- マーティン・ブットナーによるひび割れ

編集:メインポストの下にソリューションと説明を追加しました。

バックグラウンド

この言語では、ひどいドラゴンを殺すことを任された勇敢な戦士をコントロールします。ドラゴンは危険と危険に満ちた地下の迷宮に住んでおり、これまで誰もそれをマップして生き残ることができませんでした。これは、暗闇の中でドラゴンへの道をナビゲートしなければならないことを意味します。

...

まあ、そうではありません。あなたは使い捨てミニオンをほぼ無限に持ち込みました。彼らは安全なルートを発見するためにあなたの前に進んで行きます。残念ながら、それらはすべて2つの短い厚板と同じくらい厚く、あなたが彼らに指示したとおりに正確に行うだけです。あなたの手下が正しい道を発見することを確実にする賢い方法を考え出すのはあなた次第です。

いくつかの詳細

ドラゴンの巣は10x10グリッドの形をとります。グリッド内の特定の隣接するポイント間には、狭い通路があります。他の人との間に、深い割れ目と特定の死があります。4x4グリッドのレイアウト例は次のとおりです。

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

ある地点から他の地点に到達する方法は常にあることを知っていますが、それ以上は明らかにされていません。

ドラゴンを無事に倒すには、最初にいくつかのアイテムを収集する必要があります。これらのアイテムを組み合わせて、魔法のドラゴンを倒すブレードを作成できます。便利なことに、この武器のすべてのピースはドラゴンの巣の周りに散らばっています。あなたは単にそれらを収集する必要があります。

ひねりは、武器の各部分がブービートラップされていることです。1つが収集されるたびに、通路のレイアウトが変わります。以前は安全だったパスが特定の死につながる可能性があり、逆もまた同様です。

コマンド

有効なコマンドは5つのみです。

  • < -左へ一歩

  • > -右へ一歩踏み出す

  • ^ -上に一歩

  • v -一歩下がって

  • c-現在の位置に横たわっているアイテムを収集します。アイテムが存在する場合、隠れ家のレイアウトが変更されます。上記のように位置に行番号を付けて、10を法とする位置を取ります。インタープリターにハードコーディングされた10個のレイアウトがあり、レイアウトは対応するものに変更されます。たとえば、位置13にいる場合、レイアウトは次のように変更されます。layouts[3]

インターピーターに表示されるレイアウトは、次の方法で整数にエンコードされています。

  • 空のレイアウトはゼロにエンコードされます。

  • レイアウト内の各エッジについて、x結合する2つの位置のうち小さい方にしましょう。

    • ステップが水平の場合、2^(2*x)エンコードに追加します(XORではなく、べき乗です)

    • ステップが垂直の場合、2^(2*x + 1)エンコードに追加します。

実行フロー

インタープリターは、ソースファイルの名前をコマンドライン引数として実行されます。

インタープリターが実行されると、ユーザーにクエストを提供するように促します。この入力は質問で説明されている形式である必要があり、武器のコンポーネントの隠れ家の位置を決定します。具体的には、各入力整数は100を法として取得され、隠れ家の対応する位置に配置されます。

各ソースプログラムは複数の行で構成され、各行は上記の5つの有効な文字のシーケンスで構成されます。これらの線はあなたの手下を表しています。戦士であるあなたは、安全であると知られている一連の行動を追跡します。最初は、隠れ場所について何も知らないので、このシーケンスは空です。各ミニオンを順番に取得して、位置0から開始して以下を実行します。

  • ミニオンは、安全であることがわかっているすべてのアクションを実行するように指示され、その後に独自のコード行のアクションが続きます。

    • ミニオンがいずれかの時点で死んだ場合、これが通知され、隠れ家は初期構成にリセットされます。すべてのアイテムが交換され、通路は開始位置に戻ります。

    • 代わりに、ミニオンが生き残った場合、とにかくそれを蒸発させます-結局のところ、それは単なるミニオンです。前と同様に、これは隠れ家の初期状態へのリセットをトリガーしますが、今回は、ミニオンのコード行のアクションを既知の安全なアクションのシーケンスに追加します。

すべてのミニオンが使い果たされると、戦士であるあなたは、安全であると知られているすべてのアクションを実行し、再びポジション0から始めます。2つの可能な結果があります。

  • 武器のすべてのピースを収集します-この場合、ドラゴンを首尾よく殺し、エキサイティングな勝利メッセージが出力されます。この勝利メッセージは他の文字のうち、含有するであろうnもの、n入力として提供される最も高い第3の数です。

  • 武器の一部を収集できませんでした-この場合、ドラゴンは生き続け、クエストに失敗しました。

通訳者コード(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()

幸運、勇敢な戦士。

私の解決策と説明

以下は、問題を解決するためのソースコードを生成するPythonスクリプトです。興味深いことに、Martinの最終的なソースコードは、私のスクリプトで生成されたコードの約5倍です。一方、コードを生成するためのスクリプトは、MartinのMathematicaプログラムの約15倍小さい...

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

基本構造

これにより、990行のソースコードが生成されます。これらは10のグループになります。最初の10行にはすべて、ミニオンをpositionからposition 0に移動してから1アイテムを収集しようとする命令が含まれています。次の10行にはすべて、ミニオンをpositionからposition 1に移動してから2アイテムを収集しようとする指示が含まれています。など...これらのパスはpath、スクリプト内の関数を使用して計算されます-単純な深さ優先検索を実行します。

10の各グループで正確に1つのミニオンが成功することを確認できれば、問題は解決していることになります。

アプローチの問題

10のグループから正確に1つのミニオンが成功するわけではない場合があります。たとえば、0位置1から位置1へ移動するように設計されたミニオンは、位置から位置への移動に誤って成功する場合があります2は、(レイアウトの類似性により)場合があり、誤った計算が伝播し、潜在的に障害が発生します。

修正方法

私が使用した修正は次のとおりです。

位置nからに到達しようとしている各ミニオンに対してn+1、最初に位置nから位置0(左上隅)に戻って再び歩き、次に位置nから位置99(右下隅)に戻り、再び戻ります。これらの指示は、安全な位置からのみ実行できますn -他の開始位置と手先は端から歩き出します。

したがって、これらの追加の指示により、ミニオンが意図しない場所に誤って移動することを防ぎます。これにより、10の各グループから1つのミニオンが確実に成功します。それは必ずしもあなたが期待するミニオンではないことに注意してください-彼がレイアウト7にいると信じているミニオンが、実際にレイアウト7にいるときでも成功する場合があります-しかし、いずれにしても、私たちが持っているという事実現在の位置の変更は、グループ内の他のすべてのミニオンが必然的に死ぬことを意味します。これらの追加ステップは関数によって計算され、assert_at関数safe_pathはこれらの追加ステップと通常のパスを連結したパスを返します。


1
割れた。これはとても楽しかったですが、パズルを解くと実際にそのプログラミングタスクを解決することとは関係がないため、「プログラミング言語はこの1つのタスクを解決するだけ」というルールに問題があると思います。;)
マーティンエンダー

主に問題が存在することに気付いたため、この答えを作成しました-代わりに誰かが本当に難しい計算問題を使用するのを妨げるものはないようです。「いいえ暗号」の禁止は...任意に狭いようだ
サムCappleman-Lynes

本当。だからこそ、これが人気コンテストです。問題が計算的な性質のものであり、実際には非常に使いにくい適切な(チューリング完了の可能性のある)言語での回答ほど多くの票を集めるような素晴らしいストーリーに包まれていない場合。
マーティンエンダー

興味のあるソリューションを追加しました。また、興味のために、私はもともと1000x1000のグリッドを念頭に置いてパズルを設計しましたが、レイアウトを〜600000桁の数字にエンコードしないようにするために、小さいサイズを選択しました。
サム・アップルマン・ラインズ

8

Firetype、MartinBüttner によるクラック

BFとCJamの本当に奇妙なミックス。そして、誰が他に何を知っていますか!これが簡単なものであることは確かですが、とにかく楽しかったです。参考までに、その名前はファイナルファンタジータイプ0のバーミリオンファイアを指します

:この説明のあいまいさはご容赦ください。私は最高の作家ではありません...:O

マーティンはこれをすぐにクラックしました!これは問題を解決するための私のオリジナルのプログラムでした:

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











`
~
+
|
%

_
=

\
@
-
-
!
<
>
>
>

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

構文

Firetypeスクリプトは、基本的に行のリストです。各行の最初の文字は、実行するコマンドです。空行は基本的にNOPです。

意味論

整数の配列とポインターがあります(BFを考えてください)。配列を左または右に移動したり、要素を「プッシュ」したりできます。

押す

要素を「プッシュ」して、配列の最後にいる場合、余分な要素が最後に追加されます。終了していない場合、次の要素は上書きされます。とにかく、ポインターは常にインクリメントされます。

コマンド

_

ゼロを配列にプッシュします。

+

現在のポインタで要素をインクリメントします。

-

現在のポインタで要素をデクリメントします。

*

現在のポインターで要素を2倍にします。

/

現在のポインターで要素を半分にします。

%

現在のポインターの位置にある要素を取得し、その行だけ前方にジャンプして、ポインターを左に移動します。値が負の場合、代わりに後方にジャンプします。

=

現在のポインタで要素を取得し、その行+ 1にジャンプします。たとえば、現在の要素がの0場合、これはlineにジャンプします1。これにより、ポインターも左に移動します。

,

標準入力から文字を読み取り、そのASCII値をプッシュします。

^

現在のポインターで要素を取得し、文字のASCII値として解釈し、整数に変換します。たとえば、現在の値が49 (のASCII値1)の場合、現在のポインターの要素は整数に設定され1ます。

.

現在の番号を画面に書き込みます。

!

現在のポインタで要素を取得し、次の行を何回も繰り返します。また、ポインターを左に移動します。

<

ポインターを左に移動します。すでに開始している場合は、エラーがスローされます。

>

ポインターを右に移動します。すでに終了している場合は、エラーがスローされます。

~

現在の要素がゼロ以外の場合は、0; に置き換えます。それ以外の場合は、に置き換え 1ます。

|

現在の要素を二乗します。

@

現在の要素を配列の長さに設定します。

`

現在の要素を複製します。

\

ポインタの位置とその前の要素を並べ替えます。

#

現在の要素を否定します。

通訳

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)

配列の下限のアサーションはバグだと思います。self.ptr-1アクセスに使用しているため、おそらくself.ptr>0notをチェックする必要があり>=0ます。(これは、任意の有効なプログラムを無効にはならないが、いくつかのプログラムは仕事が誤っているいけない作りになるかもしれない。)
マーティン・エンダー

もう1つ:コードは=、値をarray() - 1に設定すると言い、ドキュメントには+1
マーティンエンダー

割れた。(インタープリターが説明ではなく規範的であると仮定します。)
マーティンエンダー

@MartinBüttnerDang、それは思ったよりも速かった!:OIはあなたが言及したドキュメントの問題を修正しました。
kirbyfan64sos

7

Acc !! (安全)

バックだ...

ここに画像の説明を入力してください

...そして、できれば間違いなくより抜け穴のないものにしましょう。

私はすでにAccを読みましたスペック Accはどうですか!! 違う?

Acc !! 、ループが終了すると、ループ変数はスコープ外になります。ループ内でのみ使用できます。外部では、「名前が定義されていません」というエラーが表示されます。この変更以外は、言語は同じです。

声明

コマンドは行ごとに解析されます。コマンドには3つのタイプがあります。

  1. Count <var> while <cond>

C ++に相当するゼロでない<var>限り、0からカウントアップし<cond>ますfor(int <var>=0; <cond>; <var>++)。ループカウンターは、任意の1つの小文字にすることができます。条件には任意の式を使用できますが、必ずしもループ変数が関係するわけではありません。条件の値が0になると、ループが停止します。

ループにはK&Rスタイルの中括弧(特にStroustrupバリアント)が必要です。

Count i while i-5 {
 ...
}

前述のように、ループ変数はループ内でのみ使用可能です。後でそれらを参照しようとすると、エラーが発生します。

  1. Write <charcode>

指定されたASCII / Unicode値を持つ単一の文字をstdoutに出力します。文字コードには任意の式を使用できます。

  1. 表現

独立した式はすべて評価され、アキュムレータに割り当てられます(アキュムレータとしてアクセス可能_)。したがって、たとえば、3アキュムレータを3に設定するステートメントです。_ + 1アキュムレータをインクリメントします。_ * N文字を読み取り、アキュムレータにその文字コードを乗算します。

注:アキュムレータは、直接割り当てることができる唯一の変数です。変数をループしN、計算で使用できますが、変更はできません。

アキュムレータは最初は0です。

表現

式には、整数リテラル、アキュムレータ用のループ変数(a-z)、_およびN文字を読み取り、使用されるたびにその文字コードに評価する特別な値を含めることができます。注:これは、各キャラクターの読み取りに1ショットしか使用できないことを意味します。次にを使用するNときは、次のものを読んでいます。

演算子は次のとおりです。

  • +、追加
  • -、減算; 単項否定
  • *、乗算
  • /、整数除算
  • %、モジュロ
  • ^、べき乗

括弧を使用して、操作の優先順位を強制できます。式内の他の文字は構文エラーです。

空白とコメント

先頭および末尾の空白と空行は無視されます。ループヘッダーの空白は、表示されているとおりで、ループヘッダーと開き中かっこの間にも1つのスペースが必要です。式内の空白はオプションです。

# 単一行のコメントを開始します。

入出力

Acc !! 入力として1行の文字が必要です。各入力文字は順番に取得され、その文字コードはを使用して処理されますN。行の最後の文字を超えて読み取ろうとすると、エラーが発生します。文字は、その文字コードをWriteステートメントに渡すことで出力できます。

通訳

インタプリタ(Python 3で作成)はAcc !!を翻訳しますPythonにコード化して、execそれを実行します。

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

解決

元のAccの没落ループの外部からアクセス可能なループ変数でした。これにより、アキュムレータのコピーを保存でき、ソリューションが非常に簡単になりました。

ここでは、このループホール(申し訳ありません)がなければ、アキュムレータのみが格納されます。ただし、タスクを解決するには、4つの任意の大きな値を格納する必要があります。1解決策:ビットをインターリーブし、結果の組み合わせ番号をアキュムレータに保存します。たとえば、アキュムレータ値6437は、次のデータを格納します(最下位ビットをシングルビットフラグとして使用):

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

アキュムレータを2、mod 2の適切な累乗で除算することにより、任意の数の任意のビットにアクセスできます。これにより、個々のビットを設定または反転することもできます。

マクロレベルでは、アルゴリズムは入力の単項数をループします。値を数値0に読み取り、バブルソートアルゴリズムを渡して、他の3つの数値と比較して適切な位置に配置します。最後に、数値0に残っている値は破棄されます。これは、現在4つのうち最小のものであり、3番目に大きくなることはないためです。ループが終了すると、番号1が3番目に大きいので、他を破棄して出力します。

最も難しい部分(および最初のインカネーションでグローバルループ変数を使用した理由)は、2つの数値を比較して、それらを交換するかどうかを知ることです。2ビットを比較するには、真理値表を数式に変換します。Accの私のブレークスルー!! グローバル変数がなければ、数値のビットを左から右にループする方法がないため、低位ビットから高位ビットに進む比較アルゴリズムを見つけていました。アキュムレータの最下位ビットには、現在検討中の2つの数値を交換するかどうかを示すフラグが格納されます。

私はそのAccを疑います!! チューリング完全ですが、私はそれを証明する手間をかけたいとは思いません。

コメント付きの私のソリューションは次のとおりです。

# 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質問の仕様によると、サポートする必要があるのは最大100万の値のみです。誰もそれを利用して簡単な解決策を利用できなかったことをうれしく思います。


LOL @ビル・猫の絵
spaghetto

7

ピコファック(安全)

PicofuckはSmallfuckに似ています。右側がバインドされておらず、左側がバインドされているバイナリテープで動作します。次のコマンドがあります。

  • > ポインターを右に移動します

  • <ポインターを左に移動します。ポインターがテープから外れると、プログラムは終了します。

  • * ポインタでビットを反転します

  • (ポインタのビットがの場合、0次へジャンプ)

  • )何もしない-Picofuckの括弧ifwhileループではなくブロックです。

  • .ポインタのビットをASCII 0またはとしてstdoutに書き込みます1

  • ,0または1に遭遇するまでstdinから読み取り、これをポインターのビットに格納します。

Picofuckコードラップ-プログラムの最後に到達すると、最初から続行します。

さらに、Picofuckはネストされた括弧を許可しません。Picofuckプログラムに表示される括弧は、(and )で始まり、で始まり、(で終わる必要があり)ます。

通訳

Python 2.7で書かれています

使用法: 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)

解決

次のPython 2.7プログラムは、ここにある私のソリューションを出力します

この仕組みの詳細については、後でこの記事を編集できますが、Picofuckはチューリング完全であることがわかります。

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
ほぼ2年待ちます
電卓

ご参考までに、ご提供いただいたリンクは無効になりました。」
コナーオブライエン

リンクにはダウンロードが必要で、私は面倒です。修正してください。
CalculatorFeline

@CalculatorFeline 13KBで、ダウンロードとして処理する方がはるかに簡単です。
mbomb007

7

PQRS –安全!/提供されるソリューション

基礎

すべての暗黙の命令には、4つのメモリアドレスオペランドがあります。

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

vカルテット単位のメモリサイズはどこにありますか。

PᵢQᵢRᵢSᵢあなたのマシンのネイティブサイズの整数を締結している(例えば16、32または64ビット)、我々は単語として参照されます。

各カルテットについてi[]間接を示す暗黙の操作は次のとおりです。

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

ことに注意してくださいSubleqがのサブセットですPQRS

Subleqは完全であることが証明されているため、PQRSも同様に完全なはずです!

プログラム構成

PQRSは、次のように初期ヘッダーを定義します。

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

H₀ H₁ H₂ H₃は常に最初の命令P₀ Q₀ R₀ S₀です。ロード時に定義H₀するH₃必要があります。

PQRSには初歩的なI / Oがありますが、チャレンジには十分です。

H₄ H₅:プログラムの起動時に、H₅標準入力から最大ASCII文字を読み取り、インデックスH₄以降の単語として保存します。H₄そして、 H₅負荷時に定義する必要があります。読み取り後、H₅読み取られた文字数(および保存された単語)に設定されます。

H₆ H₇:indexから始まるプログラム終了時にH₆H₇ワードを構成するすべてのバイトをASCII文字として標準出力に出力します。H₆そしてH₇プログラムが終了する前に定義する必要があります。NULLバイト'\0'の出力ではスキップされます。

終了

終了はSᵢ、範囲外i < 0またはを設定することによって達成されますi ≥ v

トリック

カルテットPᵢ Qᵢ Rᵢ Sᵢは整列または順次である必要はなく、サブカルテット間隔で分岐が許可されます。

PQRSには間接性があるため、Subleqとは異なり、サブルーチンを実装するのに十分な柔軟性があります。

コードは自己修正することができます!

通訳

インタプリタは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;
}

使用するには、上記をpqrs.cとして保存してからコンパイルします。

gcc -o pqrs pqrs.c

サンプルプログラム

「PQRS-」が前に付いた最大40文字の入力をエコーし​​ます。

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

実行するには、上記をとして保存してecho.pqrsから:

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

テストケースの実行:

$ ./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

すべてのテストケースは非常に高速に実行されます(500ミリ秒未満など)。

挑戦

PQRSは安定していると見なすことができるため、チャレンジは2015-10-31 13:00に開始し、2015-11-08 13:00にUTCで終了します。

幸運を!

解決

この言語は、世界初のプログラム内蔵型電子デジタルマシンである「ベイビー」で使用されている言語と非常によく似ています。そのページには、32ワード(CRT!)未満のメモリで整数の最高因子を見つけるプログラムがあります!

仕様に準拠するソリューションを作成することは、使用しているOSやマシン(わずかに古いハードウェア上のLinux Ubuntu派生製品)と互換性がないことがわかりました。使用可能なメモリより多くのメモリを要求し、コアダンプしていました。高度な仮想メモリ管理を備えたOSまたは少なくとも8 GBのメモリを備えたマシンでは、仕様ごとにソリューションを実行できます。両方のソリューションを提供しました。

PQRSで直接コーディングするのは非常に難しく、機械語を書くのと同じように、場合によってはマイクロコードですらあります。代わりに、一種のアセンブリ言語で記述してから「コンパイル」する方が簡単です。以下は、テストケースを実行するために最適化されたソリューションの注釈付きアセンブリ言語です。

; 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

入力を解析し、単項をバイナリに変換し、次に数値のバブルを降順でソートし、最後にバイナリを単項に変換して3番目に大きい値を出力します。

INC(増分)は負であり、DEC(減分)は正であることに注意してください!使用している、L#またはL#+1as P-またはQ-OPsの場合、起こっていることは、ポインターを更新していることです:インクリメント、デクリメント、スワッピングなど。アセンブラーはラベルをオフセットで置き換えることによりPQRSに手でコンパイルされました。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

上記のコードchallenge-optimized.pqrsは、テストケースを実行するために保存できます。

完全を期すために、仕様ごとのソースを以下に示します。

; 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

そして解決策:

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

上記を実行するには、コメントアウトし#define OPTIMIZEDて追加#define PER_SPECSpqrs.c、再コンパイルする必要があります。

これは大きな挑戦でした。メンタルワークアウトは本当に楽しかったです!昔の6502アセンブラー時代に戻ってくれました...

PQRSを「実際の」プログラミング言語として実装する場合は、おそらく間接アクセスに加えて、直接および二重間接アクセスの追加モードを追加し、分岐の間接アクセスオプションを使用して、相対位置と絶対位置を設定します。


3
投稿する前にソリューションを準備する必要があります。
feersum

1
はい、ソリューションの準備は完了です。この言語は実際に作業するのが非常に難しいため、多少の疑いがあることを理解しています。試写を希望する方は、チャレンジの終了前に公開しないことをお約束します。

6

亜鉛、割れた!によって@Zgarb

GitHubでも入手できます

Dart 1.12とPubが必要です。実行pub getして、唯一の依存関係である解析ライブラリをダウンロードします。

これが30分以上続くことを期待しています!:O

言語

亜鉛は、演算子を再定義することに重点を置いています。言語内のすべての演算子を簡単に再定義できます!

典型的な亜鉛プログラムの構造は次のようになります。

let
<operator overrides>
in <expression>

整数とセットの2つのデータタイプのみがあります。セットリテラルのようなものはなく、空のセットは許可されません。

表現

Zincで有効な式は次のとおりです。

リテラル

亜鉛は次のように、すべての通常の整数リテラルをサポート1して-2

変数

亜鉛には変数があります(ほとんどの言語と同様)。それらを参照するには、名前を使用します。繰り返しますが、ほとんどの言語が好きです!

ただし、SPythのように動作 する特別な変数がありますQ。初めて使用するときは、標準入力から1行を読み取り、一連の数値として解釈します。たとえば、入力行1234231はsetに変わり{1, 2, 3, 4, 3, 2, 1}ます。

重要な注意点!!!場合によっては、演算子オーバーライドの最後のリテラルが正しく解析されないため、括弧で囲む必要があります。

二項演算

次のバイナリ操作がサポートされています。

  • 経由での追加+1+1
  • 減算-1-1
  • 乗算*2*2
  • 課経由/4/2
  • との平等=3=3

さらに、次の単項演算もサポートされています。

  • 長さ##x

優先順位は常に右結合です。括弧を使用してこれをオーバーライドできます。

セットでは等値と長さのみが機能します。整数の長さを取得しようとすると、文字列表現の桁数が取得されます。

理解度を設定する

セットを操作するために、Zincはセットの内包表記を持っています。次のようになります。

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

句は、when句またはsort句のいずれかです。

A 句はのように見えます^<expression>。キャレットに続く式は整数になる必要があります。when句を使用すると、セット内のexpressionゼロ以外の要素のみが使用されます。式内で、変数_はセット内の現在のインデックスに設定されます。これはおおよそ次のPythonと同等です。

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

ソート句のように見える、$<expression>の値で降順セットをソートします<expression>。次のPythonと同じです。

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

理解の例を次に示します。

  • s5に等しいセットの要素のみを取得します。

    {x:s^x=5}
    
  • s要素が二乗した場合、値でセットをソートします。

    {x:s$x*x}
    

オーバーライド

演算子のオーバーライドにより、演算子を再定義できます。次のようになります。

<operator>=<operator>

または:

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

前者の場合、演算子を別の演算子と等しくなるように定義できます。たとえば、+実際に減算するように定義できます:

+=-

これを行うと、演算子を魔法の演算子に再定義できます。2つの魔法の演算子があります。

  • joinセットと整数を受け取り、セットの内容を結合します。たとえば、と結合{1, 2, 3}する4と整数になり14243ます。

  • cutまた、セットと整数を受け取り、整数が出現するたびにセットを分割します。cuton {1, 3, 9, 4, 3, 2}を使用すると... 3が作成され{{1}, {9, 4}, {2}}ますが、単一要素セットはすべてフラット化されるため、結果は実際にになります{1, {9, 4}, 2}

+演算子の意味を再定義する例を次に示しますjoin

+=join

後者の場合、演算子を特定の式に再定義できます。例として、これは値を追加してから1を追加するプラス演算を定義します。

x+y=1+:x+:y

しかし、何+:ですか?コロン:を演算子に追加して、常に組み込みバージョンを使用できます。この例では、組み込みの+via +:を使用して数値を加算し、1を加算します(すべてが右結合であることに注意してください)。

長さ演算子のオーバーライドは、次のようになります。

#x=<expression>

ほぼすべての組み込み操作(等式を除く)は、この長さ演算子を使用してセットの長さを決定することに注意してください。次のように定義した場合:

#x=1

セットで機能するZincのすべての部分は、=与えられたセットの最初の要素でのみ動作します。

複数のオーバーライド

コンマで区切ることにより、複数の演算子をオーバーライドできます。

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

印刷

Zincでは何も直接印刷できません。次の式の結果inが出力されます。セットの値はセパレーターに連結されます。たとえば、これを取る:

let
...
in expr

場合はexprセットで{1, 3, {2, 4}}1324プログラムが終了したら、画面に出力されます。

すべてを一緒に入れて

追加するように見える2+2が、結果が5になる単純なZincプログラムを次に示します。

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

通訳

これが入り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());
}

そしてこれは入りpubspec.yamlます:

name: zinc
dependencies:
  parsers: any

意図したソリューション

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

1
セットは順序付けられており、重複する可能性があるため、基本的にリストであると正しく理解できますか?また、のjoinような混合セットの場合{1,{3,2}}、エラーが発生しますか?現在、Dartをインストールできないため、自分で確認できません。
ズガーブ

@Zgarbはい、この場合、セットは基本的にリストです。ミックスセットに参加する必要があります ...エラーであってもよいが、インタプリタは、実際にATMをクラッシュ
kirbyfan64sos

インタープリターを実行するにはどうすればよいですか?私はちょうどしようとdart bin/zinc.dart test.znc、私は構文エラーを取得:'file:///D:/Development/languages/zinc/bin/zinc.dart': error: line 323 pos 41: unexpected token '?'...var input = stdin.readLineSync() ?? '';
マーティン・エンダー


1
@Zgarb仕様で、等式を除くすべての組み込み操作が長さ演算子を使用すると言ったことを覚えていますか?-2+#:S与えられたときに返すようにオーバーライドSし、2つの末尾のゼロを切り捨てました。それが私がそれが解決されることを望んでいた方法でした。そして^、セットを逆にすることは想定されていません...それはバグでした
...-kirbyfan64sos

5

コンパススープ(cardboard_boxによりクラック

通訳者:C ++

コンパススープは、無限の2次元テープを持つチューリングマシンのようなものです。主な問題は、命令メモリとデータメモリが同じスペースにあり、プログラムの出力がそのスペースの内容全体であることです。

ここに画像の説明を入力してください

使い方

プログラムは、テキストの2次元ブロックです。プログラム空間は、最初の文字が(0,0)であるソースコード全体から始まります。プログラム空間の残りは無限であり、ヌル文字(ASCII 0)で初期化されます。

プログラム空間内を移動できる2つのポインターがあります。

  • 実行ポインターには、場所と方向(北、南、東、または西)があります。ティックごとに、実行ポインタの下の命令が実行され、実行ポインタが現在の方向に移動します。実行ポインタは、!文字または存在しない場合は(0,0)でます。
  • データポインターには場所のみがあります。これは、命令で移動しxXy、とY@文字の位置から、または存在しない場合は(0,0)から開始します。

入力

stdinの内容は、>文字の位置から、または存在しない場合は(0,0)からプログラム空間に出力されます。

出力

実行ポインターが回復不能な範囲を超えて実行されると、プログラムは終了します。出力は、その時点のプログラムスペースのコンテンツ全体です。それはstdoutと 'result.txt'に送信されます。

説明書

  • n -実行ポインターを北にリダイレクト(負のy)
  • e -実行ポインターを東にリダイレクト(正のx)
  • s -実行ポインターを南にリダイレクト(正のy)
  • w -実行ポインターWestをリダイレクトします(負のx)
  • y -データポインターを北に移動(負のy)
  • X -データポインターを東に移動(正のx)
  • Y -データポインターを南に移動(正のy)
  • x -データポインターを西に移動(負のx)
  • p-実行ポインターが遭遇した次の文字をデータポインターに書き込みます。その文字は命令として実行されません。
  • j-実行ポインターが検出した次の文字を、データポインターの下の文字と照合します。その文字は命令として実行されません。それらが同じ場合、実行ポインタは次の文字にジャンプします。
  • c -データポインターにヌル文字を書き込みます。
  • * -ブレークポイント-インタープリターを中断させるだけです。

他のすべての文字は、実行ポインターによって無視されます。

通訳

インタープリターは、ソースファイルを引数として受け取り、stdinに入力します。ステッパブルデバッガがあり、コード内のブレークポイント命令(*)で呼び出すことができます。壊れている場合、実行ポインターはASCII 178(より暗い影付きブロック)として表示され、データポインターはASCII 177(より明るい影付きブロック)として表示されます。

#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();
}

こんにちは世界

Hello, World!

ネコ

>

パリティ:ゼロ(「0」)で終了する文字列を受け入れます。出力yes出力の最初の行に入力における1の数が奇数の場合、そうでなければ出力します|

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

ヒント

適切なテキストエディタを使用し、「Insert」キーの機能を慎重に使用し、「Alt-Drag」を使用して複数行のテキストを一度に追加または削除する必要があります。

解決

これが私の解決策です。ソースコードを削除する必要があったため、cardboard_boxほど良くありません。また、すべてのコードを削除し、答えだけを残す方法を見つけることを望んでいましたが、できませんでした。

私のアプローチは、1sの異なるシーケンスを別の行に分割し、1sをすべて別の行にヒットするまで「フォール」させ1、最後に入力後の3行目を除くすべてを消去することでした。

  • 右下の大きなブロックはs を#A#読み取り1、a 0が読み取られるまで分割の最後の行にコピーします。
  • #B#0をチェックし、#D#そこに北へ行きます。そうでない場合、最後の#C#行の|後に新しい分割行を開始し、に戻り#A#ます。
  • 以上のブロック#F#は重力コードです。1最初の行の最後まで歩き、1またはに達するまで上に移動し-ます。それができない場合は、行の+前に置くことで、行を終了としてマークします。
  • #G#不要な分割をすべて消去し、#H#stdinと括弧内のすべてのコードを消去しています。

コード:

 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          ))
(-@
 |>


くそー、とても近い!今夜帰宅したら、解決策を共有します。
BMac

パリティプログラムが機能しません。最初にデバッグ命令があるはずですか?私がそれをステップスルーすると、無限ループで立ち往生します。
feersum

最初はそこにあるcべきではない余分なものがあったようです。それを私が直した。また、私のソリューションを問題に追加しました。
BMac

4

Acc!ppperryによるCracc'd

この言語には、1つのループ構造、基本的な整数演算、文字I / O、およびアキュムレータ(したがって名前)があります。たった1つのアキュムレーター。したがって、名前。

声明

コマンドは行ごとに解析されます。コマンドには3つのタイプがあります。

  1. Count <var> while <cond>

C-styleと同等の非ゼロである<var>限り、0からカウントアップします。ループカウンターは、任意の1つの小文字にすることができます。条件には任意の式を使用できますが、必ずしもループ変数が関係するわけではありません。条件の値が0になると、ループが停止します。<cond>for(<var>=0; <cond>; <var>++)

ループにはK&Rスタイルの中括弧(特にStroustrupバリアント)が必要です。

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

指定されたASCII / Unicode値を持つ単一の文字をstdoutに出力します。文字コードには任意の式を使用できます。

  1. 表現

独立した式はすべて評価され、アキュムレータに割り当てられます(アキュムレータとしてアクセス可能_)。したがって、たとえば、3アキュムレータを3に設定するステートメントです。_ + 1アキュムレータをインクリメントします。_ * N文字を読み取り、アキュムレータにその文字コードを乗算します。

注:アキュムレータは、直接割り当てることができる唯一の変数です。変数をループしN、計算で使用できますが、変更はできません。

アキュムレータは最初は0です。

表現

式には、整数リテラル、アキュムレータ用のループ変数(a-z)、_およびN文字を読み取り、使用されるたびにその文字コードに評価する特別な値を含めることができます。注:これは、各キャラクターの読み取りに1ショットしか使用できないことを意味します。次にを使用するNときは、次のものを読んでいます。

演算子は次のとおりです。

  • +、追加
  • -、減算; 単項否定
  • *、乗算
  • /、整数除算
  • %、モジュロ
  • ^、べき乗

括弧を使用して、操作の優先順位を強制できます。式内の他の文字は構文エラーです。

空白とコメント

先頭および末尾の空白と空行は無視されます。ループヘッダーの空白は、表示されているとおりで、ループヘッダーと開き中かっこの間にも1つのスペースが必要です。式内の空白はオプションです。

# 単一行のコメントを開始します。

入出力

Acc!入力として1行の文字が必要です。各入力文字は順番に取得され、その文字コードはを使用して処理されますN。行の最後の文字を超えて読み取ろうとすると、エラーが発生します。文字は、その文字コードをWriteステートメントに渡すことで出力できます。

通訳

インタープリター(Python 3で作成)はAcc!を翻訳します。Pythonにコード化して、execそれを実行します。

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(安全)

(以前はSimp-plexとして知られていました。)

この言語はシンプルです。主なフロー制御はgotoです。これは最も自然で便利な制御形式です。

言語仕様

データはテープとアキュムレータに保存されます。署名なしの統合で完全に機能します。各文字はコマンドです。以下はすべてのコマンドです。

  • 文字:a- zはgotoステートメントで、それぞれA- Zに移動します。
  • ::アキュムレータをASCII値に設定し、入力からcharにします。
  • ~:アキュムレータのASCII値の文字を出力します。
  • &:アキュムレータから1つ以上の場合は1を引き、そうでない場合は1を加えます。
  • |:アキュムレーターに1を追加します。
  • <:データポインターを0に設定します。
  • +:データポインターのデータセルをインクリメントします。ポインターを+1移動します。
  • -:データポインターのデータセルが正の場合、データセルから1を引きます。ポインターを+1移動します。
  • [...]:コードをn回実行します。nは、テープ上のデータポインターの番号です(ネストできません)。
  • /:アキュムレーターが0の場合、次の命令をスキップします。

通訳者(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;
}

楽しんで!

解決

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


2
あなたのC ++コーディングが私を殺している!のcalloc代わりに new char、Cスタイルのwhileループを作成し、Cスタイルのメモリ管理を使用し、コードを変更するたびにC ++ファイルを再コンパイルし、aの代わりに20のif を使用した理由はありswitchますか?私は文句を言っていませんが、私の目は今出血しています...:O
kirbyfan64sos

3
インタプリタに肉を付けるようにスペックを修正しました。
MegaTom

@ kirbyfan64sosコードが悪いです。私はこれをすぐにまとめましたが、思ったほどうまくできなかったかもしれません。メイン関数は、入力としてコードを取るように変更できます。実際、私は今それをやると思います
...-MegaTom

1
質問はインタプリタがプログラムのコマンドラインでファイル名を取るべきだと言っています
デニス

ファイルを文字列に読み込む簡単な方法をいくつか 示します。次にstr.c_str()を取得するために呼び出しますchar*
feersum

0

ほとんどすべての難解な言語は読めないように見えるため(Jellyを見て)、これは悪い考えでした。
しかし、ここに行きます:

Pylongolf2 beta6

スタックへのプッシュ

スタックへのプッシュは、他の言語とは異なる動作をします。
コードは78プッシュ78スタックに、しかしPylongolfにそれがプッシュします78
Pylongolf2では、これはで切り替え可能Üです。

コマンド

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

文字列の連結と文字列からの正規表現パターンの削除

+記号は文字列を連結します。
-記号を使用して、文字列から正規表現パターンに続く文字を削除できます。

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

このコードは入力を受け取り、すべてのパターン一致を削除することにより、アルファベット以外の文字をすべて削除します[^a-zA-Z]
選択された項目は正規表現でなければならず、前の項目は編集する文字列でなければなりません。

Ifステートメント

ifステートメントを実行=するには、選択した項目とその後の項目を比較するためにa を入れます。
これにより、a trueまたはa falseがその場所に配置されます。
コマンド?はこのブール値をチェックします。
その場合、true何も行わず、インタープリターは続行します。
その場合false、インタープリターは最も近い¿文字にスキップします。

Githubページから取得。

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

}

これは使いにくいと思われますか?:/
CalculatorFeline

0

Rainbow(注:通訳は近日公開予定)

このチャレンジの期限が切れたことは知っています。

レインボーは...多くのものの混合物です。

Rainbowは、2つのスタック(Brain-Flakのような)と8つの方向(N NE E SE S SW W NW)を持つ2Dスタックベースの言語です。8つのコマンドがあります。

  • 1+*" 1+に彼らは正確に何をすべきか。
  • ! アクティブなスタックを切り替えます。
  • > IPを時計回りに回転させます。
  • , 文字を入力してプッシュします。
  • . 文字をポップして出力します。

ただし、ソースコード内の文字はすぐには実行されません。代わりに、[The Character in the source code]^[Top Of Stack]に、Collat​​z Conjectureモノにフィードされ、1に到達するまでのステップ数がASCIIテーブルによって文字に変換されます。この文字が実行されます。

  • 1に達するまでに127を超えるステップが必要な場合、合計ステップカウントは127で除算され、リマインダーを受け取り、商にリマインダーを追加します。

プログラムの開始時に、ソースコード(最後の文字を除く)がスタックにプッシュされます。

IPがソースコードの端に達すると、IPは終了します。

黙示録

nとmは2つのレジスタです。ときに>命令が実行され、mがインクリメントされます。黙示録は、mがnを超えた場合にのみトリガーされます。黙示録が起こるとき、それ:

  • 時計回りではなく反時計回りに回します。
  • mは0になります。
  • nがスタックの先頭になります。そして、スタックがポップされます。

mは最初はゼロで、nは最初はソースコードの最後の文字です。

暗号化

実行後、ソースコードは暗号化されます。1番目の文字のASCIIは1ずつ増加し、2番目は1ずつ減少し、3番目は2ずつ増加し、4番目は2ずつ減少します。


1
これを有効な回答にするためには通訳が必要です...
Conor O'Brien

@ ConorO'Brienこのチャレンジはすでに期限が切れているので、これはただの楽しみです。ただし、通訳を提供します。
9:45の

@HighlyRadioactive ...あなたはほぼ一ヶ月前に言った。
pppery
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.