Код гольфу: розв’яжіть логічну задачу Knights and Knaves шляхом розбору англійської мови


16

Фон

Є двоє людей, Білл та Джон. Один з них - лицар, який завжди говорить правду, а другий - монастир, який завжди говорить брехню. Ви не знаєте, хто є лицарем, а хто - монастирем. Кожна людина потім говорить кілька тверджень про те, хто є лицарем, а хто - лицарем. Використовуючи цю інформацію, ви повинні дійти висновку щодо того, хто є лицарем, а хто - монастирем.

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

Джон каже: "І я, і монахиня, і Білл - монаха". Якби Джон був лицарем, то це твердження було б помилковим, тому він не може бути лицарем. Якби він був лицарем, а Білл був лицарем, це твердження все одно було б помилковим, навіть думав, що перша частина правдива. Отже, Іван - монастир.

Змагання

Ваше завдання полягає в тому, щоб написати найкоротшу можливу програму, яка складе список заяв, зроблених кожною людиною, і з'ясує, хто є києм, а хто - лицарем. Існує багато деталей, які слід висвітлити, тому ця проблема описана в трьох розділах.

Вхідні дані

Вхід буде двома рядками, а за ними новий рядок. У кожному рядку буде вказано ім’я одного з персонажів, а потім двокрапка, а потім кілька речень, сказаних цією особою. Якщо одна людина - лицар, то всі його вироки будуть істинними, і всі вироки чнака будуть хибними. Перша літера речення завжди буде написана з великої літери, а кожне речення закінчується періодом. Ось приклад:

Joe: Both I am a knight and neither Steve is a knave nor I am a knave.
Steve: Joe is a knave. Either Joe is a knight or I am a knight.

Розбір

Кожне речення складається щонайменше з одного речення. Кожен пункт містить одну з кількох речей (сподіваємось, ви можете зрозуміти моє позначення):

both [clause] and [clause]
either [clause] or [clause]
neither [clause] nor [clause]
[I am | (other person's name) is] a [knight | knave]

Це неоднозначне, тому що це можна зрозуміти таким чином, як польське позначення. Ось приклад речення:

Both I am a knight and neither Steve is a knave nor I am a knave.

Переклад алгебри Булева простий. Висловлювання "обидва" - це AND, "або" - "XOR", а "ні" - NOR.

(I am a knight) AND ((Steve is a knave) NOR (I am a knave))

Вихідні дані

Вихід буде складатися з двох рядків. Кожен рядок складається з імені людини (по порядку), а потім говорить, чи він лицар чи монастир. Завжди знайдеться один лицар та один монастир. Ось результат для наведеного вище прикладу:

Joe is the knave.
Steve is the knight.

Якщо проблема нерозв'язна (або ви не можете сказати, хто що, або немає рішення), тоді ваша програма може зробити все, ЩО ВЗАЄМО, щоб отримати дійсний вихід.

Більше прикладів

Вхідні дані

Sir Lancelot: Either both I am a knight and Merlin is a knave or both I am a knave and Merlin is a knight.
Merlin: Either both I am a knight and Sir Lancelot is a knight or both I am a knave and Sir Lancelot is a knave.

Вихідні дані

Sir Lancelot is the knight.
Merlin is the knave.

Вхідні дані

David: Neither I am a knave nor Patrick is a knight. Either I am a knight or Patrick is a knave.
Patrick: Either I am a knight or both I am a knight and David is a knight.

Вихідні дані

David is the knave.
Patrick is the knight.

Вхідні дані

Lizard: I am a knight.
Spock: I am a knave.

Один можливий вихід

Rock Paper Scissors

Правила, положення та примітки

  1. Діють стандартні правила гольф-коду
  2. Ваша програма повинна складатися лише з друкованого ASCII
  3. Усі вхідні та вихідні дані будуть від STDIN та STDOUT

Що щодо чутливості до справ? Ваш опис синтаксису - малі, ваші приклади - великі. Чи є нечутливістю випадку обставина?
угорен

Перша літера кожного слова буде написана з великої літери, окрім того, що будуть використані лише великі літери. Яку конкретну проблему ви бачите?
PhiNotPi

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

Відповіді:


6

Пітона, 491 символів

Працює, перетворюючи кожен рядок у вираз Python та еволюціонуючи його.
Кнав і лицар оцінюються як 0 і 1. Для двох людей ми спробуємо обидва варіанти.
Наприклад, Joe: Steve is a knaveстає Joe==(Steve==knave). Таким чином, якщо Joeце клав, результат вірний лише в тому випадку, якщо він бреше.
Ви отримуєте некрасиві помилки, коли це неможливо чи не вирішено. Якщо неможливо r[0]- помилка індексу, оскільки rвона порожня. Якщо неможливо визначити, r[1:]приєднання до списку рядків викликає проблеми.

import sys
def p():
    a=s.pop(0)
    try:return{"both":"(%s*%s)","either":"(%s|%s)","neither":"(1-(%s|%s))"}[a.lower()]%(p(),p())
    except KeyError:r=s[2];del s[:4];return"(%s==%s)"%((a,m)[a=="I"],r)
x=[];w=[]
for l in sys.stdin:
    m,l=l.split(":");w+=[m]
    for s in l.split("."):
        s=s.split()
        while s:x+=["%s==%s"%(m,p())]
k=("knave","knight")
r=[a for a in[{w[0]:z,w[1]:1-z}for z in(0,1)]if all(eval(l,{k[0]:0,k[1]:1},a)for l in x)]
print"\n".join(x+" is the "+k[r[0][x]]+"."for x in w+r[1:])

3

Рубі, 352 символи

Рішення стало досить довгим, тому, можливо, все ще знайдеться місце для гольфу. Він вимагає, щоб дані були добре сформовані (як і всі приклади, наведені вище, але не намагайтеся назвати людину "Обидва" ...).

q=->{gets.split /: |\.\s/}
C,*E=q[]
D,*F=q[]
r=->c,m{t=c.shift;t[1]?(x=r[c,m];c.shift;y=r[c,m];t[0]==?B?x&y :t[0]==?E?x^y :1-(x|y)):(c.shift(2);h=c.shift[2]==?I?m : 1-m;t==?I?h :1-h)}
s=->u,v,b{u.map{|c|r[c.gsub(v,?J).upcase.split,b]==b}.all?}
t=->b{s[E,D,b]&&s[F,C,1-b]}
u=->v,b{v+" is the kn#{b ?:ight: :ave}."}
puts t[1]^t[0]&&u[C,t[1]]+$/+u[D,t[0]]

Здається, ви залишаєте періоди у виході, але це здається виправленням двох символів.
PhiNotPi

1
@PhiNotPi Готово. Було виправлено нульовий характер ...
Говард

0

Perl - 483 байти

(($a,$b),($c,$d))=map{split':'}@ARGV;$h='y';$i='x';$s=' is the kn';$g='ight.';$v='ave.';for($b,$d){$_.=' 1';s/ am a | is a /==/g;s/knight/1)/g;s/knave/0)/g;s/I=/(\$$i=/g;s/($a|$c)=/(\$$h=/g;s/([^.]+)\./($1)and/g;s/ or / xor /g;s/ nor / or /g;while(s/(?<= )(\w+ \((?:[^()]+|(?1))\) \w+ \((?:[^()]+|(?1))\))/($1)/g){}s/neither/!/gi;s/both|either//gi;$h=$i++}$x=0;$y=1;$k=!eval($b)&&eval($d);$x=$y--;$n=!eval($d)&&eval($b);print"$a$s$v
$c$s$g"if($k&&!$n);print"$a$s$g
$c$s$v"if($n&&!$k)

Подібно до рішення Python. Він зменшує речення до коду Perl, а потім evalїх. Він може надрукувати майже допустимий вихід, якщо вхід дивний, але нічого не друкує, якщо це не можна визначити. Добре сформований вхід працює як очікувалося. Речення подаються в командному рядку всередині лапок, і спеціальні прапори не потрібні.

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