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.


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.


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!


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)

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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.