Mutiertes Pangolin

28

Dies ist eine Code-Golf-Herausforderung, bei der Sie ein Programm entwickeln müssen, das sich wie ein Quine verhält oder das sich selbst modifiziert, um das maschinelle Lernen zu veranschaulichen.

Hintergrund

Es ist ein grundlegendes künstliche Intelligenz Programm ‚das pangolin Spiel‘ genannt , die beschrieben wird , hier . Die Grundidee ist, dass das Programm beim ersten Start fragt:

OK, bitte denken Sie an etwas

Ist es ein Pangolin?

Sie können dann entweder antworten:

Ja

In welchem ​​Fall heißt es:

Gut. Das war soooo einfach.

Oder wenn nicht, heißt es:

Oh. Na dann gewinnst du - woran hast du gedacht?

Zu dem könnte man sagen:

ein Hund

Zu dem würde es sagen

Bitte geben Sie mir eine Frage zu einem Hund, damit ich den Unterschied zwischen einem Hund und einem Pangolin erkennen kann

du könntest antworten

Frisst es Ameisen?

Es würde dann fragen:

Was ist die Antwort für einen Hund?

Zu dem würdest du sagen

Nein

Und es würde sagen

Vielen Dank

Wenn es das nächste Mal ausgeführt wird, würde es die obige Frage stellen und einen binären Baum solcher Fragen aufbauen.

Die Herausforderung

Genug vom Hintergrund. Diese Herausforderung besteht darin, ein selbstmodifizierendes Pangolin-Programm zu schreiben. Die Regeln sind wie folgt:

  1. Die Programmausgabe (wie oben beschrieben) sollte an sein STDERR. Die endgültige Antwort lautet immer "Gut. Das war soooo einfach." oder "Danke". Danach sollte entweder die aktuelle Version des Programms oder eine neue Version des Programms ausgegeben werden, die die Frage enthält STDOUT. Keine Antwort, die in einer Sprache verfasst wurde, die das Schreiben STDOUTund STDERRLesen nicht unterstützt, STDINist gültig.

  2. Mit anderen Worten, unter UNIX können Sie das Programm folgendermaßen aufrufen:

Beispiel:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. Das Programm muss genau die angegebenen Eingabeaufforderungen verwenden (da das Verkürzen der Eingabeaufforderungen keine Fertigkeiten zeigt). Die Eingabeaufforderungen lauten (ohne Anführungszeichen und wobei% s ersetzt wird) wie folgt:

Liste:

"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. Wenn erwartet Ja / Nein - Antworten, soll Ihr Programm übernehmen yoder yesin jedem Fall für ‚Ja‘, und noder noin jedem Fall für ‚Nein‘. Was Sie mit nicht konformen Eingaben tun, liegt bei Ihnen. Zum Beispiel könnten Sie sich dafür entscheiden, eine Antwort zu nehmen, die mit yoder Yals "Ja" beginnt , und alles andere als "Nein".

  2. Sie können davon ausgehen, dass die Namen der gelieferten Dinge und der Fragen nur aus ASCII-Buchstaben, -Zahlen, -Leerzeichen, -Trennstrichen, -Fragezeichen, -Kommas, -Punkten, -Doppelpunkten und -Semikolons bestehen, dh sie stimmen mit dem folgenden regulären Ausdruck überein ^[-?,.;: a-zA-Z]+$. Wenn Sie mehr als das bewältigen können (insbesondere die Anführungszeichen in der von Ihnen gewählten Sprache), werden Sie selbstgefällig, erhalten aber keine zusätzlichen Punkte.

  3. Ihr Programm kann eine beliebige Datei nicht lesen oder schreiben (ohne STDIN, STDOUTund STDERR), oder aus dem Netz; Insbesondere kann er keinen eigenen Code von der Festplatte lesen oder schreiben. Sein Zustand muss im Programmcode selbst gespeichert werden.

  4. Wenn das Programm ausgeführt wird und die Antwort richtig errät, muss es genau wie ein Quine funktionieren, dh es muss STDOUTunverändert in genau seinen eigenen Code schreiben .

  5. Wenn das Programm ausgeführt wird und die Antwort falsch errät, muss es die bereitgestellte neue Frage und Antwort in seinem eigenen Code codieren und in seinen eigenen Code schreiben STDOUT, damit es in der Lage ist, zwischen seiner ursprünglichen Vermutung und dem bereitgestellten neuen Objekt zu unterscheiden Neben der Unterscheidung zwischen allen zuvor gegebenen Objekten.

  6. Sie müssen in der Lage sein, mehrere sequenzielle Läufe der Software zu bewältigen, damit sie viele Objekte erkennt. Sehen Sie hier Beispiele für mehrere Läufe.

  7. Testläufe finden Sie am Link im Kopf (natürlich nur im STDINund STDERR-Dialog).

  8. Standardlücken sind ausgeschlossen.

abligh
quelle
Sollte das Programm mehrmals mutieren und mehr als 2 Tiere unterstützen können? Wenn ja, können Sie ein Beispiel für den Dialog "Bitte geben Sie mir eine Frage zu ..." bereitstellen, wenn das Programm bereits zwei oder mehr Tiere kennt?
Cristian Lupascu
Was ist, wenn der Benutzer nur "Hund" anstelle von "Hund" sagt? Sollen wir den Satz analysieren, um "ein / eine" zu erkennen, oder können wir die Antwort wörtlich behandeln? Ich gehe davon aus, dass die von Ihnen gegebenen Aufforderungen (% s) so lauten.
Coredump
1
@coredump Wenn der Benutzer "Hund" und nicht "ein Hund" sagt, sind die Antworten nicht grammatikalisch. Das ist kein Problem.
abligh
1
Oof. Dies in Runic zu versuchen, wäre ein Albtraum. Der Hauptgrund dafür ist, dass die Verdrahtung aller Bits zur Bewältigung beliebiger Eingabezeichenfolgen (die dann im resultierenden Ausgabeprogramm als Zeichenfolgenliterale vorliegen müssen) im Grunde unmöglich wäre. Oh und Runic können nicht an STDERR ausgeben.
Draco18s
1
Dies schien ein lustiges "Spiel" zu sein, und statt Golf zu spielen, habe ich einen Codepen erstellt, mit dem Sie das Pangolin-Spiel nach Herzenslust spielen können. Genießen!
Skidsdev

Antworten:

20

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

Beispielsitzung

Benennen Sie das Skript pango1.lispund führen Sie es wie folgt aus (mit 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

Eine weitere Runde mit dem Bären:

~$ 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

Faultier hinzufügen (wir testen den Fall, in dem die Antwort "nein" lautet):

~$ 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

Testen der letzten Datei:

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

Bemerkungen

  • Ich habe zuerst vergessen zu drucken "Thanks", hier ist es.
  • Wie Sie sehen, folgen Fragen (y or n), weil ich die vorhandene y-or-n-pFunktion verwende. Ich kann die Antwort aktualisieren, um diese Ausgabe bei Bedarf zu entfernen.
  • Common Lisp hat einen bidirektionalen *QUERY-IO*Stream für die Benutzerinteraktion, den ich hier verwende. Standard-Ausgabe und Benutzer-Interaktion nicht durcheinander, was IMHO den Geist der Frage folgt.
  • Verwenden SAVE-LISP-AND-DIEwäre in der Praxis ein besserer Ansatz.

Erzeugte Ausgabe

Hier ist das zuletzt generierte Skript:

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

Erklärungen

Ein Entscheidungsbaum kann sein:

  • eine Zeichenfolge, wie "a pangolin", die ein Blatt darstellt.
  • Eine Liste mit drei Elementen: (question if-true if-false)Wo questionist eine geschlossene Ja / Nein- Frage als Zeichenfolge und if-trueund if-falsesind die zwei möglichen Unterbäume, die mit der Frage verknüpft sind.

Die UFunktion geht und gibt einen möglicherweise geänderten Baum zurück. Jede Frage wird der Reihe nach gestellt, angefangen von der Wurzel bis zum Erreichen eines Blattes, während mit dem Benutzer interagiert wird.

  • Der zurückgegebene Wert für einen Zwischenknoten (Q Y N)ist (Q (U Y) N)(bzw. (Q Y (U N))), wenn die Antwort auf die Frage Ja (bzw. Nein ) Qlautet .

  • Der zurückgegebene Wert für ein Blatt ist entweder das Blatt selbst, wenn das Programm die Antwort richtig erraten hat, oder ein verfeinerter Baum, in dem das Blatt durch eine Frage und zwei mögliche Ergebnisse gemäß den vom Benutzer übernommenen Werten ersetzt wird.

Dieser Teil war ziemlich einfach. Um den Quellcode zu drucken, verwenden wir Leservariablen, um selbstreferenziellen Code zu erstellen.Durch Setzen *PRINT-CIRCLE*von "true" vermeiden wir eine unendliche Rekursion beim Pretty-Printing.Der Trick bei der Verwendung WRITEvon :print-circle Tbesteht darin, dass die Funktion möglicherweise auch den Wert an die REPL zurückgibt, abhängig davon, ob write die letzte Form ist. Wenn die REPL also keine kreisförmigen Strukturen verarbeitet, wie es durch den Standardwert von definiert ist *PRINT-CIRCLE*wird es eine unendliche Rekursion geben. Wir müssen nur sicherstellen, dass die kreisförmige Struktur nicht an die REPL zurückgegeben wird, deshalb gibt es eine NIL an der letzten Position des LET. Dieser Ansatz reduziert das Problem erheblich.

Core-Dump
quelle
Sieht gut aus! Das (y or n)ist nicht erforderlich, aber ich bin versucht, es zuzulassen, da es eine Verbesserung ist.
abligh
@abligh Danke. Über j / n wäre das nett, es hilft und IMHO ist dies nicht wirklich im Widerspruch zu # 3, bei dem es darum geht, das Verkürzen der Eingabeaufforderungen zu vermeiden.
Coredump
9

Python 2.7.6, 820 728 Bytes

(Könnte an verschiedenen Versionen arbeiten, aber ich bin mir nicht sicher)

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)

Nun, es ist nicht so kurz wie die Common-Lisp-Antwort, aber hier ist ein Code!

Blau
quelle
4

Python 3, 544 Bytes

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)

Probieren Sie es online!

Die Fragen / Antworten / Antworten werden in einem Array gespeichert. Wenn das Array drei Elemente (z. B. ['Does it eat ants',['a pangolin'],['a dog']]) speichert , erhält es eine Antwort auf die Frage und wird je nach Antwort nur mit dem Inhalt des zweiten oder dritten Elements wiederholt. Wenn es zu einem Array mit nur einem Element kommt, stellt es die Frage, und da sein gesamter Quellcode eine Zeichenfolge enthält, kann es die Split-Join-Methode verwenden, um die Erweiterung in das Array einzufügen, um den neuen Zweig hinzuzufügen .

Ursprünglich schrieb ich dies, ohne die Anforderungen an das Quine-Format zu erfüllen. Daher war es schwierig, die Frage erneut zu lesen und einen Weg zu finden, wie ich Code ausführen und als Zeichenfolge verwenden kann. Schließlich stieß ich auf die Idee des erweiterbaren Quine-Formats:

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

Python 3 , 497 Bytes

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)

Ganz ähnlich wie die Antwort von Harmless für die Baumdarstellung. Die nächste Frage wird rekursiv gestellt, während die Liste vertieft wird, bis nur noch eine Antwort vorliegt.

Ungolfed version (ohne quining)

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)
Matthew Jensen
quelle