Pangolín mutante

28

Este es un desafío de código de golf en el que debe diseñar un programa que actúe como una quine o una quine que se modifique para ilustrar el aprendizaje automático.

Fondo

Hay un programa básico de inteligencia artificial llamado 'el juego de pangolín' que se describe aquí . La idea básica es que el programa cuando se ejecuta por primera vez pregunta:

OK, por favor piensa en algo

¿Es un pangolín?

Luego puede responder:

En cuyo caso dice:

Bueno. Eso fue muuuy fácil.

O si no dice:

Oh. Bueno, entonces ganas. ¿En qué estabas pensando?

A lo que podrías decir:

un perro

A lo que diría

Por favor, háganme una pregunta sobre un perro, para que pueda distinguir entre un perro y un pangolín.

podrías responder

¿Come hormigas?

Luego preguntaría:

¿Cuál es la respuesta para un perro?

A lo que dirías

no

Y diría

Gracias

La próxima vez que se ejecute, haría la pregunta anterior y generaría un árbol binario de tales preguntas.

El reto

Basta de antecedentes. Este desafío es escribir un programa de pangolín auto modificable. Las reglas son las siguientes:

  1. La salida del programa (como se describió anteriormente) debe ser a STDERR. La respuesta final siempre será "Bien. Eso fue muuuy fácil". o "gracias". Después de esto, debería mostrar la versión actual del programa o una nueva versión del programa que incorpore la pregunta STDOUT. No será válida ninguna respuesta escrita en un idioma que no admita escribir STDOUTy STDERRleer STDIN.

  2. En otras palabras, bajo UNIX podría invocar el programa de esta manera:

ejemplo:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. El programa tiene que usar exactamente las indicaciones especificadas (porque acortar las indicaciones no muestra habilidad). Las indicaciones son (sin las comillas, y donde se sustituye% s) de la siguiente manera:

lista:

"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. Al esperar respuestas sí / no, su programa debe aceptar yo, yesen cualquier caso, 'sí', y / no noen cualquier caso 'no'. Lo que haga con entradas no conformes depende de usted. Por ejemplo, puede decidir tomar cualquier respuesta que comience con yo Ycomo 'sí', y cualquier otra cosa como no.

  2. Puede suponer que los nombres de los elementos suministrados y las preguntas consisten solo en letras ASCII, números, espacios, guiones, signos de interrogación, comas, puntos, puntos y punto y coma, es decir, coinciden con la siguiente expresión regular ^[-?,.;: a-zA-Z]+$. Si puede hacer frente a algo más que eso (especialmente los caracteres entre comillas en el idioma elegido) puede ser presumido, pero no gana ningún punto extra.

  3. Su programa no puede leer o escribir en cualquier archivo (excluyendo STDIN, STDOUTy STDERR), o desde la red; específicamente no puede leer ni escribir su propio código desde el disco. Su estado debe guardarse en el propio código del programa.

  4. Cuando se ejecuta el programa y adivina la respuesta correctamente, debe funcionar exactamente como una quine, es decir, debe escribir STDOUTexactamente su propio código, sin cambios.

  5. Cuando el programa se ejecuta y adivina la respuesta incorrectamente, debe codificar la nueva pregunta y respuesta proporcionadas dentro de su propio código y escribirla STDOUTen su propio código, por lo que es capaz de distinguir entre su conjetura original y el nuevo objeto proporcionado. Además de distinguir entre todos los objetos dados previamente.

  6. Debe poder hacer frente a múltiples ejecuciones secuenciales del software para que aprenda sobre muchos objetos. Consulte aquí para ver ejemplos de ejecuciones múltiples.

  7. Las pruebas se ejecutan en el enlace en la cabecera (obviamente, solo cubren el diálogo STDINy STDERR).

  8. Las lagunas estándar están excluidas.

abligh
fuente
¿Debería el programa poder mutar varias veces y soportar más de 2 animales? Si es así, ¿puede dar un ejemplo del diálogo "Por favor, hágame una pregunta sobre ..." cuando ya hay dos o más animales que el programa conoce?
Cristian Lupascu
¿Qué sucede si el usuario solo dice "perro" en lugar de "un perro"? ¿Analizamos la oración para detectar "a / an" o podemos tratar la respuesta literalmente? Supongo que sí, dadas las indicaciones que diste (% s).
coredump
1
@coredump si el usuario dice "perro" no "un perro", entonces las respuestas no serán gramaticales. Eso no es un problema.
abligh
1
Oof Intentar hacer esto en Runic sería una pesadilla. La razón principal es que conectar todos los bits para hacer frente a cadenas de entrada arbitrarias (que luego deben estar presentes como literales de cadena en el programa de salida resultante) sería básicamente imposible. Ah, y Runic no puede enviar a STDERR.
Draco18s
1
Esto parecía un "juego" divertido, así que en lugar de jugarlo al golf, hice un codepen donde puedes jugar Pangolin Game a tu gusto . ¡Disfrutar!
Skidsdev

Respuestas:

20

Lisp común, 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)()))

Sesión de ejemplo

Asigne un nombre al script pango1.lispy ejecútelo de la siguiente manera (usando 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

Otra ronda, agregando el oso:

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

Agregar un perezoso (probamos el caso donde la respuesta es "no"):

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

Probar el último archivo:

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

Observaciones

  • Primero olvidé imprimir "Thanks", aquí está.
  • Como puede ver, las preguntas son seguidas por (y or n), porque estoy usando la y-or-n-pfunción existente . Puedo actualizar la respuesta para eliminar esta salida si es necesario.
  • Common Lisp tiene un *QUERY-IO*flujo bidireccional dedicado a la interacción del usuario, que es lo que estoy usando aquí. La salida estándar y la interacción del usuario no se complican, lo que sigue en mi humilde opinión el espíritu de la pregunta.
  • Usar SAVE-LISP-AND-DIEsería un mejor enfoque en la práctica.

Salida generada

Aquí está el último script generado:

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

Explicaciones

Un árbol de decisión puede ser:

  • una cadena, como "a pangolin", que representa una hoja.
  • Una lista de tres elementos: (question if-true if-false)donde questiones una pregunta cerrada sí / no , como una cadena, if-truey if-falseson los dos posibles subárboles asociados con la pregunta.

La Ufunción camina y devuelve un árbol posiblemente modificado. Cada pregunta se hace a su vez, comenzando desde la raíz hasta llegar a una hoja, mientras interactúa con el usuario.

  • El valor devuelto para un nodo intermedio (Q Y N)es (Q (U Y) N)(resp. (Q Y (U N))) Si la respuesta a la pregunta Qes (resp. No ).

  • El valor devuelto para una hoja es la hoja misma, si el programa adivinó correctamente la respuesta, o un árbol refinado donde la hoja se reemplaza por una pregunta y dos resultados posibles, de acuerdo con los valores tomados del usuario.

Esta parte fue bastante sencilla. Para imprimir el código fuente, utilizamos variables de lectura para construir código autorreferencial.Al establecerlo *PRINT-CIRCLE*en verdadero, evitamos la recursión infinita durante la impresión bonita.El truco cuando se usa WRITEcon :print-circle Tes que la función también puede devolver el valor a REPL, dependiendo de si escribir es la última forma, y ​​si REPL no maneja estructuras circulares, como se define por el valor predeterminado estándar de *PRINT-CIRCLE*, habrá una recursión infinita. Solo necesitamos asegurarnos de que la estructura circular no se devuelva al REPL, por eso hay un NIL en la última posición del LET. Este enfoque reduce en gran medida el problema.

volcado de memoria
fuente
¡Se ve bien! No (y or n)es obligatorio, pero estoy tentado a permitirlo, ya que es una mejora.
Abligh
@abligh Gracias. Sobre y / n, eso sería bueno, ayuda y en mi humilde opinión, esto no está realmente en contradicción con el n. ° 3, que trata de evitar acortar las indicaciones.
coredump
9

Python 2.7.6, 820 728 bytes

(Podría funcionar en diferentes versiones pero no estoy seguro)

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)

Bueno, no es tan corto como la respuesta de Common Lisp, ¡pero aquí hay un código!

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

Pruébalo en línea!

Las preguntas / respuestas / respuestas se almacenan en una matriz, donde si la matriz almacena tres elementos (por ejemplo ['Does it eat ants',['a pangolin'],['a dog']]), obtiene una respuesta a la pregunta y se repite solo con el contenido del segundo o tercer elemento, dependiendo de la respuesta. Cuando llega a una matriz con solo un elemento, hace la pregunta, y dado que tiene todo su código fuente como una cadena, puede usar el método de unión dividida para insertar la extensión en la matriz para agregar la nueva rama .

Originalmente escribí esto sin darme cuenta del requisito de quine, por lo que volver a leer la pregunta y tener que encontrar una manera de poder ejecutar el código y usarlo como una cadena fue difícil, pero finalmente me topé con la idea del buen formato de quine expandible:

q="""
print("Some actual stuff")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec()'
print(q)
"""
exec(q)
Inofensivo
fuente
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)

Muy similar a la respuesta Inofensiva para la representación de árbol. Hace recursivamente la siguiente pregunta, mientras profundiza en la lista, hasta que solo hay una respuesta.

Versión sin golf (sin comillas)

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
fuente