変異センザンコウ


28

これはコードゴルフの課題であり、機械学習を説明するために馬または馬を改造する馬のように振る舞うプログラムを考案する必要があります。

バックグラウンド

ここで説明する「センザンコウゲーム」と呼ばれる基本的な人工知能プログラムがあります。基本的なアイデアは、プログラムを初めて実行するときに次のことを尋ねることです。

OK、何か考えてください

それはセンザンコウですか?

その後、次のいずれかを返信できます。

はい

その場合、それは言います:

良い。とても簡単でした。

または、そうでない場合:

ああ。さて、あなたは勝ちます-あなたは何を考えていましたか?

あなたが言うかもしれない:

それに言うだろう

犬について質問してください。犬とセンザンコウの違いがわかります。

返信するかもしれません

アリを食べますか?

それから尋ねます:

犬の答えは何ですか?

あなたが言うだろう

いや

そしてそれは言うだろう

ありがとう

次回の実行時に、上記の質問をし、そのような質問のバイナリツリーを構築します。

チャレンジ

背景は十分です。この課題は、自己修正のセンザンコウプログラムを作成することです。ルールは次のとおりです。

  1. プログラムの出力(上記の説明を参照)はになりますSTDERR。最終的な応答は常に「良い。それはすっごく簡単だった」です。または「ありがとう」。この後、プログラムの現在のバージョン、または質問をに組み込んだプログラムの新しいバージョンを出力する必要がありますSTDOUT。書き込みをサポートしていない言語で書かれた無回答STDOUTおよびSTDERRまたはからの読み取りが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」で始まる、yまたはY「yes」で始まる答えはすべて答え、それ以外は「no」で答える場合があります。

  2. 提供されるものの名​​前と質問はASCII文字、数字、スペース、ハイフン、疑問符、コンマ、ピリオド、コロン、セミコロンのみで構成されていると仮定できます^[-?,.;: a-zA-Z]+$。つまり、次のregexと一致します。それ以上(特に選択した言語の引用文字)に対処できる場合は、独善的になりますが、余分なポイントは獲得しません。

  3. 任意のファイルを読み書きないかもしれないあなたのプログラムが(除くSTDINSTDOUT、およびSTDERRまたはネットワークから、); 具体的には、ディスクから独自のコードを読み書きすることはできません。その状態は、プログラムコード自体に保存する必要があります。

  4. プログラムが実行され、答えを正しく推測するとき、それ正確にクインと同じように実行する必要があります。つまりSTDOUT、変更せずに正確に独自のコードに書き込む必要があります。

  5. プログラムを実行して、間違って答えを推測されている場合、それはしなければならない、独自のコード内で提供される新しい質問と答えをエンコードし、それを書きSTDOUT、それが元の推測と提供される新しいオブジェクト間で区別することができるので、独自のコードでは、中以前に指定されたすべてのオブジェクトを区別することに加えて。

  6. 多くのオブジェクトについて学習するために、ソフトウェアの複数の連続実行に対処できる必要があります。複数の実行の例については、こちらをご覧ください。

  7. テストの実行は、ヘッド内リンクで行われます(もちろんSTDINSTDERRダイアログのみを対象としています)。

  8. 標準の抜け穴は除外されます。


プログラムは複数回変異し、2匹以上の動物をサポートできるべきですか?もしそうなら、プログラムが知っている動物がすでに2匹以上いる場合に、「…について質問してください」というダイアログの例を提供できますか?
クリスチャンルパスク

ユーザーが「犬」ではなく「犬」とだけ言うとどうなりますか?「a / an」を検出するために文を解析するか、答えを文字通り扱うことができますか?あなたが与えたプロンプト(%s)を考えるとそう思います。
コアダンプ

1
@coredumpユーザーが「犬」ではなく「犬」と言った場合、応答は文法的になりません。それは問題ではありません。
8

1
おー Runicでこれを実行しようとすると、悪夢になります。主な理由は、任意の入力文字列(結果の出力プログラムに文字列リテラルとして存在する必要がある)に対処するためにすべてのビットを配線することは基本的に不可能だからです。OhとRunicはSTDERRに出力できません。
Draco18s

1
これは楽しい「ゲーム」のように思えたので、ゴルフではなく、私はあなたが心行くまでパンゴリンゲームをプレイできるコードペンを作成しました。楽しい!
スキッドデブ

回答:


20

Common 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"葉を表すなどの文字列。
  • 3つの要素のリスト:(question if-true if-false)where questionは閉じたyes / noの質問で、文字列として、if-trueおよびif-falseは質問に関連付けられた2つの可能なサブツリーです。

Uこの関数は歩く返す可能性の修正ツリーを。各質問は、ユーザーと対話しながら、ルートからリーフに到達するまで順番に尋ねられます。

  • 中間ノードのために返された値が(Q Y N)ある(Q (U Y) N)(それぞれ(Q Y (U N))の質問への答えがあれば)Qであるイエス(それぞれなし)。

  • リーフの戻り値は、プログラムが答えを正しく推測した場合はリーフ自体、またはユーザーから取得した値に応じて、リーフが質問と2つの可能な結果に置き換えられる洗練されたツリーのいずれかです。

この部分はかなり簡単でした。ソースコードを印刷するには、リーダー変数を使用して自己参照コードを作成します。*PRINT-CIRCLE*trueに設定することにより、プリティプリンティング中の無限再帰を回避します。WRITEwith を使用する際の秘The :print-circle Tは、関数がwriteが最後の形式であるかどうかに応じてREPLに値を返す可能性があることです。したがって、REPLが標準のデフォルト値*PRINT-CIRCLE*、無限の再帰があります。循環構造がREPLに返されないことを確認する必要があるだけです。そのため、LETの最後の位置にNILがあります。このアプローチにより、問題が大幅に軽減されます。


いいね!これ(y or n)は必須ではありませんが、改善されているため、許可したいと思います。
15

@ablighありがとう。y / nについては、それがいいと思います。これは、プロンプトの短縮を避けることについての#3と実際には矛盾していません。
コアダンプ

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)

オンラインでお試しください!

質問/回答/回答は配列に格納されます。配列に3つの項目(例:)が格納されている場合['Does it eat ants',['a pangolin'],['a dog']]、質問に対する回答が取得され、回答に応じて2番目または3番目の項目の内容だけが繰り返されます。アイテムが1つだけの配列に到達すると、質問をします。ソースコード全体が文字列であるため、split-joinメソッドを使用して、新しいブランチを追加するために配列に拡張機能を挿入できます。 。

私はもともとこれを書いて、クインの要件を実現していなかったので、質問を読み直して、コードを実行して文字列として使用する方法を見つける必要がありましたが、最終的に素敵な拡張可能なクイン形式のアイデアに出会いました:

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の回答にかなり似ています。リストをさらに深くしながら、次の質問を再帰的に要求し、応答が1つだけになるまで続けます。

ゴルフされていないバージョン(カニングなし)

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.