Мутант Ящер


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 животных? Если да, можете ли вы привести пример диалога «Пожалуйста, дайте мне вопрос о ...», когда уже есть два или более животных, о которых программа знает?
Кристиан Лупаску

Что если пользователь говорит только «собака» вместо «собака»? Должны ли мы разобрать предложение, чтобы определить «a / an», или мы можем рассматривать ответ буквально? Я полагаю, что с учетом подсказок, которые вы дали (% s).
coredump

1
@coredump, если пользователь говорит «собака», а не «собака», то ответы не будут грамматическими. Это не проблема.
abligh

1
Уф. Попытка сделать это в Руни было бы кошмаром. Основная причина в том, что подключение всех битов для обработки произвольных входных строк (которые затем должны присутствовать в виде строковых литералов в результирующей выходной программе) было бы в принципе невозможно. Ох, и Рунический не может выводить в STDERR.
Draco18s

1
Это выглядело как забавная «игра», поэтому вместо игры в гольф я сделал кодовую ручку, где вы можете играть в игру « Ящер», как душе угодно. Наслаждайтесь!
Скидсдев

Ответы:


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 Спасибо. Насчет y / n, это было бы неплохо, это помогает, и ИМХО, на самом деле это не противоречит # 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

Python 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:

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.