Code golf: résoudre un problème de logique Knights and Knaves en analysant l'anglais

16

Contexte

Il y a deux personnes, Bill et John. L'un d'eux est un chevalier, qui dit toujours la vérité, et l'autre est un chevalier, qui dit toujours un mensonge. Vous ne savez pas qui est le chevalier et qui est le chevalier. Chaque personne dit ensuite plusieurs déclarations sur qui est le chevalier et qui est le chevalier. En utilisant ces informations, vous devez arriver à une conclusion quant à savoir qui est le chevalier et qui est le chevalier.

Le problème de logique Knights and Knaves est basé sur l'algèbre de Booleen. Les mots qu'une personne dit forment un problème de satisfiabilité de Booleen. Les déclarations du chevalier doivent toujours être fausses et les déclarations de l'autre chevalier doivent toujours être vraies.

John dit "Je suis un mec et Bill est un mec". Si John était le chevalier, alors cette déclaration serait fausse, donc il ne peut pas être le chevalier. S'il était l'escroc et Bill le chevalier, cette affirmation serait toujours fausse, même si la première partie est vraie. Donc, John est le coquin.

Le défi

Votre défi est d'écrire le programme le plus court possible qui prendra une liste de déclarations faites par chaque personne et déterminera qui est le chevalier et qui est le chevalier. Il y a beaucoup de détails à couvrir, donc ce problème est décrit dans trois sections.

Contribution

La saisie se fera sur deux lignes suivies d'une nouvelle ligne. Chaque ligne donnera le nom d'un des caractères, suivi de deux points, suivi de plusieurs phrases prononcées par cette personne. Si une personne est le chevalier, alors toutes ses phrases seront vraies et toutes les phrases du chevalier seront fausses. La première lettre d'une phrase sera toujours en majuscule et chaque phrase se terminera par un point. Voici un exemple:

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.

Analyse

Chaque phrase comprend au moins une clause. Chaque clause contient une ou plusieurs choses (j'espère que vous pouvez comprendre ma notation):

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

Ceci est sans ambiguïté car il peut être compris d'une manière similaire à la notation polonaise. Voici un exemple de phrase:

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

La traduction en algèbre de Booleen est simple. Les instructions "Both" sont des ET, les instructions "Soit" sont des XOR et les instructions "Ni" sont des NOR.

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

Production

La sortie consistera en deux lignes. Chaque ligne se compose du nom d'une personne (dans l'ordre) et indique ensuite s'il est le chevalier ou le valet. Il y aura toujours un chevalier et un chevalier. Voici la sortie de l'exemple ci-dessus:

Joe is the knave.
Steve is the knight.

Si le problème est insoluble (soit vous ne pouvez pas dire qui est quoi, soit il n'y a pas de solution), votre programme peut faire quoi que ce soit SAUF pour produire une sortie valide.

Plus d'exemples

Contribution

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.

Production

Sir Lancelot is the knight.
Merlin is the knave.

Contribution

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.

Production

David is the knave.
Patrick is the knight.

Contribution

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

Une sortie possible

Rock Paper Scissors

Règles, règlements et notes

  1. Les règles de golf à code standard s'appliquent
  2. Votre programme doit être composé uniquement d'ASCII imprimables
  3. Toutes les entrées et sorties proviendront de STDIN et STDOUT
PhiNotPi
la source
Et la sensibilité à la casse? Votre description de syntaxe est en minuscules, vos exemples sont en majuscules. L'insensibilité à la casse est-elle une exigence?
ugoren
La première lettre de chaque phrase sera en majuscule, mais à part cela, seuls les noms seront en majuscule. Quel problème spécifique voyez-vous?
PhiNotPi
Puisque vous utilisez une majuscule anglaise correcte, la première lettre dans les deux / soit / ni l'un ni l'autre dépend du contexte. Je suppose qu'être insensible à la casse est le moyen le plus simple de le gérer.
ugoren

Réponses:

6

Python, 491 caractères

Fonctionne en convertissant chaque ligne en une expression Python et en l'évaluant.
L'esclave et le chevalier sont évalués comme 0 et 1. Pour les deux personnes, nous essayons les deux options.
Par exemple, Joe: Steve is a knavedevient Joe==(Steve==knave). De cette façon, si Joeest un coquin, le résultat n'est vrai que s'il ment.
Vous obtenez des erreurs laides quand c'est impossible ou indécis. Si impossible, r[0]c'est une erreur d'index, car rest vide. Si indécidable, la concaténation r[1:]à une liste de chaînes provoque des problèmes.

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:])
ugoren
la source
3

Ruby, 352 caractères

La solution est devenue assez longue, il pourrait donc y avoir encore de la place pour le golf. Cela nécessite que l'entrée soit bien formée (comme le sont tous les exemples ci-dessus - mais n'essayez pas de nommer une personne "Les deux" ...).

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]]
Howard
la source
Vous semblez laisser de côté les périodes dans la sortie, mais cela semble être une correction à deux caractères.
PhiNotPi
1
@PhiNotPi Done. Était un correctif à zéro caractère ...
Howard
0

Perl - 483 octets

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

Similaire à la solution Python. Il réduit les phrases au code Perl puis les evals. Il peut imprimer une sortie presque valide si l'entrée est bizarre mais n'imprime rien si elle est indécidable. Une entrée bien formée fonctionne comme prévu. Les phrases sont passées sur la ligne de commande entre guillemets et aucun indicateur spécial n'est nécessaire.

CJ Dennis
la source