创建仅看起来无法使用的编程语言


85

强盗的挑战线程在这里

警察的挑战:设计一种似乎无法用于编程的编程语言,但可以通过某种非显而易见的机制来接受计算(或至少完成任务)。

您应该设计一种简单的编程语言,该语言从输入文件中读取代码,然后执行某些操作。您必须准备一个解决方案程序,当在解释器中运行该解决方案程序时,该程序可在输入中找到第3大数字。您需要使劫匪尽可能地难以找到解决方案。请注意,强盗可以发布完成任务的任何解决方案,而不仅仅是您想到的解决方案。

这是一次人气竞赛。警察的目标是在发布口译员后的8天幸存下来而不会被破解,从而获得尽可能多的选票。为此,以下做法应有所帮助:

  • 准确解释您语言的语义
  • 编写可读代码

强烈建议您不要采用以下策略:

  • 使用加密,哈希或其他加密方法。如果您看到使用RSA加密的语言,或者除非SHA-3哈希值等于0x1936206392306,否则拒绝执行程序,请不要犹豫。

强盗的挑战:编写一个程序,当在警察的解释器中运行时,找到输入中的第三大整数。

这是相对简单的。为了破解警察的答案,您必须创建一个程序,该程序在其解释器中运行时才能完成任务。当您破解答案时,请在链接到您的帖子的警察的答案上发表一条评论,说“破解”。破解最多警察的人将赢得强盗的支持。

I / O规则

  • 口译员应在程序的命令行中使用文件名,并在运行程序时使用标准输入和输出。
  • 输入将以一元形式给出,且仅包含字符01(ASCII中的48和49)。数字N编码为N, 1s后跟a 00文件结束前还有一个附加内容。示例:对于序列(3,3,1,14),输入为11101110101111111111111100
  • 确保输入至少3个数字。所有数字均为正整数。
  • 输出将由1程序暂停前打印的s 数来判断。其他字符将被忽略。

在以下示例中,第一行是十进制格式的输入;第二行是十进制格式的输入。第二个是实际程序输入;第三是示例输出。

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工作6,并且任何数量小于10的整数的6(不一定在一个小时内),条件是总输入长度小于10 9
  • 为了安全起见,警察必须在8天后将解决方案程序编辑到答案中。

计分

得分最高且得分为正的安全警察赢得了这个问题。


您没有明确说明这一点,但是我认为警察实际上必须在他们的回答中写和张贴口译员是对的吗?
蓝色

@muddyfish是的,翻译应该是警察答复的内容。
feersum 2015年

1
@ kirbyfan64sos 输出将根据程序停止之前打印的1数来判断。其他字符将被忽略。
mbomb007'2015-10-27


20
我很讨厌这个挑战。我创建了一个编程语言,然后花了几个小时的编程任务,看看语言甚至可以只工作,找出它实际上不可用的。
桑奇斯

Answers:


24

更换(安全)

ShapeScript

ShapeScript是一种自然发生的编程语言。形状移位器(或换生灵,因为它们更愿意被调用)可以转换成的一组指令,使他们能够处理数据。

ShapeScript是具有相对简单语法的基于堆栈的语言。毫不奇怪,它的大多数内置函数都可以处理改变字符串的形状。逐字符解释如下:

  • '然后"开始一个字符串文字。

    在源代码中找到匹配的引号之前,这些匹配的引号之间的所有字符都收集在一个字符串中,然后将其压入堆栈。

  • 09推整数09在堆栈中。请注意,10推入两个整数。

  • ! 从堆栈中弹出一个字符串,并尝试将其评估为ShapeScript。

  • ? 从堆栈中弹出一个整数,并在该索引处推送堆栈项的副本。

    索引0对应于最顶层的堆栈项(LIFO),索引-1对应于最底层的堆栈项。

  • _ 从堆栈中弹出一个可迭代对象,并推入其长度。

  • @ 从堆栈中弹出两个项目,并以相反的顺序推送它们。

  • $从堆栈中弹出两个字符串,并在出现最高的字符串时将最底部的一个字符串拆分。返回结果列表。

  • &从堆栈中弹出一个字符串(最顶部)和一个可迭代对象,并使用该字符串作为分隔符将可迭代对象联接起来。返回结果字符串。

  • 如果在我们的星球上使用ShapeScript,由于python是Changelings在地球上的最亲密的亲戚,所有其他字符c从堆栈中弹出xy(最顶部)两项,并尝试评估Python代码x c y

    例如,23+将评估字符序列2+3,而"one"3*评估'one'*3字符序列,1''A并且评估字符序列1A''

    在最后一种情况下,由于结果不是有效的Python,因此,更改人会抱怨他当前的形状是无用的(语法错误),因为它不是有效的ShapeScript。

在执行代码之前,解释器会将整个用户输入以字符串形式放置在堆栈上。执行源代码后,解释器将在堆栈上打印所有项目。如果两者之间的任何部分发生故障,则变速装置将抱怨其当前形状不足(运行时错误)。

变形

在其自然状态下,“变形动物”不具有ShapeScript的形状。但是,其中一些可以转换为一个潜在的源代码(不一定有用,甚至在语法上有效)。

所有符合条件的变更都具有以下自然形式:

  • 所有行必须具有相同数量的字符。

  • 所有行必须由可打印的ASCII字符组成,后跟单个换行符。

  • 行数必须与每行可打印字符数匹配。

例如,字节序列ab\ncd\n是合格的变更。

为了转换为ShapeScript,Changeling进行了以下转换:

  • 最初,没有源代码。

  • 对于每一行,都会发生以下情况:

    • 变速箱的累加器设置为零。

    • 对于该行的每个字符c(包括尾随的换行符),c的代码点与累加器除以2,然后进行XOR运算,并且将与所得代码点相对应的Unicode字符附加到源代码中。

      之后,将c的代码点与空格(32)的代码点之差加到累加器。

如果以上任何部分失败,则更改者将抱怨其当前形状不愉快。

在处理完所有行之后,将Changeling转换为(希望有效)ShapeScript,并执行所生成的代码。

解决方案(ShapeScript)

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

实际上,ShapeScript非常有用。它甚至可以执行素数测试(证明),因此满足我们对编程语言的定义。

在GitHub上重新发布了ShapeScript,但语法略有修改,并且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将立即对任何尚不了解的字符进行错误处理,但是我们可以将任意字符串作为填充符并稍后弹出。这使我们在每行上只放置少量代码(在开始时,累加器很小),然后是一个开行"。如果我们从开始下一行"#,则在不影响实际代码的情况下关闭并弹出字符串。

此外,我们必须克服三个小障碍:

  • ShapeScript代码中的长字符串是单个标记,因此我们无法将其放在一行上。

    我们将推动这个字符串中块('@''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 底部的数字表示更改代码中的最低和最高代码点,必须在32到126之间。


1
-1用于异或/变换。在我看来,转换为ShapeScript转换非常像加密。
MegaTom

10
@MegaTom您可以根据自己的意愿进行投票,但是由于加密需要一个密钥,因此问题就不存在了,因为只有警察才能知道这个常数,这使强盗处于明显的劣势。转换是无密钥转换。
丹尼斯

1
ShapeScript,67个字节:0"#002?'+'&'0'$'0?2?-@2?>*+00'&!++'1'*'0'+@1?$0?''&@_2-2?*@+@"3*!@#。不过,我已经放弃寻找它的变速箱了。即使有大多无用语句穿插,我一直没能在获得超过20个字节。
普里莫

2
@MegaTom我实际上对给定的解决方案感到失望。我期待的东西比92.9%的无用代码要聪明得多。
2015年

2
@primo进行了一些修改,但我发现此Changeling也可以与Python 2一起使用。我不知道如何聪明我的回答是,但我发布了漏洞,一个警察的计划,到不得不找到破解它似乎已经奏效。
丹尼斯

30

随机播放(用C ++编写),破解!马丁

编辑 马丁破解了它。要查看他的解决方案,请单击链接。我的解决方案也已添加。

编辑 固定print-d命令,使其能够同时处理寄存器和堆栈。由于这是解决方案中不允许的调试命令,因此这不会影响使用先前版本解释程序的任何人

我还是这个新手,所以如果我的答案或口译员有问题,请告诉我。如果不清楚,请要求澄清。

我不认为这会非常困难,但希望它将提供某种挑战。使随机播放相当不可用的原因是,只有在适当位置放置才可以打印。

->基础:

有24个堆栈,我们称它们为stack1, ... stack24。这些堆栈位于列表中。在任何程序的开始,这些堆栈都被压入零,并且它们从正确的位置开始,即堆栈i在列表中的第i个位置(请注意,我们将从1开始索引,这与C ++不同)。在程序执行过程中,列表中堆栈的顺序将发生变化。这很重要,原因将在我讨论命令时加以说明。

有5个寄存器可供使用。它们被命名为AlbertoGertrudeHansLeopoldShabbySam。在程序开始时,每个参数都设置为零。

因此,在任何程序开始时,都有24个堆栈,每个堆栈的编号与其在堆栈列表中的索引相匹配。每个堆栈的顶部都恰好有一个零。五个寄存器中的每一个都初始化为零。

->命令和语法

在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 Hans),则可以movfs Hans Gertrude用来将Gertrude设置为等于堆栈4的顶部mov

  • inc register 将寄存器值增加1。
  • dec register 将寄存器值减1。
  • compg value value register这代表“比较大”。它将寄存器设置为等于两个值中的较大者。value可以是整数,寄存器或后跟“ s”的堆栈索引,如上所述。
  • compl value value register “比较较小”与上述相同,只是取较小的值。
  • gte value1 value2 register检查value1 >= value2然后是否将布尔值(作为1或0)放入register
  • POP!! indexindex堆栈列表中被索引的堆栈顶部弹出。
  • jmp label无条件跳转到标签label。这是谈论标签的好时机。标签是单词,后跟“:”。解释器会预先准备标签,因此您可以向前和向后跳转。
  • jz value label跳转到labelif value为零。
  • jnz value label跳转到labelif value非零。

    标签和跳跃的示例:

    this_is_my_label:
         jmp this_is_my_label
    
    mov 10 Hans
    jnz Hans infinite_loop
    
    infinite_loop:
         jmp infinite_loop
    
  • "shuffle" permutation这是shuffle命令。这使您可以排列堆栈列表。有迹象表明,可以作为参数,三个有效的排列lfb

  • print register此检查,如果所有的栈都为它们的初始位置,即栈是在索引在堆栈列表。如果是这种情况,它将register以一元形式输出值。否则,它会显示令人讨厌的错误。如您所见,要输出任何内容,堆栈必须全部放在正确的位置。
  • done!这告诉程序退出没有错误。如果程序结束时没有done!,它将在控制台上显示每个堆栈顶部的编号,然后是堆栈编号。堆栈的打印顺序是它们在堆栈列表中出现的顺序。如果堆栈为空,则将其省略。此行为是出于调试目的,不能在解决方案中使用。
  • print-d value这将打印给定的堆栈,寄存器或整数的值(访问堆栈iis作为参数传递,如上所述)。这是调试工具,不是语言的一部分,因此在解决方案中是不允许的。

->这是解释器代码

所有解析都在main函数中进行。在这里可以找到针对特定命令的解析。

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

->排列 排列以以下方式排列堆栈列表的元素:

哪里意味着

(这些也出现在解释器代码中。如果存在差异,则解释器是正确的。)

->简单的例子

这两个简单程序将数字打印为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 Rubik魔方)。24个堆栈就像多维数据集上的24个独立正方形。排列是允许的基本移动:上,下,右,左,前,后。

此处的主要难题是,当推送值时,仅使用三个移动:上,下和右。但是,“改组”堆栈时,您无权访问这些移动。您只有其他三个动作。

事实证明,这三组动作实际上都跨越了整个组(即发电机),因此问题可以解决。这意味着您实际上只能使用3个举动来求解任何2x2 Rubik立方体。

剩下要做的就是弄清楚如何使用其他三个撤消上,下和右移动。为此,我使用了一个名为GAP的计算机代数系统。

取消排列后,找到第三大数字相当简单。

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
破解 :)这门语言给我留下了深刻的印象!
马丁·恩德

哇,这比我预期的要快。谢谢,我很高兴弄明白写起来很有趣。
利亚姆

我很好奇,如果我将排列的名称更改为与Rubik的多维数据集不太明显的名称,会难吗?
利亚姆

他们绝对是一个线索,但我认为它不会采取的是如果他们有不同的名称更长的时间。
马丁·恩德

呵呵,看起来GAP在反转三个输入排列方面并不是特别聪明。;)
Martin Ender 2015年

22

布莱恩和查克由cardboard_box破解

我已经被一种编程语言的想法吸引了一段时间,其中两个程序相互交互(可能是受ROCB的启发)。这一挑战是最终设法解决该概念的一个很好的动机,同时试图使该语言尽可能少。设计目标是使该语言成为图灵完备的,而其各个部分分别不是图灵完备的。此外,即使不使用源代码操作,即使将它们一起使用也不应该是图灵完备的。我认为我已经成功完成了,但是我还没有正式证明这些东西。所以事不宜迟,我向您介绍...

主角

Brian和Chuck是两个Brainfuck程序。从Brian开始,在给定的时间仅执行其中一个。问题在于Brian的存储带也是Chuck的源代码。Chuck的存储磁带也是Brian的源代码。此外,Brian的磁带头也是Chuck的指令指针,反之亦然。磁带是半无限的(即,右侧是无限的),并且可以容纳带符号的任意精度整数,这些整数被初始化为零(除非源代码另有规定)。

由于源代码也是存储带,因此命令在技术上由整数值定义,但它们对应于合理的字符。存在以下命令:

  • ,44):从STDIN读取一个字节到当前存储单元。只有Brian可以做到。对于Chuck,此命令不可操作。
  • .46):将当前存储单元以256为模,以字节形式写入STDOUT。只有Chuck可以做到这一点。对于Brian来说,此命令是不可操作的。
  • +43):增加当前存储单元。
  • -45):减少当前存储单元。
  • ?63):如果当前存储单元为零,则为无操作。否则,将控制权移交给其他程序。程序上使用的磁带头?将保留在上?。另一个程序的磁带头将在执行第一个命令之前将一个单元向右移动(因此用作测试的单元本身不会执行)。
  • <60):将磁带头向左移动一个单元格。如果磁带头已经在磁带的左端,则为空。
  • >62):将磁带头向右移动一个单元格。
  • {123):反复向左移动磁带头,直到当前单元格为零或到达磁带的左端。
  • }125):反复向右移动磁带头,直到当前单元格为零。

当活动程序的指令指针到达右边没有更多指令的位置时,程序终止。

源代码

源文件的处理如下:

  • 如果文件包含字符串```,则在该字符串首次出现时会将文件分为两部分。除去所有前导和尾随空格,第一部分用作Brian的源代码,第二部分用作Chuck的源代码。
  • 如果文件不包含该字符串,则文件的第一行将用作Brian的源,而Chuck的第二部分将用作Chuck的源(除定界的换行符外,不会删除任何空格)。
  • _两个程序中所有出现的都替换为NULL字节。
  • 用对应于所得字符串的字符代码初始化两个存储带。

例如,以下源文件

  abc
```
0_1
23

将产生以下初始磁带:

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

口译员

解释器是用Ruby编写的。这需要必须两个命令行标志通过任何溶液中使用(因为它们不是实际的语言规范的一部分):

  • -d:有了这个标志,Brian和Chuck可以再理解两个命令。!将打印两个存储磁带的字符串表示形式,活动程序首先列出(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:
{<{<?

此代码是可执行的,是的,因为所有的注释使用无操作,并通过跳过{}

基本思想是:

  1. 在列表中(在Chuck磁带的末尾)添加一个新的零元素,并将列表长度增加1。
  2. 在读取1s时,增加该元素。
  3. 当我们阅读时0,请进行一些清理。如果结果整数大于零,请返回1。

    现在,我们在Chuck磁带的末尾有了一个输入数字的列表,我们知道了列表的长度。

  4. 从列表的长度中减去2(因此以下步骤将忽略两个最大的元素),称为this N

  5. N > 0,则递增运行总计,然后递减所有列表元素。每当列表元素达到零时,就递减N

    最后,运行总计将在输入中包含第三大数字M

  6. M副本复制.到Chuck磁带的末尾。

  7. 在Chuck上,1在Brian的磁带上搜索a ,然后执行.最后生成的磁带。

完成此操作后,我意识到我可以在某些地方进行很多简化。例如,不必跟踪该计数器并将其写入.Chuck的磁带,而可以在1每次减少所有列表元素之前立即打印一次。但是,对此代码进行更改是很痛苦的,因为它会在整个地方传播其他更改,因此,我不必理会更改。

有趣的一点是如何跟踪列表以及如何遍历列表。您不能只将数字背对背存储在Chuck的磁带上,因为如果您要检查列表元素之一的条件,则冒着执行列表中可能包含有效代码的其余部分的风险。您也不知道列表将有多长,因此您不能只在Chuck的代码前保留一些空间。

下一个问题是,N在处理列表时,我们需要使列表递减,并且我们需要回到以前的位置。但是{}只是跳过整个列表。

因此,我们需要在Chuck上动态编写一些代码。实际上,每个列表元素i都具有以下形式:

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

1是一个标记,我们可以将其设置为零以指示我们在哪里停止处理列表。它的目的是捕获{}将仅跳过代码和i。我们还使用此值来检查在处理过程中我们是否在列表的末尾,因此当我们不在列表的末尾时,这将是1有条件的,并将条件?控件切换到Chuck。Code A用于处理这种情况并相应地在Brian上移动IP。

现在,当我们递减时,i我们需要检查是否i已经为零。虽然不是,但?仍将再次切换控制权,因此Code B在那里进行处理。



@cardboard_box不错!
mbomb007

15

HPR,用Python 3编写(由TheNumberOne破解

HPR(名称没有任何含义)是一种用于处理整数列表的语言。它被设计为简约极其有限,并且没有“人为”限制。HPR中的编程很痛苦,不是因为您必须解决一个难题以防止解释器对您大喊大叫,而是因为很难使程序做任何有用的事情。我不知道HPR是否具有比计算列表中的第三大元素更有趣的功能。

语言规格

HPR程序在一个环境中运行,该环境是无序的非负整数集和非负整数列表。最初,环境仅包含输入列表(解释器为您解析)。有三个命令和两个“控制流”运算符可修改环境:

  • *删除环境中每个非空列表的第一个元素,并将其放置在环境中。空列表不受影响。例如,它会转变

    {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]}
    
  • $将环境中的每个列表向左旋转一个步骤。例如,它会转变

    {4,1,[0,2],[3,4,1]} -> {4,1,[2,0],[4,1,3]}
    
  • !(A)(B),其中AB是程序,基本上是一个while循环。它执行“操作”,A直到“测试” B将导致一个空环境。这可能会产生无限循环。
  • #(A)(B),在那里AB一些程序,应用AB当前的环境和需要的结果的对称差。

命令从左到右执行。最后,环境的大小以一元打印。

口译员

该解释器具有debug命令?,该命令将打印环境而不进行修改。它不应出现在任务的任何解决方案中。除此以外的所有字符都*-$!#()?将被忽略,因此您可以将注释直接写入代码中。最后,解释器将惯用语识别!(A)(#(A)())为“执行A直到结果不再更改”,并对其进行优化以提高速度(我需要它来使我的解决方案在最后一个测试用例下不到一个小时即可完成)。

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字节程序相比非常短。这很可能是由于TheNumberOne为在HPR中编程而开发的复杂而强大的宏系统。我们两个程序的主要思想是相似的:

  1. 了解如何产生列表的最大元素。
  2. 要删除最大元素,请旋转列表,直到第一个元素等于最大数量,然后将其弹出。
  3. 删除最大值两次,然后打印新的最大值元素。

但是,据我所知,确切的实现细节完全不同。这是我的解决方案:

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

这是产生它的注释后的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添加了我的解决方案。
Zgarb 2015年

12

TKDYNS(要杀死龙,你需要一把剑)-MartinBüttner 破解

编辑:我已经在主要帖子下面添加了我的解决方案和说明。

背景

用这种语言,您可以控制一个英勇的战士,他被杀死了一条可怕的龙。龙生活在一个充满危险和危险的地下迷宫中,直到现在,还没有人能够将其绘制出来并生存下来。这意味着您必须在漆黑的黑暗中导航至巨龙,仅凭直觉和勇敢来引导您。

...

好吧,不完全是。您已经带来了几乎无限量的一次性奴才供应,他们乐于助您发现安全的路线。不幸的是,它们的厚度都只有两个短木板,只能按照您的要求做。由您自己提出一种聪明的方法来确保您的仆从发现正确的道路。

更多细节

龙的巢穴采用10x10网格的形式。在网格中某些相邻点之间,有一条狭窄的人行道。在其他人之间,存在着深深的鸿沟和必死的生命。4x4网格的示例布局可能如下:

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

您知道,总有一种方法可以从一个点到达任何其他点,但是向您揭示的不多。

要成功击败巨龙,您首先需要收集一些物品,您可以将它们组合在一起,以制造出神奇的屠龙刃。方便地,此武器的所有零件散落在龙的巢穴周围。您只需要收集它们。

不同的是,武器的每一部分都被诱杀了。每次收集时,人行道的布局都会改变。以前安全的路径现在可能会导致某些死亡,反之亦然。

指令

只有5个有效命令。

  • < -向左走一步

  • > -向右走一步

  • ^ -向上走一步

  • v -往下走

  • c-收集恰好在您当前位置的任何物品。如果存在物品,则巢穴的布局会发生变化。使用上面按行编号的位置,将您的位置取模10。将10个布局硬编码到解释器中,然后布局更改为对应的布局。例如,如果您位于位置13,则布局将更改为layouts[3]

出现在交错器中的布局已通过以下方式编码为整数:

  • 空布局被编码为零。

  • 对于布局中的每个边,让其x为两个所连接位置中的较小者。

    • 如果台阶是水平的,则添加2^(2*x)到编码中(这是幂的,不是XOR)

    • 如果台阶是垂直的,则添加2^(2*x + 1)到编码中。

执行流程

解释器以源文件的名称作为命令行参数运行。

运行解释器时,它将提示用户提供任务。该输入应采用问题中所述的形式,并确定武器组件在巢穴中的位置。具体来说,每个输入整数取100为模,并放置在巢穴中的相应位置。

每个源程序由几行组成,每行由上述5个有效字符的某些序列组成。这些线代表您的奴才。您,战士,跟踪已知安全的一系列动作。最初,您对巢穴一无所知,因此此序列是空的。从位置0开始依次执行每个小兵,执行以下操作:

  • 指示该奴才执行所有已知的安全动作,然后执行其自己的代码行中的动作。

    • 如果小兵在任何时候死亡,系统会通知您,巢穴会重置为其初始配置。所有物品均被替换,人行道返回其初始位置。

    • 相反,如果小兵可以幸存,那么无论如何您都会将其蒸发-毕竟这只是小兵。和以前一样,这会触发将巢穴重置为其初始状态,但是这次,您将小仆的代码行中的动作附加到已知的安全动作序列中。

一旦所有小兵都精疲力尽,您即战士将再次执行从位置0开始的所有已知安全动作。有两种可能的结果:

  • 您收集了武器的所有零件-在这种情况下,您成功杀死了龙,并输出了令人兴奋的胜利消息。除其他字符外,该胜利消息将包含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倍。另一方面,我生成代码的脚本比马丁的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行都包含试图将一个小兵从一个位置移动到另一个0位置1,然后收集一个项目的指令-每个可能的布局一组。接下来的10行都包含试图将一个小兵从一个位置移动到另一个1位置2,然后收集物品的指令。依此类推...这些路径是使用path脚本中的函数计算的-它仅进行简单的深度优先搜索。

如果我们可以确保每10个小组中只有一个小兵成功,那么我们将解决问题。

方法的问题

并非总是10个一组中的一个小兵会成功-例如,设计成从一个位置0移到另一个小兵1可能会偶然地从一个位置移到另一个1位置2(由于布局相似),并且错误的计算将传播通过,从而可能导致失败。

如何修复

我使用的修复程序如下:

对于试图从位置n移到的每个爪牙n+1,首先使他从位置n移到位置0(左上角)并再次返回,然后从位置n移到位置99(右下角)并再次返回。这些说明只能从正确的位置安全地执行n-任何其他起始位置,小仆都将离开边缘。

因此,这些额外的说明可以防止小兵意外地进入他们不想要的地方,并且这可以确保每10个小组中只有一个小兵成功。请注意,不一定是您期望的奴才-即使我们确实在布局7中,也可能是认为自己在布局0中的奴才成功了-但无论如何,我们都有现在位置改变意味着该小组中的所有其他仆从必将死亡。这些额外的步骤由assert_at函数计算,并且该safe_path函数返回一个路径,该路径是这些额外的步骤与普通路径的串联。


1
破解 这很有趣,但是我认为它提出了“您的编程语言只需要解决一个任务”规则的问题,因为破解难题与实际解决该编程任务无关。;)
Martin Ender 2015年

我之所以创建这个答案,主要是因为我注意到这个问题的存在-似乎没有任何东西可以阻止某人使用真正困难的计算问题来代替它。对“禁止加密”的禁令似乎是狭窄的……
山姆·卡普曼·莱恩斯

真正。我想这就是为什么这是一次人气竞赛。如果问题更多是计算性质的问题,而不是包含在这么好的故事中,而不是用适当的(可能是图灵完整的)语言来作为答案的投票,实际上这真的很难使用。
马丁·恩德

添加了我感兴趣的解决方案。同样出于兴趣,我最初设计拼图时考虑了1000x1000的网格,但是出于没有布局编码为〜600000位数字的兴趣,我选择了较小的尺寸。
Sam Cappleman-Lynes

8

火种,马丁·布特纳(MartinBüttner)

BF和CJam真的很奇怪。还有谁知道呢!可以肯定,这将是一件容易的事,但还是很有趣。仅供参考,该名称指的是《最终幻想Type-0》中的《朱砂之火》

注意:请谅解我在此描述中的任何歧义。我不是最好的作家...:O

马丁真的很快就破解了!这是我解决问题的原始程序:

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











`
~
+
|
%

_
=

\
@
-
-
!
<
>
>
>

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

句法

Firetype脚本基本上是几行。每行的第一个字符是要运行的命令。空行基本上是NOP。

语义学

您有一个整数数组和一个指针(请考虑BF)。您可以左右移动或“推”元素到数组上。

推动

当您“推送”一个元素并且您位于数组的末尾时,一个额外的元素将附加到末尾。如果您不在最后,则将覆盖下一个元素。无论如何,指针将始终递增。

指令

_

将零推入数组。

+

在当前指针处增加元素。

-

减少当前指针处的元素。

*

将当前指针处的元素加倍。

/

在当前指针处将元素减半。

%

将元素放在当前指针处,向前跳那么多行,然后将指针移到左侧。如果该值为负,则向后跳转。

=

以当前指针处的元素为例,跳至该行+1。例如,如果当前元素为0,则将跳转至line 1。这也将指针向左移动。

,

从标准输入中读取一个字符并推送其ASCII值。

^

将元素放在当前指针处,将其解释为字符的ASCII值,然后将其转换为整数。例如,如果当前值为49 (的ASCII值1),则当前指针处的元素将设置为integer 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>0不应该选中>=0。(这不应使任何有效程序无效,但可能会使某些程序意外工作,而这不应该。)
Martin Ender

还有一件事:代码说=将值设置为array() - 1,文档说+1
马丁·恩德

破解 (假设解释器是规范性的,而不是描述性的。)
Martin Ender

@MartinBüttnerDang,那比我想象的要快!:OI解决了您提到的文档问题。
kirbyfan64sos

7

加速!(安全)

哎呀...

在此处输入图片说明

... 希望绝对能更好地防止漏洞。

我已经读过Acc!规格 怎么样啊!不同?

acc!,退出循环时,循环变量将超出范围。您只能在循环内使用它们。在外面,您会收到“名称未定义”错误。除了此更改外,语言是相同的。

陈述

命令逐行解析。共有三种类型的命令:

  1. Count <var> while <cond>

<var>只要<cond>非零就从0开始计数,等效于C ++ for(int <var>=0; <cond>; <var>++)。循环计数器可以是任何单个小写字母。条件可以是任何表达式,不一定涉及循环变量。当条件的值变为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,该特殊值读取字符并在每次使用时对其字符码求值。注意:这意味着您只能读一次才能读取每个字符;下次使用时N,您将阅读下一个。

运算符是:

  • +,加法
  • -,减法;一元否定
  • *,乘法
  • /,整数除法
  • %,取模
  • ^,求幂

括号可用于强制执行操作的优先级。表达式中的任何其他字符都是语法错误。

空格和注释

开头和结尾的空格以及空行将被忽略。循环标题中的空格必须完全如图所示,并且在循环标题和左花括号之间也要有一个空格。表达式内的空格是可选的。

# 开始单行注释。

输入输出

加速!期望单行字符作为输入。可以按顺序检索每个输入字符,并使用来处理其字符代码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的失败是循环变量,它们在循环之外仍然可以访问。这样就可以保存累加器的副本,这使得解决方案过于简单。

在这里,没有这个循环孔(对不起),我们只蓄电池存储的东西。但是,要解决该任务,我们需要存储四个任意大的值。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的适当次幂来访问任意数量的任何位。mod2。这也允许设置或翻转单个位。

在宏级别,该算法循环输入中的一元数。它将一个值读入数字0,然后通过冒泡排序算法以将其放置在与其他三个数字相比合适的位置。最后,它舍弃了数字0中剩余的值,因为它是现在四个中最小的,而且永远不可能是第三大。循环结束后,数字1是第三大数字,因此我们丢弃其他数字并输出。

最困难的部分(也是我在第一个实例中具有全局循环变量的原因)是比较两个数字以了解是否交换它们。为了比较两位,我们可以将真值表转换为数学表达式。我在Acc上的突破正在寻找一种从低位到高位进行比较的比较算法,因为如果没有全局变量,就无法从左到右遍历数字的位。累加器的最低位存储一个标志,该标志指示是否交换当前正在考虑的两个数字。

我怀疑是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根据问题规范,仅需要支持不超过一百万的值。我很高兴没有人利用它来获得更简单的解决方案-尽管我不完全确定您将如何进行比较。


哈哈@ Bill the Cat照片
意大利面条2015年

7

Picofuck(安全)

Picofuck与Smallfuck相似。它在二进制磁带上操作,该磁带在右侧未绑定,在左侧未绑定。它具有以下命令:

  • > 向右移动指针

  • <向左移动指针。如果指针从磁带上掉下来,程序将终止。

  • * 翻转指针处的位

  • (如果指针处的位是0,则跳到下一个)

  • )什么都不做-Picofuck中的括号是if块,而不是while循环。

  • .以ascii 01

  • ,从stdin读取,直到遇到a 01,并将其存储在指针的位中。

Picofuck代码自动换行-程序结束后,便从头开始继续。

此外,Picofuck禁止使用嵌套括号。Picofuck程序中出现的括号必须在(和之间交替),以开头(和结尾)

口译员

用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

@CalculatorFeline这是13KB,下载起来要容易得多。
mbomb007 '18

7

PQRS –安全!/提供的解决方案

基本

所有隐含指令都有四个内存地址操作数:

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ᵢ₊₁

请注意,SubleqPQRS的子集。

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₇需要在程序结束之前进行定义。'\0'输出中的空字节将被跳过。

终止

终止是通过设置Sᵢ界限i < 0或来实现的i ≥ v

技巧

四重奏Pᵢ Qᵢ Rᵢ Sᵢ无需对齐或顺序排列,允许在子四重奏间隔进行分支。

PQRS具有间接性,因此与Subleq不同,PQRS具有足够的灵活性来实现子例程。

代码可以自我修改!

口译员

解释器用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

样例程序

回声输入最多40个字符,后跟“ PQRS-”。

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

挑战

PQRS可以认为是稳定的,因此挑战开始于2015年10月31日13:00,结束于2015-11-08 13:00,以UTC时间为准。

祝好运!

该语言与世界上第一台存储程序的电子数字机器“ Baby”中使用的语言非常相似。该页面上的程序可以在少于(CRT!)个32字的内存中查找整数的最高因数!

我发现编写符合规范的解决方案与我所使用的操作系统和机器不兼容(Linux Ubuntu衍生产品在较旧的硬件上)。它只是在请求比可用内存更多的内存和核心转储。在具有高级虚拟内存管理的操作系统或内存至少为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

它的作用是解析输入,将一元转换为二进制,然后对数值进行冒泡排序,数值按降序排列,最后通过将二进制转换回一元输出第三大值。

请注意,INC(增加)为负,DEC(减少)为正!在使用L#or 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

语言

锌围绕重新定义运营商。您可以轻松地用该语言重新定义所有运算符!

典型的Zinc程序的结构如下:

let
<operator overrides>
in <expression>

只有两种数据类型:整数和集合。没有集合字面量这样的东西,并且不允许使用空集合。

表达方式

以下是Zinc中的有效表达式:

文字

Zinc支持所有普通的整数文字,例如1-2

变数

锌具有变量(像大多数语言一样)。要引用它们,只需使用名称。像大多数语言一样!

但是,有一个特殊的变量称为S,其行为类似于Pyth's Q。首次使用时,它将从标准输入中读取一行,并将其解释为一组数字。例如,输入线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子句。

一个当条款的样子^<expression>。插入符号后面的表达式必须为整数。使用when子句将只接受集合中expression非零的元素。在表达式内,变量_将被设置为集合中的当前索引。它大致等效于以下Python:

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

看起来像一个sort子句$<expression>将按降序对集合进行排序<expression>。等于这个Python:

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

以下是一些理解示例:

  • 只取set s等于5 的元素:

    {x:s^x=5}
    
  • s如果集合的元素平方,则按值对集合排序:

    {x:s$x*x}
    

覆写

运算符覆盖使您可以重新定义运算符。他们看起来像这样:

<operator>=<operator>

要么:

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

在第一种情况下,您可以将一个运算符定义为等于另一个运算符。例如,我可以定义+通过以下方式实际减去:

+=-

执行此操作时,可以将运算符重新定义为魔术运算符。有两个魔术运算符:

  • 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

那是+:什么 您可以将冒号附加:到运算符以始终使用内置版本。本示例使用内置++:将数字加在一起,然后加一个1(请记住,所有内容都是右关联的)。

覆盖长度运算符看起来像:

#x=<expression>

请注意,几乎所有内置操作(相等性除外)都将使用此length运算符来确定集合的长度。如果您将其定义为:

#x=1

锌的每个部分都可用于套装,除了 =都只能的第一个元素上操作。

多重覆写

您可以使用逗号分隔多个运算符:

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

列印

您不能直接在Zinc中打印任何内容。以下表达式的结果in将被打印。一组值将与分隔符连接。例如,采取以下方法:

let
...
in expr

如果expr设置为{1, 3, {2, 4}}1324则程序完成后将被打印到屏幕上。

放在一起

这是一个似乎添加的简单Zinc程序,2+2但导致结果为5:

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

@Zgarb是的,在这种情况下,集合基本上是列表。加入混合集应该是一个错误,但解释程序实际上
会使

如何运行口译员?如果我只是尝试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() ?? '';
Martin Ender


1
@Zgarb还记得规范中我说过除相等性以外的所有内置操作都使用长度运算符吗?-2+#:S给定时S,我将其覆盖以返回,这会切断两个尾随的零。那就是我希望能解决的方式。而且^不应该颠倒设置...那是一个错误...
kirbyfan64sos 2015年

5

指南针汤(被硬纸箱破解

口译员:C ++

指南针汤有点像带有无限二维胶带的图灵机。主要问题是指令存储器和数据存储器在同一空间中,而程序的输出是该空间的全部内容。

在此处输入图片说明

这个怎么运作

程序是二维文本块。程序空间从整个源代码开始,第一个字符位于(0,0)。程序空间的其余部分是无限的,并使用空字符(ASCII 0)进行初始化。

有两个指针可以在程序空间中移动:

  • 执行指针具有位置和方向(北,南,东或西)。每次滴答,执行指针下的指令都会被执行,然后执行指针沿其当前方向移动。执行指针开始向东移动(正x),位于!字符如果不存在则从(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'一次添加或删除多行文本。

这是我的解决方案。它不如boardboard_box的好,因为我必须删除源代码本身。我也希望我能找到一种删除所有代码并只留下答案的方法,但我做不到。

我的方法是将1s 的不同序列分成不同的行,然后通过使1s全部“下降”直到它们碰到另一行来对它们进行排序1,最后清除输入后第三行以外的所有内容。

  • #A#读取1s 右下角的大块,并将它们复制到拆分的最后一行,直到0读取a。
  • #B#检查一秒钟0然后往北走#D#。否则,#C#通过|在最后一条之后放置新的分割线,然后返回#A#
  • 上面和上面的块#F#是重力代码。它走到1第一行的最后一行,然后将其向上移动直到达到1-。如果无法做到这一点,则通过在行+之前标记行结束。
  • #G#正在删除所有不必要的拆分,并#H#删除标准输入和括号之间的所有代码。

码:

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

我无法使奇偶校验程序正常工作。一开始应该有一条调试指令吗?如果我单步执行它陷入无限循环,您知道我在做什么错吗?
feersum 2015年

似乎一c开始就应该有一个额外的地方。我修好了它。还添加了我的解决方案。
BMac

4

加速!Cracc'd通过ppperry

该语言具有一个循环结构,基本整数数学,字符I / O和一个累加器(因此称为名称)。只有一个蓄能器。因此,名称。

陈述

命令逐行解析。共有三种类型的命令:

  1. Count <var> while <cond>

<var>只要<cond>不为零就从0开始计数,等效于C-style for(<var>=0; <cond>; <var>++)。循环计数器可以是任何单个小写字母。条件可以是任何表达式,不一定涉及循环变量。当条件的值变为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,该特殊值读取字符并在每次使用时对其字符码求值。注意:这意味着您只能读一次才能读取每个字符;下次使用N,您将阅读下一个。

运算符是:

  • +,加法
  • -,减法;一元否定
  • *,乘法
  • /,整数除法
  • %,取模
  • ^,求幂

括号可用于强制执行操作的优先级。表达式中的任何其他字符都是语法错误。

空格和注释

开头和结尾的空格以及空行将被忽略。循环标题中的空格必须完全如图所示,并且在循环标题和左花括号之间也要有一个空格。表达式内的空格是可选的。

# 开始单行注释。

输入输出

加速!期望单行字符作为输入。可以按顺序检索每个输入字符,并使用来处理其字符代码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值的char。
  • &:如果累加器等于或大于1,则从累加器中减去一个,否则加1。
  • |:将一个加到累加器上。
  • <:将数据指针设置为0。
  • +:在数据指针处增加数据单元格;将指针移动+1。
  • -:如果为正,则从数据指针的数据单元中减去一个;否则为0。将指针移动+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 ++文件,并使用20 ifs而不是a switch?我不是在抱怨,但我的眼睛现在正在流血...:O
kirbyfan64sos 15-10-30

3
我已经修复了斑点问题,使翻译人员吃饱了。
MegaTom,2015年

@ kirbyfan64sos代码错误。我有点把它放在一起,可能做得还不够好。可以更改主要功能,以将代码作为输入。事实上,我认为我现在
会这样做

1
问题是,解释器应在程序的命令行中使用文件名
丹尼斯

这是将文件读入字符串的一些 简短方法。然后致电str.c_str()获取char*
feersum

0

这是一个坏主意,因为几乎所有深奥的语言看起来都不可读(请看Jelly)。
但是这里:

Pylongolf2 beta6

推入堆栈

压入堆栈的行为与其他语言不同。
代码7878到堆栈,但是在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语句,请放置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是一种基于2D堆栈的语言,具有两个堆栈(如Brain-Flak)和8个方向(N NE E SE S SW W NW)。有8个命令:

  • 1+*"正是他们在1+做什么做的。
  • ! 切换活动堆栈。
  • > 顺时针旋转IP。
  • , 输入一个字符并按下。
  • . 弹出并输出一个字符。

但是,源代码中的字符不会立即执行。而是[The Character in the source code]^[Top Of Stack]将其输入到Collat​​z猜想中,达到1所需的步数将通过ASCII表转换为字符。然后执行此字符。

  • 如果达到1所需的步数超过127,则将总步数除以127,获取提醒,然后将提醒添加到商中。

在程序的开头,源代码(最后一个字符除外)被压入堆栈。

当IP到达源代码的边缘时,它将终止。

启示录

n和m是两个寄存器。当>指令被执行,将m递增。启示仅在m超过n时触发。启示录发生时:

  • 逆时针旋转而不是顺时针旋转。
  • m变为0。
  • n成为堆栈的顶部。然后,堆栈弹出。

m最初为零,n最初为源代码的最后一个字符。

加密

执行任何执行后,将对源代码进行加密。第一个字符的ASCII递增1,第二个字符递减1,第三个字符递减2,第4个字符递减2,依此类推。


1
非常确定您需要一位翻译才能使这成为有效答案...
Conor O'Brien

@ ConorO'Brien由于此挑战已经过期,所以这很有趣。不过,我将提供口译员。
高度放射性

@HighlyRadioactive ...您差不多一个月前说过。
pppery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.