Мутантний панголін


28

Це виклик для гольфу з кодом, за допомогою якого вам потрібно розробити програму, яка діє як квінка або кволка, яка модифікує себе, щоб ілюструвати машинне навчання.

Фон

Існує основна програма штучного інтелекту під назвою "гра в панголін", яка описана тут . Основні ідеї полягають у тому, що програма при першому запуску запитує:

Добре, будь ласка, придумайте щось

Це панголін?

Ви можете відповісти:

Так

У такому випадку він говорить:

Добре. Це було так легко.

Або якщо ні, це говорить:

Ой. Ну ви виграєте тоді - Що ви думали?

Ви можете сказати:

собака

На яке б це сказало

Будь ласка, дайте мені питання про собаку, щоб я міг сказати різницю між собакою і панголіном

ви можете відповісти

Чи їсть мурах?

Потім він запитав:

Яка відповідь на собаку?

До якого ви б сказали

ні

І сказало б

Спасибі

Наступного разу, коли він запускається, він би задав питання вище, і створив бинарне дерево таких питань.

Змагання

Досить фону. Це завдання полягає в написанні самомодифікуючої програми панголіну. Правила такі:

  1. Вихід програми (як описано вище) повинен бути STDERR. Остаточна відповідь завжди буде "Добре. Це було так легко". або "Спасибі". Після цього він повинен вивести або поточну версію програми, або нову версію програми, яка включає питання до STDOUT. Жодна відповідь, написана мовою, яка не підтримує написання STDOUTта STDERRчитання з неї, STDINне буде дійсною.

  2. Іншими словами, під UNIX ви можете викликати програму так:

приклад:

$ mylanguage myprogram > myprogram.1
[dialog goes here]
$ mylanguage myprogram1 > myprogram.2
[dialog goes here]
  1. Програма повинна використовувати саме вказані підказки (оскільки скорочення підказок не виявляє навичок). Підказки (без лапок і де% s заміщені) наступні:

список:

"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. Очікуючи відповідей "так / ні", ваша програма повинна прийняти yабо yesв будь-якому випадку "так", nабо noв будь-якому випадку "ні". Що ви робите з невідповідними входами, залежить від вас. Наприклад , ви можете вирішити , прийняти будь-яку відповідь , який починається з yабо Yяк «так», і нічого іншого , як немає.

  2. Ви можете припустити, що назви поставлених речей та запитань складаються лише з літер ASCII, цифр, пробілів, дефісів, знаків запитань, коми, крапки, колонки та крапки з комою, тобто вони відповідають наступному регулярному вираженню ^[-?,.;: a-zA-Z]+$. Якщо ви зможете впоратися з більш ніж цим (особливо цитування символів на обраній вами мові), ви можете бути саморобним, але не набирайте зайвих балів.

  3. Ваша програма не може прочитати або записати будь-який файл ( за винятком STDIN, STDOUTі STDERR), або з мережі; зокрема він не може ні читати, ні записувати власний код з диска. Її стан необхідно зберегти в самому програмному коді.

  4. Коли програма запускається і правильно відгадує відповідь, вона повинна виконуватись точно як квінт, тобто вона повинна записувати в STDOUTточно свій код, без змін.

  5. Коли програма запускається і неправильно відгадує відповідь, вона повинна кодувати надане нове запитання та відповідь у своєму власному коді та записувати його STDOUTу свій власний код, щоб вона могла розрізнити оригінальну здогадку та наданий новий об’єкт у крім розрізнення всіх раніше заданих об’єктів.

  6. Ви повинні вміти справлятися з декількома послідовними запусками програмного забезпечення, щоб вони дізналися про багато об'єктів. Дивіться тут приклади декількох прогонів.

  7. Тестові запуски наведені за посиланням у голові (очевидно, що охоплює лише діалогове вікно STDINта STDERRдіалог).

  8. Стандартні лазівки виключаються.


Чи повинна програма мутувати кілька разів мутувати та підтримувати понад 2 тварин? Якщо так, чи можете ви навести приклад діалогу "Будь ласка, дайте мені питання про ...", коли вже є дві або більше тварин, про які програма знає?
Крістіан Лупаску

Що робити, якщо користувач скаже лише "собака" замість "собака"? Чи повинен ми розібрати речення, щоб виявити "а / а" чи можемо ми відповісти буквально? Я вважаю, що враховуючи підказки, які ви дали (% s).
coredump

1
@coredump, якщо користувач каже "собака", а не "собака", відповіді не будуть граматичними. Це не проблема.
abligh

1
Вихід. Спроба зробити це в Руніку було б кошмаром. Основна причина полягає в тому, що з'єднання всіх бітів, щоб справлятися з довільними рядками введення (які потім повинні бути представлені як рядкові літерали в отриманій програмі виводу), було б в основному неможливим. Ох і Runic не можуть вивести на STDERR.
Draco18s

1
Це здавалося веселою "грою", тому замість того, щоб гольф, я створив кодек, де можна пограти в ігри в Панголін до душі. Насолоджуйтесь!
Skidsdev

Відповіді:


20

Лисп звичайний, 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)()))

Приклад сеансу

Назвіть сценарій pango1.lispта виконайте так (використовуючи 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

Ще один раунд, додавши ведмедя:

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

Додаючи лінь (ми перевіряємо випадок, коли відповідь "ні"):

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

Тестування останнього файлу:

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

Зауваження

  • Я перший забув друк "Thanks", ось він.
  • Як бачимо, подаються питання (y or n), тому що я використовую існуючу y-or-n-pфункцію. Я можу оновити відповідь, щоб видалити цей вихід, якщо потрібно.
  • Common Lisp має двонаправлений *QUERY-IO*потік, присвячений взаємодії з користувачем, що я тут використовую. Стандартний вихід і взаємодія з користувачем не псуються, що випливає із духу питання ІМХО.
  • Використання SAVE-LISP-AND-DIEбуло б кращим підходом на практиці.

Згенерований вихід

Ось останній створений сценарій:

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

Пояснення

Деревом рішень може бути:

  • рядок, як "a pangolin", що представляє лист.
  • список з трьох елементів: (question if-true if-false)де questionзакрите питання " так / ні" , як рядок, і if-trueчи if-falseє два можливі підрядки, пов'язані з питанням.

UФункція ходить і повертає в можливо модифіковане дерево. Кожне питання задається по черзі, починаючи від кореня до досягнення листа, під час взаємодії з користувачем.

  • Повернене значення для проміжного вузла (Q Y N)- (Q (U Y) N)(відповідно (Q Y (U N))), якщо відповідь на питання Q- так (відповідно, ні ).

  • Повернене значення для листа - це сам лист, якщо програма правильно відгадала відповідь, або вдосконалене дерево, де лист замінюється питанням та двома можливими результатами, відповідно до значень, взятих у користувача.

Ця частина була досить відвертою. Для того, щоб надрукувати вихідний код, ми використовуємо змінні зчитувача для побудови коду, що референсує.Встановлюючи *PRINT-CIRCLE*значення true, ми уникаємо нескінченних рекурсій під час друку.Підступ при використанні WRITEз :print-circle Tполягає в тому, що функція також може повернути значення в REPL, залежно від того, чи є запис останньою формою, і так, якщо REPL не обробляє кругові структури, як це визначено стандартним значенням за замовчуванням *PRINT-CIRCLE*, відбудеться нескінченна рекурсія. Нам потрібно лише переконатися, що кругла структура не повертається до REPL, тому в останньому положенні LET є NIL. Такий підхід значно зменшує проблему.


Виглядає добре! Це (y or n)не потрібно, але я намагаюся дозволити це, оскільки це є поліпшенням.
abligh

@abligh Дякую Щодо г / п, це було б добре, це допомагає та ІМХО це насправді не суперечить # 3, що стосується уникнення скорочення підказок.
coredump

9

Python 2.7.6, 820 728 байт

(Можливо, працюю в різних версіях, але я не впевнений)

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)

Ну, це не так коротко, як відповідь Common Lisp, але ось якийсь код!


4

Пітон 3, 544 байти

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)

Спробуйте в Інтернеті!

Запитання / відповіді / відповіді зберігаються в масиві, де, якщо масив зберігає три елементи (наприклад ['Does it eat ants',['a pangolin'],['a dog']]), то він отримує відповідь на питання і повторює лише вміст другого або третього елемента, залежно від відповіді. Коли він потрапляє до масиву лише з одним елементом, він задає питання, а оскільки у нього є весь вихідний код, як рядок, він може використовувати метод split-join для вставки розширення до масиву, щоб додати нову гілку .

Спочатку я писав це, не усвідомлюючи вимоги quine, тому перечитати питання і потрібно було знайти спосіб, щоб я міг виконувати код і використовувати його як рядок було складно, але врешті-решт я натрапив на ідею приємного розширюваного формату quine:

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

1

Python 3 , 497 байт

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)

Досить схожий на відповідь "Безшкідливий" для представлення дерева. Він рекурсивно задає наступне питання, заглиблюючись у список, поки не знайдеться лише одна відповідь.

Версія без вогків (без квітації)

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.