Pangolin mutant

28

Il s'agit d'un défi de code-golf dans lequel vous devez concevoir un programme qui agit comme un quine ou un quine qui se modifie pour illustrer l'apprentissage automatique.

Contexte

Il existe un programme d'intelligence artificielle de base appelé «le jeu du pangolin» qui est décrit ici . L'idée de base est que le programme lors de sa première exécution demande:

OK, pensez à quelque chose

Est-ce un pangolin?

Vous pouvez alors répondre soit:

Oui

Dans ce cas, il dit:

Bien. C'était tellement facile.

Sinon, il dit:

Oh. Eh bien, vous gagnez alors - À quoi pensiez-vous?

À laquelle vous pourriez dire:

un chien

À quoi il dirait

Veuillez me poser une question sur un chien, afin que je puisse faire la différence entre un chien et un pangolin

tu pourrais répondre

Mange-t-il des fourmis?

Il demanderait alors:

Quelle est la réponse pour un chien?

À laquelle vous diriez

non

Et ça dirait

Merci

La prochaine fois qu'il s'exécutera, il posera la question ci-dessus et construira un arbre binaire de ces questions.

Le défi

Assez de fond. Ce défi consiste à écrire un programme de pangolins auto-modifiant. Les règles sont les suivantes:

  1. La sortie du programme (comme décrit ci-dessus) doit être vers STDERR. La réponse finale sera toujours "Bien. C'était tellement facile." ou "Merci". Après cela, il doit sortir soit la version actuelle du programme, soit une nouvelle version du programme qui incorpore la question à STDOUT. Aucune réponse écrite dans une langue qui ne prend pas en charge l'écriture STDOUTet / STDERRou la lecture STDINne sera valide.

  2. En d'autres termes, sous UNIX, vous pouvez appeler le programme comme ceci:

Exemple:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. Le programme doit utiliser exactement les invites spécifiées (car le raccourcissement des invites ne montre aucune compétence). Les invites sont (sans les guillemets et où% s est substitué) comme suit:

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. Lorsque vous attendez des réponses oui / non, votre programme doit accepter you yesdans tous les cas pour «oui» et nou nodans tous les cas pour «non». Ce que vous faites avec des entrées non conformes dépend de vous. Par exemple, vous pourriez décider de prendre n'importe quelle réponse commençant par you Ycomme «oui», et toute autre chose comme non.

  2. Vous pouvez supposer que les noms des éléments fournis et les questions ne comprennent que des lettres ASCII, des chiffres, des espaces, des tirets, des points d'interrogation, des virgules, des points, des deux-points et des points-virgules, c'est-à-dire qu'ils correspondent à l'expression régulière suivante ^[-?,.;: a-zA-Z]+$. Si vous pouvez faire face à plus que cela (en particulier les caractères de citation dans la langue que vous avez choisie), vous devenez suffisant, mais ne gagnez pas de points supplémentaires.

  3. Votre programme ne peut pas lire ou écrire un fichier ( à l' exception STDIN, STDOUTet STDERR), ou du réseau; en particulier, il ne peut ni lire ni écrire son propre code à partir du disque. Son état doit être enregistré dans le code du programme lui-même.

  4. Lorsque le programme est exécuté et suppose la réponse correctement, il doit fonctionner exactement comme une quine, c'est-à-dire qu'il doit écrire STDOUTexactement dans son propre code, inchangé.

  5. Lorsque le programme est exécuté et devine la réponse de manière incorrecte, il doit coder la nouvelle question et la réponse fournies dans son propre code et l'écrire STDOUTdans son propre code, afin qu'il soit capable de faire la distinction entre sa supposition d'origine et le nouvel objet fourni, dans en plus de distinguer entre tous les objets précédemment donnés.

  6. Vous devez être en mesure de faire face à plusieurs exécutions séquentielles du logiciel afin qu'il se renseigne sur de nombreux objets. Voir ici pour des exemples de plusieurs exécutions.

  7. Les tests sont donnés au lien dans la tête (couvrant évidemment uniquement la boîte de dialogue STDINet STDERR).

  8. Les failles standard sont exclues.

abligh
la source
Le programme devrait-il être capable de muter plusieurs fois et de soutenir plus de 2 animaux? Si oui, pouvez-vous donner un exemple de dialogue "Veuillez me poser une question sur ..." lorsqu'il y a déjà deux animaux ou plus que le programme connaît?
Cristian Lupascu
Que faire si l'utilisateur dit seulement "chien" au lieu de "chien"? Allons-nous analyser la phrase pour détecter "a / an" ou pouvons-nous traiter la réponse littéralement? Je suppose que oui étant donné les invites que vous avez données (% s).
coredump
1
@coredump si l'utilisateur dit "chien" et non "chien", les réponses ne seront pas grammaticales. Ce n'est pas un problème.
abligh
1
Oof. Essayer de faire cela dans Runic serait un cauchemar. La principale raison étant que le câblage de tous les bits pour faire face à des chaînes d'entrée arbitraires (qui doivent ensuite être présentes en tant que littéraux de chaîne dans le programme de sortie résultant) serait fondamentalement impossible. Oh et Runic ne peut pas sortir vers STDERR.
Draco18s
1
Cela semblait être un "jeu" amusant, donc plutôt que de jouer au golf, j'ai créé un codepen où vous pouvez jouer au jeu Pangolin à votre guise . Prendre plaisir!
Skidsdev

Réponses:

20

Lisp commun, 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)()))

Exemple de session

Nommez le script pango1.lispet exécutez comme suit (à l'aide de 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

Un autre tour, en ajoutant l'ours:

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

Ajout d'un paresseux (nous testons le cas où la réponse est "non"):

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

Test du dernier fichier:

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

Remarques

  • J'ai d'abord oublié l'impression "Thanks", la voici.
  • Comme vous pouvez le voir, les questions sont suivies (y or n), car j'utilise la y-or-n-pfonction existante . Je peux mettre à jour la réponse pour supprimer cette sortie si nécessaire.
  • Common Lisp a un *QUERY-IO*flux bidirectionnel dédié à l'interaction avec l'utilisateur, c'est ce que j'utilise ici. La sortie standard et l'interaction utilisateur ne gâchent pas, ce qui suit à mon humble avis l'esprit de la question.
  • L'utilisation SAVE-LISP-AND-DIEserait une meilleure approche dans la pratique.

Sortie générée

Voici le dernier script généré:

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

Explications

Un arbre de décision peut être:

  • une chaîne, comme "a pangolin", qui représente une feuille.
  • une liste de trois éléments: (question if-true if-false)questionest une question fermée oui / non , sous forme de chaîne, et if-trueet if-falsesont les deux sous-arbres possibles associés à la question.

La Ufonction marche et retourne un arbre éventuellement modifié. Chaque question est posée tour à tour, depuis la racine jusqu'à atteindre une feuille, tout en interagissant avec l'utilisateur.

  • La valeur retournée pour un nœud intermédiaire (Q Y N)est (Q (U Y) N)(resp. (Q Y (U N))) Si la réponse à la question Qest oui (resp. No ).

  • La valeur retournée pour une feuille est soit la feuille elle-même, si le programme a correctement deviné la réponse, soit un arbre raffiné où la feuille est remplacée par une question et deux résultats possibles, selon les valeurs prises par l'utilisateur.

Cette partie était plutôt simple. Afin d'imprimer le code source, nous utilisons des variables de lecteur pour construire du code auto-référentiel.En définissant la valeur *PRINT-CIRCLE*true, nous évitons la récursion infinie lors de la jolie impression.L'astuce lors de l'utilisation WRITEavec :print-circle Test que la fonction peut également renvoyer la valeur au REPL, selon que l'écriture est la dernière forme, et donc, si le REPL ne gère pas les structures circulaires, comme il est défini par la valeur par défaut standard de *PRINT-CIRCLE*, il y aura une récursion infinie. Nous devons seulement nous assurer que la structure circulaire n'est pas retournée au REPL, c'est pourquoi il y a un NIL dans la dernière position du LET. Cette approche réduit considérablement le problème.

coredump
la source
Cela semble bon! Ce (y or n)n'est pas obligatoire, mais je suis tenté de le permettre car c'est une amélioration.
abligh
@abligh Merci. À propos de y / n, ce serait bien, cela aide et à mon humble avis ce n'est pas vraiment en contradiction avec le n ° 3 qui consiste à éviter de raccourcir les invites.
coredump
9

Python 2.7.6, 820 728 octets

(Pourrait fonctionner sur différentes versions mais je ne suis pas sûr)

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)

Eh bien, ce n'est pas aussi court que la réponse Common Lisp, mais voici du code!

Bleu
la source
4

Python 3, 544 octets

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)

Essayez-le en ligne!

Les questions / réponses / réponses sont stockées dans un tableau, où si le tableau stocke trois éléments (par exemple ['Does it eat ants',['a pangolin'],['a dog']]), il obtient une réponse à la question et se répète avec uniquement le contenu du deuxième ou du troisième élément, selon la réponse. Quand il arrive à un tableau avec un seul élément, il pose la question, et comme il a son code source entier une chaîne, il est capable d'utiliser la méthode de jointure fractionnée pour insérer l'extension dans le tableau afin d'ajouter la nouvelle branche .

À l'origine, j'ai écrit ceci sans réaliser l'exigence de quine, donc relire la question et avoir à trouver un moyen d'exécuter du code et de l'utiliser comme chaîne était difficile, mais j'ai finalement trouvé l'idée d'un joli format de quine extensible:

q="""
print("Some actual stuff")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec()'
print(q)
"""
exec(q)
Inoffensif
la source
1

Python 3 , 497 octets

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)

Assez similaire à la réponse de Harmless pour la représentation des arbres. Il pose récursivement la question suivante, tout en approfondissant la liste, jusqu'à ce qu'il n'y ait qu'une seule réponse.

Version non golfée (sans 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
la source