Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Clue game that I learnt recently how to developed with propositional logics #437

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions mohan89en/0000/clue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#This is a clue game and we use logical operators to determine the answer
#this game I learnt from CS50 introduction to AI
#The game goes like we have given 3 places and 3 people and 3 weapons and we have some clues using them we need to find who did murder at which place
import termcolor
from logic import *
mustrad = Symbol("ColMustard")
plum = Symbol("ProfPlum")
scarlet = Symbol("mrscr")
characters = [mustrad,plum,scarlet]
ballroom = Symbol("ballroom")
kitchen = Symbol("kitchen")
library = Symbol("library")
rooms = [ballroom,kitchen,library]
knife = Symbol("knife")
revolver = Symbol("Revolver")
wrench = Symbol("Wrench")
weapons = [knife,revolver,wrench]
symbols = characters+rooms+weapons
def check_knowledge(knowledge):
for symbol in symbols:
if model_check(knowledge,symbol):
termcolor.cprint(f"{symbol}:YES","green")
elif not model_check(knowledge,Not(symbol)):
print(f"{symbol}:MAYBE")
knowledge = And(
Or(mustrad,plum,scarlet),
Or(ballroom,kitchen,library),
Or(knife,revolver,wrench)
)

knowledge.add (Not(mustrad))
#library adds something
knowledge.add(Not(ballroom))
knowledge.add(Not(revolver))
knowledge.add(Or(
Not(scarlet),Not(library),Not(wrench)
))
knowledge.add(Not(plum))
knowledge.add(Not(knife))
check_knowledge(knowledge)

263 changes: 263 additions & 0 deletions mohan89en/0000/logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import itertools


class Sentence():

def evaluate(self, model):
"""Evaluates the logical sentence."""
raise Exception("nothing to evaluate")

def formula(self):
"""Returns string formula representing logical sentence."""
return ""

def symbols(self):
"""Returns a set of all symbols in the logical sentence."""
return set()

@classmethod
def validate(cls, sentence):
if not isinstance(sentence, Sentence):
raise TypeError("must be a logical sentence")

@classmethod
def parenthesize(cls, s):
"""Parenthesizes an expression if not already parenthesized."""
def balanced(s):
"""Checks if a string has balanced parentheses."""
count = 0
for c in s:
if c == "(":
count += 1
elif c == ")":
if count <= 0:
return False
count -= 1
return count == 0
if not len(s) or s.isalpha() or (
s[0] == "(" and s[-1] == ")" and balanced(s[1:-1])
):
return s
else:
return f"({s})"


class Symbol(Sentence):

def __init__(self, name):
self.name = name

def __eq__(self, other):
return isinstance(other, Symbol) and self.name == other.name

def __hash__(self):
return hash(("symbol", self.name))

def __repr__(self):
return self.name

def evaluate(self, model):
try:
return bool(model[self.name])
except KeyError:
raise EvaluationException(f"variable {self.name} not in model")

def formula(self):
return self.name

def symbols(self):
return {self.name}


class Not(Sentence):
def __init__(self, operand):
Sentence.validate(operand)
self.operand = operand

def __eq__(self, other):
return isinstance(other, Not) and self.operand == other.operand

def __hash__(self):
return hash(("not", hash(self.operand)))

def __repr__(self):
return f"Not({self.operand})"

def evaluate(self, model):
return not self.operand.evaluate(model)

def formula(self):
return "¬" + Sentence.parenthesize(self.operand.formula())

def symbols(self):
return self.operand.symbols()


class And(Sentence):
def __init__(self, *conjuncts):
for conjunct in conjuncts:
Sentence.validate(conjunct)
self.conjuncts = list(conjuncts)

def __eq__(self, other):
return isinstance(other, And) and self.conjuncts == other.conjuncts

def __hash__(self):
return hash(
("and", tuple(hash(conjunct) for conjunct in self.conjuncts))
)

def __repr__(self):
conjunctions = ", ".join(
[str(conjunct) for conjunct in self.conjuncts]
)
return f"And({conjunctions})"

def add(self, conjunct):
Sentence.validate(conjunct)
self.conjuncts.append(conjunct)

def evaluate(self, model):
return all(conjunct.evaluate(model) for conjunct in self.conjuncts)

def formula(self):
if len(self.conjuncts) == 1:
return self.conjuncts[0].formula()
return " ∧ ".join([Sentence.parenthesize(conjunct.formula())
for conjunct in self.conjuncts])

def symbols(self):
return set.union(*[conjunct.symbols() for conjunct in self.conjuncts])


class Or(Sentence):
def __init__(self, *disjuncts):
for disjunct in disjuncts:
Sentence.validate(disjunct)
self.disjuncts = list(disjuncts)

def __eq__(self, other):
return isinstance(other, Or) and self.disjuncts == other.disjuncts

def __hash__(self):
return hash(
("or", tuple(hash(disjunct) for disjunct in self.disjuncts))
)

def __repr__(self):
disjuncts = ", ".join([str(disjunct) for disjunct in self.disjuncts])
return f"Or({disjuncts})"

def evaluate(self, model):
return any(disjunct.evaluate(model) for disjunct in self.disjuncts)

def formula(self):
if len(self.disjuncts) == 1:
return self.disjuncts[0].formula()
return " ∨ ".join([Sentence.parenthesize(disjunct.formula())
for disjunct in self.disjuncts])

def symbols(self):
return set.union(*[disjunct.symbols() for disjunct in self.disjuncts])


class Implication(Sentence):
def __init__(self, antecedent, consequent):
Sentence.validate(antecedent)
Sentence.validate(consequent)
self.antecedent = antecedent
self.consequent = consequent

def __eq__(self, other):
return (isinstance(other, Implication)
and self.antecedent == other.antecedent
and self.consequent == other.consequent)

def __hash__(self):
return hash(("implies", hash(self.antecedent), hash(self.consequent)))

def __repr__(self):
return f"Implication({self.antecedent}, {self.consequent})"

def evaluate(self, model):
return ((not self.antecedent.evaluate(model))
or self.consequent.evaluate(model))

def formula(self):
antecedent = Sentence.parenthesize(self.antecedent.formula())
consequent = Sentence.parenthesize(self.consequent.formula())
return f"{antecedent} => {consequent}"

def symbols(self):
return set.union(self.antecedent.symbols(), self.consequent.symbols())


class Biconditional(Sentence):
def __init__(self, left, right):
Sentence.validate(left)
Sentence.validate(right)
self.left = left
self.right = right

def __eq__(self, other):
return (isinstance(other, Biconditional)
and self.left == other.left
and self.right == other.right)

def __hash__(self):
return hash(("biconditional", hash(self.left), hash(self.right)))

def __repr__(self):
return f"Biconditional({self.left}, {self.right})"

def evaluate(self, model):
return ((self.left.evaluate(model)
and self.right.evaluate(model))
or (not self.left.evaluate(model)
and not self.right.evaluate(model)))

def formula(self):
left = Sentence.parenthesize(str(self.left))
right = Sentence.parenthesize(str(self.right))
return f"{left} <=> {right}"

def symbols(self):
return set.union(self.left.symbols(), self.right.symbols())


def model_check(knowledge, query):
"""Checks if knowledge base entails query."""

def check_all(knowledge, query, symbols, model):
"""Checks if knowledge base entails query, given a particular model."""

# If model has an assignment for each symbol
if not symbols:

# If knowledge base is true in model, then query must also be true
if knowledge.evaluate(model):
return query.evaluate(model)
return True
else:

# Choose one of the remaining unused symbols
remaining = symbols.copy()
p = remaining.pop()

# Create a model where the symbol is true
model_true = model.copy()
model_true[p] = True

# Create a model where the symbol is false
model_false = model.copy()
model_false[p] = False

# Ensure entailment holds in both models
return (check_all(knowledge, query, remaining, model_true) and
check_all(knowledge, query, remaining, model_false))

# Get all symbols in both knowledge and query
symbols = set.union(knowledge.symbols(), query.symbols())

# Check that knowledge entails query
return check_all(knowledge, query, symbols, dict())