Zmutowana łuskowiec

28

Jest to wyzwanie polegające na kodzie golfowym, w ramach którego musisz opracować program, który działa jak quine lub quine, która modyfikuje się w celu zilustrowania uczenia maszynowego.

tło

Jest to podstawowy program sztucznej inteligencji o nazwie „gra łuskowiec”, który jest opisany tutaj . Podstawowe pomysły są takie, że program przy pierwszym uruchomieniu pyta:

OK, proszę coś wymyślić

Czy to łuskowiec?

Następnie możesz odpowiedzieć:

tak

W takim przypadku jest napisane:

Dobry. To było takie proste.

A jeśli nie, to mówi:

O. Więc wygrywasz - o czym myślałeś?

Do czego możesz powiedzieć:

pies

Do czego by to powiedział

Zadaj mi pytanie dotyczące psa, bym mógł odróżnić psa od łuskowca

możesz odpowiedzieć

Czy to je mrówki?

Następnie zapytałby:

Jaka jest odpowiedź dla psa?

Do czego byś powiedział

Nie

I to by powiedziało

Dzięki

Następnym razem, gdy uruchomi się, zadaje powyższe pytanie i tworzy binarne drzewo takich pytań.

Wyzwanie

Dosyć tła. Wyzwanie polega na napisaniu samomodyfikującego się programu łuskowca. Reguły są następujące:

  1. Wyjście programu (jak opisano powyżej) powinno być STDERR. Ostateczna odpowiedź zawsze brzmi: „Dobrze. To było takie proste”. lub „Dzięki”. Następnie powinien wypisać bieżącą wersję programu lub nową wersję programu, która zawiera pytanie STDOUT. Żadna odpowiedź napisana w języku, który nie obsługuje pisania STDOUTi STDERRczytania z, STDINbędzie ważna.

  2. Innymi słowy w systemie UNIX można wywołać program w następujący sposób:

przykład:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. Program musi używać dokładnie podanych podpowiedzi (ponieważ skrócenie podpowiedzi nie pokazuje umiejętności). Podpowiedzi to (bez cudzysłowów i gdzie% s jest podstawiony) w następujący sposób:

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. Kiedy tak / nie oczekując odpowiedzi, Twój program powinien przyjąć ylub yesw każdym razie na „tak”, i nczy now każdym razie na „nie”. To, co zrobisz z niezgodnymi danymi wejściowymi, zależy od Ciebie. Na przykład możesz zdecydować się na odpowiedź, która zaczyna się na „tak” ylub Y„tak”, a wszystko inne na „nie”.

  2. Możesz założyć, że nazwy dostarczonych rzeczy i pytań składają się tylko z liter ASCII, cyfr, spacji, łączników, znaków zapytania, przecinków, kropek, dwukropków i średników, tzn ^[-?,.;: a-zA-Z]+$. Pasują one do wyrażenia regularnego . Jeśli potrafisz sobie z tym poradzić (zwłaszcza cytowane znaki w wybranym języku), możesz być zadowolony z siebie, ale nie zdobywaj żadnych dodatkowych punktów.

  3. Twój program nie może odczytać lub zapisać dowolny plik (z wyjątkiem STDIN, STDOUTi STDERR), lub z sieci; w szczególności nie może czytać ani zapisywać własnego kodu z dysku. Jego stan należy zapisać w samym kodzie programu.

  4. Kiedy program jest uruchomiony i poprawnie zgaduje odpowiedź, musi działać dokładnie tak, jak quine, tj. Musi pisać STDOUTdokładnie do własnego kodu, bez zmian.

  5. Gdy program jest uruchamiany i niepoprawnie zgaduje odpowiedź, musi zakodować podane nowe pytanie i odpowiedź w swoim własnym kodzie i zapisać go STDOUTwe własnym kodzie, aby był w stanie rozróżnić pierwotne przypuszczenie i podany nowy obiekt w dodatek do rozróżnienia wszystkich wcześniej podanych obiektów.

  6. Musisz być w stanie poradzić sobie z wieloma sekwencyjnymi uruchomieniami oprogramowania, aby uczyło się ono o wielu obiektach. Zobacz tutaj przykłady wielu przebiegów.

  7. Przebiegi testowe są podane na łączu w głowie (oczywiście obejmujące tylko okno dialogowe STDINi STDERR).

  8. Standardowe luki są wykluczone.

w płomieniach
źródło
Czy program powinien być w stanie mutować wiele razy i wspierać więcej niż 2 zwierzęta? Jeśli tak, czy możesz podać przykładowy dialog „Proszę o pytanie…”, gdy program zna już dwa lub więcej zwierząt?
Cristian Lupascu,
Co się stanie, jeśli użytkownik powie tylko „pies” zamiast „pies”? Czy powinniśmy przeanalizować zdanie w celu wykrycia „an / an”, czy też możemy potraktować odpowiedź dosłownie? Zakładam, że biorąc pod uwagę podane przez ciebie podpowiedzi (% s).
coredump
1
@coredump, jeśli użytkownik powie „pies”, a nie „pies”, wówczas odpowiedzi nie będą gramatyczne. To nie jest problem.
abligh
1
Nie. Próba zrobienia tego w Runicznej byłoby koszmarem. Głównym powodem jest to, że okablowanie wszystkich bitów, aby poradzić sobie z dowolnymi ciągami wejściowymi (które muszą być obecne jako literały ciągów w wynikowym programie wyjściowym) byłoby zasadniczo niemożliwe. Aha i Runiczna nie może wyprowadzać danych do STDERR.
Draco18s
1
To wydawało się zabawną „grą”, więc zamiast gry w golfa, stworzyłem codepen, w którym możesz grać w grę Pangolin według własnego uznania . Cieszyć się!
Skidsdev

Odpowiedzi:

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

Przykładowa sesja

Nazwij skrypt pango1.lispi uruchom go w następujący sposób (używając 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

Kolejna runda, dodając niedźwiedzia:

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

Dodanie lenistwa (testujemy przypadek, w którym odpowiedź brzmi „nie”):

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

Testowanie ostatniego pliku:

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

Uwagi

  • Najpierw zapomniałem o drukowaniu "Thanks", oto jest.
  • Jak widać po pytaniach następują pytania (y or n), ponieważ używam istniejącej y-or-n-pfunkcji. W razie potrzeby mogę zaktualizować odpowiedź, aby usunąć to wyjście.
  • Common Lisp ma dwukierunkowy *QUERY-IO*strumień poświęcony interakcjom użytkowników, z czego właśnie tu korzystam. Standardowa wydajność i interakcja z użytkownikiem nie psują się, co podąża za duchem pytania IMHO.
  • Korzystanie SAVE-LISP-AND-DIEbyłoby lepszym podejściem w praktyce.

Wygenerowana produkcja

Oto ostatni wygenerowany skrypt:

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

Objaśnienia

Drzewem decyzyjnym może być:

  • ciąg, "a pangolin"który reprezentuje liść.
  • lista trzech elementów: (question if-true if-false)gdzie questionjest zamknięte pytanie typu tak / nie , if-truei if-falsejest dwoma możliwymi poddrzewami związanymi z pytaniem.

UFunkcja spacery i zwraca się ewentualnie zmodyfikowane drzewo. Każde pytanie zadawane jest kolejno, od korzenia aż do liścia, podczas interakcji z użytkownikiem.

  • Zwrócona wartość dla węzła pośredniego (Q Y N)to (Q (U Y) N)(odpowiednio (Q Y (U N))), jeśli odpowiedź na pytanie Qbrzmi „ tak” (odpowiednio. Nie ).

  • Zwróconą wartością liścia jest albo sam liść, jeśli program poprawnie odgadł odpowiedź, albo wyrafinowane drzewo, w którym liść jest zastąpiony pytaniem i dwoma możliwymi wynikami, zgodnie z wartościami pobranymi od użytkownika.

Ta część była raczej prosta. Aby wydrukować kod źródłowy, używamy zmiennych czytnika do budowania kodu referencyjnego.Ustawiając *PRINT-CIRCLE*na true, unikamy nieskończonej rekurencji podczas ładnego drukowania.Sztuką przy użyciu WRITEz :print-circle Tjest to, że funkcja może również zwrócić wartość do REPL, w zależności od tego, czy zapis jest ostatnią formą, a zatem, jeśli REPL nie obsługuje struktur kołowych, tak jak to jest zdefiniowane przez standardową wartość domyślną *PRINT-CIRCLE*nastąpi nieskończona rekurencja. Musimy tylko upewnić się, że struktura kołowa nie zostanie zwrócona do REPL, dlatego w ostatniej pozycji LET znajduje się NIL. Takie podejście znacznie zmniejsza problem.

rdzeń rdzeniowy
źródło
Wygląda dobrze! Nie (y or n)jest to wymagane, ale kusi mnie, aby na to pozwolić, ponieważ jest to poprawa.
abligh
@abligh Thanks. O tak, byłoby fajnie, pomaga i IMHO nie jest to tak naprawdę sprzeczne z numerem 3, który polega na unikaniu skracania podpowiedzi.
coredump
9

Python 2.7.6, 820 728 bajtów

(Może działać na różnych wersjach, ale nie jestem pewien)

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)

Cóż, nie jest tak krótki jak odpowiedź Common Lisp, ale oto kod!

niebieski
źródło
4

Python 3, 544 bajty

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)

Wypróbuj online!

Pytania / odpowiedzi / odpowiedzi są przechowywane w tablicy, gdzie jeśli tablica przechowuje trzy elementy (np. ['Does it eat ants',['a pangolin'],['a dog']]), Wówczas otrzymuje odpowiedź na pytanie i powtarza się z zawartością drugiego lub trzeciego elementu, w zależności od odpowiedzi. Gdy dojdzie do tablicy zawierającej tylko jeden element, zadaje pytanie, a ponieważ ma cały kod źródłowy jako ciąg, może użyć metody split-join do wstawienia rozszerzenia do tablicy w celu dodania nowej gałęzi .

Oryginalnie napisałem to, nie zdając sobie sprawy z wymogu quine, więc ponowne przeczytanie pytania i znalezienie sposobu, w którym mógłbym zarówno wykonać kod, jak i użyć go jako łańcucha, było trudne, ale ostatecznie natknąłem się na pomysł ładnego, rozszerzalnego formatu quine:

q="""
print("Some actual stuff")
q='q=""'+'"'+q+'""'+'"'+chr(10)+'exec()'
print(q)
"""
exec(q)
Nieszkodliwy
źródło
1

Python 3 , 497 bajtów

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)

Całkiem podobna do nieszkodliwej odpowiedzi na reprezentację drzewa. Rekurencyjnie zadaje następne pytanie, wchodząc głębiej w listę, dopóki nie ma tylko jednej odpowiedzi.

Wersja bez golfa (bez quitingu)

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
źródło