变异穿山甲


28

这是一个代码挑战,您需要设计一个程序,像担子一样工作,或者修改自己以说明机器学习的担子。

背景

有一个叫被描述“穿山甲游戏的一个基本的人工智能程序在这里。基本思想是程序在首次运行时会询问:

好啦请想一想

是穿山甲吗?

然后,您可以答复:

在这种情况下,它说:

好。那太容易了。

否则,它会说:

哦。那你赢了-你在想什么?

您可能会说:

一只狗

它会说

请给我一个关于狗的问题,以便告诉我狗和穿山甲的区别

你可能会回复

它吃蚂蚁吗?

然后它会问:

狗的答案是什么?

你会说

没有

它会说

谢谢

下次运行时,它会问上面的问题,并将建立一个此类问题的二叉树。

挑战

足够的背景。这个挑战是编写一个自我修改的穿山甲程序。规则如下:

  1. 程序输出(如上所述)应为STDERR。最终的答复永远是“好。那太容易了”。或“谢谢”。此后,它应该输出程序的当前版本,或将问题合并到的程序的新版本STDOUT。没有答案写在不支持写入语言STDOUTSTDERR或阅读STDIN将是有效的。

  2. 换句话说,在UNIX下,您可以像这样调用程序:

例:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. 该程序必须完全使用指定的提示(因为缩短提示不显示任何技巧)。提示如下(不带引号,并用%s代替):

清单:

"OK, please think of something"
"Is it %s?"
"Good. That was soooo easy."
"Oh. Well you win then -- What were you thinking of?"
"Please give me a question about %s, so I can tell the difference between %s and %s"
"What is the answer for %s?"
"Thanks"
  1. 当期望是/否答案时,您的程序应该接受,y或者yes在任何情况下都接受“是”,n或者no在任何情况下都接受“否”。您如何处理不合格的输入取决于您自己。例如,您可能决定采用以“ yes” 开头yY为“ yes”的其他答案,否则为“ no”。

  2. 您可以假定提供的事物和问题的名称仅包含ASCII字母,数字,空格,连字符,问号,逗号,句号,冒号和分号,即它们与以下正则表达式匹配^[-?,.;: a-zA-Z]+$。如果您能应付的不止这些(尤其是所选语言中的引号字符),您会感到自鸣得意,但不会获得任何额外的加分。

  3. 你的程序可能无法读取或写入任何文件(不包括STDINSTDOUTSTDERR),或从网络; 特别是它既不能从磁盘读取也不可以写入自己的代码。它的状态必须保存在程序代码本身中。

  4. 当程序运行并正确猜出答案时,它必须完全像一个木篮一样执行,即它必须STDOUT准确地编写自己的代码,而不会改变。

  5. 当程序运行并错误地猜出答案时,它必须在其自己的代码中对提供的新问题和答案进行编码,然后将STDOUT其写入自己的代码中,以便能够区分其原始猜测和提供的新对象。除了区分所有先前给定的对象。

  6. 您必须能够处理该软件的多个顺序运行,这样它才能了解许多对象。有关多次运行的示例,请参见此处

  7. 测试运行在头部链接处进行(显然仅涵盖STDINSTDERR对话框)。

  8. 不包括标准漏洞


该程序是否应该能够变异多次并支持超过2种动物?如果是这样,当程序已经知道两个或两个以上的动物时,可以提供一个示例“请给我一个有关...的问题”对话框吗?
Cristian Lupascu 2015年

如果用户只说“狗”而不是“狗”怎么办?我们应该分析句子以检测“一个/一个”还是可以按字面意义对待答案?考虑到您给出的提示(%s),我认为是这样。
coredump

1
@coredump如果用户说“狗”而不是“狗”,则答复将不会是语法上的。那不是问题。
2015年

1
钱币。试图在符文里做到这一点将是一场噩梦。主要原因是将所有位连接起来以处理任意输入字符串(然后需要在结果输出程序中将其作为字符串文字出现)基本上是不可能的。哦,符文不能输出到STDERR。
Draco18s

1
这似乎是一个有趣的“游戏”,所以我制作了一个密码笔,而不是打高尔夫球,在那里您可以尽情地玩穿山甲游戏。请享用!
Skidsdev

Answers:


20

普通Lisp 631 576

(let((X"a pangolin"))#1=(labels((U(M &AUX(S *QUERY-IO*))(IF(STRINGP M)(IF(Y-OR-N-P"Is it ~A?"M)(PROG1 M(FORMAT S"Good. That was soooo easy.~%"))(LET*((N(PROGN(FORMAT S"Oh. Well you win then -- What were you thinking of?~%")#2=(READ-LINE S)))(Q(PROGN(FORMAT S"Please give me a question about ~A, so I can tell the difference between ~A and ~A~%"N N M)#2#)))(PROG1(IF(Y-OR-N-P"What is the answer for ~A?"N)`(,Q ,N ,M)`(,Q ,M ,N))(FORMAT S"Thanks~%"))))(DESTRUCTURING-BIND(Q Y N)M(IF(Y-OR-N-P Q)`(,Q ,(U Y),N)`(,Q ,Y,(U N)))))))(write(list'let(list`(X',(U x)))'#1#):circle t)()))

会话示例

命名脚本pango1.lisp并执行以下操作(使用SBCL):

~$ sbcl --noinform --quit --load pango1.lisp > pango2.lisp
Is it a pangolin? (y or n) n
Oh. Well you win then -- What were you thinking of?
a cat
Please give me a question about a cat, so I can tell the difference between a cat and a pangolin
Does it sleep a lot?
What is the answer for a cat? (y or n) y
Thanks

再加一轮熊:

~$ sbcl --noinform --quit --load pango2.lisp > pango3.lisp
Does it sleep a lot? (y or n) y

Is it a cat? (y or n) n
Oh. Well you win then -- What were you thinking of?
a bear
Please give me a question about a bear, so I can tell the difference between a bear and a cat
Does it hibernate?
What is the answer for a bear? (y or n) y
Thanks

添加一个懒惰(我们测试答案是否为“否”的情况):

~$ sbcl --noinform --quit --load pango3.lisp > pango4.lisp
Does it sleep a lot? (y or n) y

Does it hibernate? (y or n) n

Is it a cat? (y or n) n
Oh. Well you win then -- What were you thinking of?
a sloth
Please give me a question about a sloth, so I can tell the difference between a sloth and a cat
Does it move fast?
What is the answer for a sloth? (y or n) n
Thanks

测试最后一个文件:

~$ sbcl --noinform --quit --load pango4.lisp > pango5.lisp
Does it sleep a lot? (y or n) y

Does it hibernate? (y or n) n

Does it move fast? (y or n) y

Is it a cat? (y or n) y
Good. That was soooo easy.

备注

  • 我首先忘记打印了"Thanks",就在这里。
  • 如您所见,问题后跟(y or n),这是因为我正在使用现有y-or-n-p功能。如果需要,我可以更新答案以删除此输出。
  • Common Lisp有一个*QUERY-IO*专用于用户交互的双向流,这就是我在这里使用的。标准输出和用户交互不会混乱,这遵循恕我直言的精神。
  • SAVE-LISP-AND-DIE在实践中使用将是更好的方法。

产生的输出

这是最后生成的脚本:

(LET ((X
       '("Does it sleep a lot?"
              ("Does it hibernate?" "a bear"
               ("Does it move fast?" "a cat" "a sloth"))
              "a pangolin")))
  #1=(LABELS ((U (M &AUX (S *QUERY-IO*))
                (IF (STRINGP M)
                    (IF (Y-OR-N-P "Is it ~A?" M)
                        (PROG1 M (FORMAT S "Good. That was soooo easy.~%"))
                        (LET* ((N
                                (PROGN
                                 (FORMAT S
                                         "Oh. Well you win then -- What were you thinking of?~%")
                                 #2=(READ-LINE S)))
                               (Q
                                (PROGN
                                 (FORMAT S
                                         "Please give me a question about ~A, so I can tell the difference between ~A and ~A~%" 
                                         N N M)
                                 #2#)))
                          (PROG1
                              (IF (Y-OR-N-P "What is the answer for ~A?" N)
                                  `(,Q ,N ,M)
                                  `(,Q ,M ,N))
                            (FORMAT S "Thanks~%"))))
                    (DESTRUCTURING-BIND
                        (Q Y N)
                        M
                      (IF (Y-OR-N-P Q)
                          `(,Q ,(U Y) ,N)
                          `(,Q ,Y ,(U N)))))))
       (WRITE (LIST 'LET (LIST `(X ',(U X))) '#1#) :CIRCLE T)
       NIL))

说明

决策树可以是:

  • 一个字符串,如"a pangolin",表示叶子。
  • 包含三个元素的列表:(question if-true if-false)其中question是一个闭合的是/否问题(以字符串形式),if-true并且if-false是与该问题相关联的两个可能的子树。

U功能,并返回一个可能被修改树。在与用户交互的同时,依次询问每个问题,从根开始直到到达叶子。

  • 对于中间节点返回的值(Q Y N)(Q (U Y) N)(相应(Q Y (U N))),如果回答的问题Q肯定的(相应地,)。

  • 叶子的返回值可以是叶子本身(如果程序正确猜出了答案),也可以是精致树,根据用户取值,叶子被问题和两个可能的结果代替。

这部分非常简单。为了打印源代码,我们使用阅读器变量来构建自引用代码。通过设置*PRINT-CIRCLE*为true,我们可以避免在漂亮打印期间进行无限递归。使用WRITEwith 的技巧:print-circle T是,函数也可能会将值返回给REPL,具体取决于写操作是否为最后一种形式,因此,如果REPL不处理圆形结构,例如由标准默认值定义*PRINT-CIRCLE*,则将无限递归。我们只需要确保没有将循环结构返回给REPL,这就是为什么在LET的最后位置有一个NIL的原因。这种方法大大减少了问题。


看起来不错!该(y or n)不是必需的,但我忍不住要允许它,因为它是一种进步。
2015年

@abligh谢谢。关于y / n,那会很好,它会有所帮助,恕我直言,这与#3(实际上是避免避免缩短提示)并不矛盾。
coredump

9

蟒2.7.6,820个 728字节

(可能适用于不同版本,但我不确定)

def r(O,R):
 import sys,marshal as m;w=sys.stderr.write;i=sys.stdin.readline;t=O;w("OK, please think of something\n");u=[]
 def y(s):w(s);return i()[0]=='y'
 while t:
  if type(t)==str:
   if y("Is it %s?"%t):w("Good. That was soooo easy.")
   else:w("Oh. Well you win then -- What were you thinking of?");I=i().strip();w("Please give me a question about %s, so I can tell the difference between %s and %s"%(I,t,I));q=i().strip();a=y("What is the answer for %s?"%q);w("Thanks");p=[q,t];p.insert(a+1,I);z=locals();exec"O"+"".join(["[%s]"%j for j in u])+"=p"in z,z;O=z["O"]
   t=0
  else:u+=[y(t[0])+1];t=t[u[-1]]
 print"import marshal as m;c=%r;d=lambda:0;d.__code__=m.loads(c);d(%r,d)"%(m.dumps(R.__code__),O)
r('a pangolin',r)

好吧,它不比Common Lisp的答案短,但是这里有一些代码!


4

Python 3,544字节

q="""
d=['a pangolin'];i=input;p=print
p("OK, Please think of something")
while len(d)!=1:
    d=d[1+(i(d[0])[0]=="n")]
x=i("Is it "+d[0]+"?")
if x[0]=="n":
    m=i("Oh. Well you win then -- What were you thinking of?")
    n=[i("Please give me a question about "+m+", so I can tell the difference between "+d[0]+" and "+m),*[[d[0]],[m]][::(i("What is the answer for "+m+"?")[0]=="n")*2-1]]
    p("Thanks")
    q=repr(n).join(q.split(repr(d)))
else:
    p("Good. That was soooo easy.")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec(q)'
p(q)
"""
exec(q)

在线尝试!

问题/答案/答案存储在一个数组中,如果该数组存储三个项目(例如['Does it eat ants',['a pangolin'],['a dog']]),则它会得到问题的答案,并根据答案仅重复第二或第三项的内容。当它到达只有一个项目的数组时,它会问这个问题,并且由于它的整个源代码都是字符串,因此它能够使用split-join方法将扩展名插入到数组中以添加新分支。 。

我最初写的代码并没有实现quine的要求,因此重读问题并必须找到一种既可以执行代码又可以将其用作字符串的方法,但是最终我偶然发现了一种不错的可扩展quine格式:

q="""
print("Some actual stuff")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec()'
print(q)
"""
exec(q)

1

Python 3,497个字节

t=["a pangolin"];c='''p=print;i=input;a=lambda q:i(q)[0]in"Yy"
def q(t):
  if len(t)<2:
    g=t[0]
    if a(f"Is it {g}?"):p("Good. That was soooo easy.")
    else:s=i("Oh. Well you win then -- What were you thinking of?");n=i(f"Please give me a question about {s}, so I can tell the difference between {s} and {g}.");t[0]=n;t+=[[g],[s]][::1-2*a(f"What is the answer for {s}?")];p("Thanks")
  else:q(t[2-a(t[0])])
p("Ok, please think of something");q(t);p(f"t={t};c=''{c!r}'';exec(c)")''';exec(c)

非常类似于Harmless对树表示的回答。它递归地询问下一个问题,同时深入列表中,直到只有一个响应为止。

非高尔夫版本(无奎因)

tree = ['a pangolin']

def ask(question):
  answer = input(question + '\n')
  if answer.lower() in ['yes', 'no']:
    return answer.lower() == 'yes'
  else:
    print('Please answer "yes" or "no".')
    return ask(question)
    
def query(tree):
  if len(tree) == 1:
    guess = tree.pop()
    if ask(f'Is it {guess}?'):
      print('Good. That was soooo easy.')
      tree.append(guess)
    else:
      thing = input('Oh. Well you win then -- What were you thinking of?\n')
      new_question = input(f'Please give me a question about {thing}, so I can tell the difference between {thing} and {guess}.\n')
      answer = ask(f'What is the answer for {thing}?')
      print('Thanks')
      tree.append(new_question)
      if answer:
        tree.append([thing])
        tree.append([guess])
      else:
        tree.append([guess])
        tree.append([thing])
  else:
    if ask(tree[0]):
      query(tree[1])
    else:
      query(tree[2])
      
while True:
  input('Ok, please think of something\n')
  query(tree)
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.