Pangolina mutante


28

Questa è una sfida di code-golf in base alla quale è necessario ideare un programma che si comporta come un quine o un quine che si modifica per illustrare l'apprendimento automatico.

sfondo

Esiste un programma base di intelligenza artificiale chiamato "il gioco del pangolino" che è descritto qui . L'idea di base è che il programma alla prima esecuzione chieda:

OK, per favore, pensa a qualcosa

È un pangolino?

È quindi possibile rispondere a:

Nel qual caso si dice:

Buono. È stato davvero facile.

O in caso contrario si dice:

Oh. Bene, allora vinci - A cosa stavi pensando?

A cui potresti dire:

un cane

A cui direbbe

Per favore, fammi una domanda su un cane, così posso dire la differenza tra un cane e un pangolino

potresti rispondere

Mangia le formiche?

Quindi chiederebbe:

Qual è la risposta per un cane?

A cui diresti

no

E direbbe

Grazie

La prossima volta che viene eseguito, pone la domanda sopra e crea un albero binario di tali domande.

La sfida

Basta lo sfondo. Questa sfida è scrivere un programma di pangolino che si modifica da solo. Le regole sono le seguenti:

  1. L'output del programma (come descritto sopra) dovrebbe essere a STDERR. La risposta finale sarà sempre "Bene. È stato davvero facile". o "Grazie". Successivamente, dovrebbe essere visualizzata la versione corrente del programma o una nuova versione del programma che incorpora la domanda STDOUT. Nessuna risposta scritta in una lingua che non supporta la scrittura STDOUTe STDERRo di lettura STDINsarà valida.

  2. In altre parole, in UNIX è possibile richiamare il programma in questo modo:

esempio:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. Il programma deve usare esattamente i prompt specificati (perché accorciare i prompt non mostra abilità). I prompt sono (senza virgolette e dove% s è sostituito) come segue:

elenco:

"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. Quando aspettandosi risposte sì / no, il programma dovrebbe accettare yo yescomunque per 'sì', e no nocomunque per 'no'. Quello che fai con input non conformi dipende da te. Ad esempio, potresti decidere di prendere qualsiasi risposta che inizia con yo Ycome "sì" e qualsiasi altra cosa come no.

  2. Si può presumere che i nomi delle cose fornite e le domande siano costituite solo da lettere, numeri, spazi, trattini, punti interrogativi, virgole, punti e virgola, punti e virgola, cioè ASCII ^[-?,.;: a-zA-Z]+$. Se riesci a farcela con qualcosa di più (in particolare i caratteri di citazione nella lingua che hai scelto) diventerai compiaciuto, ma non guadagnerai punti extra.

  3. Il vostro programma non può leggere o scrivere qualsiasi tipo di file (ad esclusione STDIN, STDOUTe STDERR), o dalla rete; in particolare non può né leggere né scrivere il proprio codice dal disco. Il suo stato deve essere salvato nel codice del programma stesso.

  4. Quando il programma viene eseguito e indovina correttamente la risposta, deve funzionare esattamente come un quine, ovvero deve scrivere STDOUTesattamente nel proprio codice, invariato.

  5. Quando il programma viene eseguito e indovina la risposta in modo errato, deve codificare la nuova domanda fornita e rispondere nel proprio codice e scriverla STDOUTnel proprio codice, quindi è in grado di distinguere tra la sua ipotesi originale e il nuovo oggetto fornito, in oltre a distinguere tra tutti gli oggetti precedentemente indicati.

  6. Devi essere in grado di far fronte a più esecuzioni sequenziali del software in modo che apprenda su molti oggetti. Vedi qui per esempi di corse multiple.

  7. Le esecuzioni dei test sono riportate nel link in testa (ovviamente copre solo il dialogo STDINe STDERR).

  8. Sono escluse le scappatoie standard .


Il programma dovrebbe essere in grado di mutare più volte e supportare più di 2 animali? In tal caso, puoi fornire un esempio del dialogo "Per favore, fammi una domanda sul ..." quando ci sono già due o più animali di cui il programma è a conoscenza?
Cristian Lupascu,

Che cosa succede se l'utente dice solo "cane" invece di "un cane"? Analizziamo la frase per rilevare "a / an" o possiamo trattare la risposta alla lettera? Presumo così dato i suggerimenti che hai dato (% s).
coredump,

1
@coredump se l'utente dice "cane" non "un cane", le risposte non saranno grammaticali. Questo non è un problema.
circa

1
Oof. Cercare di farlo a Runic sarebbe un incubo. Il motivo principale è che il cablaggio di tutti i bit per far fronte a stringhe di input arbitrarie (che devono quindi essere presenti come valori letterali di stringa nel programma di output risultante) sarebbe sostanzialmente impossibile. Oh e Runic non possono essere inviati a STDERR.
Draco18s

1
Sembrava un "gioco" divertente, quindi piuttosto che giocare a golf, ho creato un codepen in cui puoi giocare a Pangolin Game a tuo piacimento . Godere!
Skidsdev,

Risposte:


20

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

Sessione di esempio

Denominare lo script pango1.lisped eseguire come segue (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

Un altro giro, aggiungendo l'orso:

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

Aggiunta di un bradipo (testiamo il caso in cui la risposta è "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

Test dell'ultimo file:

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

Osservazioni

  • Prima ho dimenticato di stampare "Thanks", eccolo qui.
  • Come puoi vedere, le domande sono seguite (y or n), perché sto usando la y-or-n-pfunzione esistente . Posso aggiornare la risposta per rimuovere questo output se necessario.
  • Common Lisp ha un *QUERY-IO*flusso bidirezionale dedicato all'interazione dell'utente, che è quello che sto usando qui. L'output standard e l'interazione dell'utente non scherzano, il che segue IMHO lo spirito della domanda.
  • L'uso SAVE-LISP-AND-DIEsarebbe un approccio migliore nella pratica.

Uscita generata

Ecco l'ultimo script generato:

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

spiegazioni

Un albero decisionale può essere:

  • una stringa, come "a pangolin", che rappresenta una foglia.
  • un elenco di tre elementi: (question if-true if-false)dove questionè una domanda sì / no chiusa , come una stringa, if-truee if-falsesono i due possibili sottoalberi associati alla domanda.

La Ufunzione cammina e restituisce un albero eventualmente modificato. Ogni domanda viene posta a sua volta, a partire dalla radice fino a raggiungere una foglia, mentre interagisce con l'utente.

  • Il valore restituito per un nodo intermedio (Q Y N)è (Q (U Y) N)(risp. (Q Y (U N))) Se la risposta alla domanda Qè (resp. No ).

  • Il valore restituito per una foglia è o la foglia stessa, se il programma ha indovinato correttamente la risposta, o un albero raffinato in cui la foglia è sostituita da una domanda e due possibili risultati, in base ai valori presi dall'utente.

Questa parte era piuttosto semplice. Per stampare il codice sorgente, utilizziamo le variabili del lettore per creare codice autoreferenziale.Impostando *PRINT-CIRCLE*su true, evitiamo la ricorsione infinita durante la bella stampa.Il trucco quando si utilizza WRITEcon :print-circle Tè che la funzione potrebbe anche restituire il valore a REPL, a seconda che write sia l'ultimo modulo e quindi, se REPL non gestisce strutture circolari, come è definito dal valore predefinito standard di *PRINT-CIRCLE*, ci sarà una ricorsione infinita. Dobbiamo solo assicurarci che la struttura circolare non venga restituita al REPL, ecco perché c'è un NIL nell'ultima posizione della LET. Questo approccio riduce notevolmente il problema.


Sembra buono! Non (y or n)è richiesto, ma sono tentato di permetterlo perché è un miglioramento.
circa

@abligh Grazie. A proposito di y / n, sarebbe bello, aiuta e IMHO questo non è in contraddizione con il n. 3, che consiste nell'evitare di accorciare i prompt.
coredump,

9

Python 2.7.6, 820 728 byte

(Potrebbe funzionare su versioni diverse ma non sono sicuro)

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)

Bene, non è breve come la risposta Common Lisp, ma ecco un po 'di codice!


4

Python 3, 544 byte

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)

Provalo online!

Le domande / risposte / risposte sono memorizzate in un array, dove se l'array memorizza tre elementi (ad es. ['Does it eat ants',['a pangolin'],['a dog']]) Allora ottiene una risposta alla domanda e si ripete con solo il contenuto del secondo o terzo elemento, a seconda della risposta. Quando arriva a un array con un solo elemento, pone la domanda e poiché ha tutti i suoi codici sorgente una stringa è in grado di usare il metodo split-join per inserire l'estensione nell'array al fine di aggiungere il nuovo ramo .

Inizialmente ho scritto questo non realizzando il requisito di Quine, quindi rileggere la domanda e dover trovare un modo per eseguire sia il codice che usarlo come stringa era difficile, ma alla fine mi sono imbattuto nell'idea del bel formato espandibile di Quine:

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

1

Python 3 , 497 byte

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)

Abbastanza simile alla risposta di Harmless per la rappresentazione dell'albero. Fa ricorsivamente la domanda successiva, mentre si approfondisce l'elenco, fino a quando non c'è una sola risposta.

Versione Ungolfed (senza 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.