diff --git a/week5/material/fol_example.py b/week5/material/fol_example.py
new file mode 100644
index 0000000000000000000000000000000000000000..93bf9343c78eba98974a271d89137d58975889e5
--- /dev/null
+++ b/week5/material/fol_example.py
@@ -0,0 +1,23 @@
+from pyDatalog import pyDatalog
+
+pyDatalog.create_terms('IsFemale,IsMale,AreParents,IsHungry,Want,X,Y,Z')
+
+#+ IsFemale('Jane')
+#+ IsMale('Joe')
++ IsMale('Bob')
++ AreParents('Jane','Joe','Bob')
++ IsHungry('Bob')
+
+# adding last fact with .load method
+pyDatalog.load("+ Want('Bob','Milk')")
+
+# adding clauses (and commenting the two given facts above)
+IsFemale(X) <= AreParents(X, Y, Z)
+IsMale(Y) <= AreParents(X, Y, Z)
+
+result = IsMale(X)
+print(result)
+
+# asking with .ask method
+answer = pyDatalog.ask('IsMale(X) & IsHungry(X)')
+print(answer)
\ No newline at end of file
diff --git a/week5/material/guess_who.csv b/week5/material/guess_who.csv
new file mode 100644
index 0000000000000000000000000000000000000000..67fdf1c47177e731d7b4621a9c1949ea7eaf7ac1
--- /dev/null
+++ b/week5/material/guess_who.csv
@@ -0,0 +1,25 @@
+Name,Gender,EyeColour,HairColour,HairLength,LipsSize,HasMoustache,HasBeard,WearHat,IsBald,WearGlasses,WearEarrings
+Alex,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No
+Alfred,Male,Blue,Red,Short,Small,Yes,No,No,No,No,No
+Anita,Female,Blue,White,Short,Small,No,No,No,No,No,No
+Anne,Female,Brown,Black,Short,Small,No,No,No,No,No,Yes
+Bernard,Male,Brown,Brown,Short,Small,No,No,Yes,No,No,No
+Bill,Male,Brown,Red,Short,Small,No,Yes,No,Yes,No,No
+Charles,Male,Brown,Blond,Short,Big,Yes,No,No,No,No,No
+Claire,Female,Brown,Red,Long,Small,No,No,Yes,No,Yes,No
+David,Male,Brown,Blond,Long,Small,No,Yes,No,No,No,No
+Eric,Male,Brown,Blond,Short,Small,No,No,Yes,No,No,No
+Frans,Male,Brown,Red,Short,Small,No,No,No,No,No,No
+George,Male,Brown,White,Short,Small,No,No,Yes,No,No,No
+Herman,Male,Brown,Red,Short,Small,No,No,No,Yes,No,No
+Joe,Male,Brown,Blond,Short,Small,No,No,No,No,Yes,No
+Maria,Female,Brown,Brown,Long,Small,No,No,Yes,No,No,Yes
+Max,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No
+Paul,Male,Brown,White,Short,Small,No,No,No,No,Yes,No
+Peter,Male,Blue,White,Short,Big,No,No,No,No,No,No
+Philip,Male,Brown,Black,Short,Small,No,Yes,No,No,No,No
+Richard,Male,Brown,Brown,Short,Small,Yes,Yes,No,Yes,No,No
+Robert,Male,Blue,Brown,Short,Small,No,No,No,No,No,No
+Sam,Male,Brown,White,Short,Small,No,No,No,Yes,Yes,No
+Susan,Female,Brown,Blond,Long,Big,No,No,No,No,No,Yes
+Tom,Male,Blue,Black,Short,Small,No,No,No,Yes,Yes,No
\ No newline at end of file
diff --git a/week5/material/guess_who.py b/week5/material/guess_who.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ccfaa492e145dfeb931d2178350325d3a85b604
--- /dev/null
+++ b/week5/material/guess_who.py
@@ -0,0 +1,92 @@
+import csv
+import random
+from pyDatalog import pyDatalog
+import guess_who_kb
+
+def find_best_question(remaining_characters, remaining_predicates):
+    # TODO
+    pass
+    
+if __name__ == '__main__':
+    # MAIN APPLICATION
+
+    names_answer = pyDatalog.ask("Gender(X, Y)")
+    names = []
+    for item in names_answer.answers:
+        names.append(item[0])
+    
+    with open('guess_who.csv', newline='', mode='r', encoding='utf-8-sig') as f:
+        reader = csv.reader(f)
+        properties = None
+        for i, row in enumerate(reader):
+            if i == 0:
+                # First row with the predicates
+                properties = row[1:]
+                break
+
+    # selecting a character randomly that the AI agent would need to guess
+    character_name = random.choice(names)
+    print("The AI agent will need to guess the character with name '{0}'".format(character_name))
+
+    # start with all the possible characters
+    available_characters = names.copy()
+
+    # start with all the predicates we can use for questions
+    available_predicates = {}
+    for prop in properties:
+        query_outputs = pyDatalog.ask("{0}(X, Y)".format(prop))
+        predicate_values = []
+        for output in query_outputs.answers:
+            if output[1] not in predicate_values:
+                predicate_values.append(output[1])
+        available_predicates[prop] = predicate_values
+    
+    # main body
+    print("AI starts the game with the following names on the board:")
+    print(available_characters)
+
+    # The game stops when we have only one character available or we have no more questions to ask
+    while len(available_characters) > 1 and len(available_predicates.keys()) > 0:
+        # looking for the best next question
+        best_question = find_best_question(available_characters, available_predicates)
+        if best_question is None:
+            print("Function find_best_question not implemented. Asking a random question")
+            predicate = random.choice(list(available_predicates.keys()))
+            value = random.choice(available_predicates[predicate])
+            best_question = (predicate, value)
+
+        question = "{0}(X, '{1}')".format(best_question[0], best_question[1])
+        print("AI Asking query: {0}".format(question))
+        # retrieving the names with the considered feature
+        names_with_feature = [r[0] for r in pyDatalog.ask(question).answers if r[0] in available_characters]
+
+        # checking if answer to the question is YES or NO
+        yes_no_answer = pyDatalog.ask("{0}('{1}','{2}')".format(best_question[0], character_name, best_question[1]))
+        yes_no_answer = "YES" if yes_no_answer is not None else "NO"
+        print("Opponent answering: The answer is {0}".format(yes_no_answer))
+
+        # removing predicate
+        available_predicates[best_question[0]].remove(best_question[1])
+        # if there is less than 2 options or the answer was YES, we can remove the predicate completely
+        if yes_no_answer == "YES" or len(available_predicates[best_question[0]]) < 2:
+            del available_predicates[best_question[0]]
+        
+        # updating the remaining characters on the board
+        if yes_no_answer == "YES":
+            available_characters = names_with_feature.copy()
+        else:
+            for name in names_with_feature:
+                available_characters.remove(name)
+        
+        print("AI updates the remaining names on the board. Remaining names:")
+        print(available_characters)
+    
+    # making a guess
+    if len(available_characters) == 1:
+        # we are sure of the guess
+        print("AI says: The hidden character is {0}".format(available_characters[0]))
+    else:
+        # we are not sure, random guess
+        print("AI says: I am not sure who the character is. I will make a guess.")
+        guess = random.choice(available_characters)
+        print("AI says: The hidden character is {0}".format(guess))
\ No newline at end of file
diff --git a/week5/material/guess_who_kb.py b/week5/material/guess_who_kb.py
new file mode 100644
index 0000000000000000000000000000000000000000..495f05a34165aa9110c82d26129d4ad54ee0837a
--- /dev/null
+++ b/week5/material/guess_who_kb.py
@@ -0,0 +1,7 @@
+import csv
+from pyDatalog import pyDatalog
+
+# KNOWLEDGE BASE
+
+# loading the features of the guess who characters from the csv
+# TODO
\ No newline at end of file
diff --git a/week5/solution/exercise1-2.py b/week5/solution/exercise1-2.py
new file mode 100644
index 0000000000000000000000000000000000000000..29d98b7d0c59c9d92a5a8df20feecb4274583220
--- /dev/null
+++ b/week5/solution/exercise1-2.py
@@ -0,0 +1,39 @@
+# Exercise 1
+
+QUERIES = {
+    'alpha1': (lambda p, q, z: not (p or q) and z, ['P', 'Q', 'Z']),
+    'alpha2': (lambda p, q, z: (not p and q) or z, ['P', 'Q', 'Z']), # using logic equivalence
+    'alpha3': (lambda p, q, z: (p or q) == q, ['P', 'Q']),
+    'alpha4': (lambda p, q, z: (p and q) or (p or q) and not(p and z), ['P', 'Q', 'Z']),
+    'alpha5': (lambda p, q, z: p and not p, ['P'])
+}
+
+for query, query_val in QUERIES.items():
+    query_lambda, propsym = query_val
+    print("")
+    print("Models of M({0}):".format(query))
+    models = []
+    # list all models
+    for p in [True, False]:
+        for q in [True, False]:
+            for z in [True, False]:
+                if query_lambda(p, q, z):
+                    model = {}
+                    for sym in propsym:
+                        model[sym] = eval(sym.lower())
+
+                    if model not in models:        
+                        models.append(model)
+                        print(model)
+
+"""
+Exercise 2:
+Check the output from the script for exercise 1 for the models in M(alpha<N>).
+
+1. For all models of alpha2 we have that Q OR Z is True, so alpha2 |= Q OR Z
+2. For P = False and Q = True in M(alpha3), P <=> Q is False, so alpha3 does not entail P <=> Q
+3. The sentence P => (Q OR NOT Z) is logically equivalent to NOT P OR (Q OR NOT Z). 
+For P = False, the sentence is always True.
+For P = True the sentence is True when Q is True or Z is False. All the remaining models in M(alpha4) satisfy that.
+So, alpha4 |= P => (Q OR NOT Z)
+"""
\ No newline at end of file
diff --git a/week5/solution/exercise3.py b/week5/solution/exercise3.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a1791a87db69e5fa8f7ad27721ab83cd78c1126
--- /dev/null
+++ b/week5/solution/exercise3.py
@@ -0,0 +1,52 @@
+KB = {
+    'P->Q': lambda p, q, r, s: not p or q, # using the logic equivalence
+    'not R -> not P or Q': lambda p, q, r, s: r or (not p or q),
+    'S -> not Q or R': lambda p, q, r, s: not s or (not q or r)
+}
+
+QUERIES = {
+    'P and S': lambda p, q, r, s: p and s,
+    'not Q and not R': lambda p, q, r, s: not q and not r,
+    'not P or Q': lambda p, q, r, s: not p or q
+}
+
+if __name__ == '__main__':
+
+    # listing all models in M(KB)
+    KB_models = []
+    for p in [True, False]:
+        for q in [True, False]:
+            for r in [True, False]:
+                for s in [True, False]:
+                    valid = True
+                    for rule in KB.values():
+                        # Testing if current model
+                        # makes KB rules true
+                        # If only one rules is not true
+                        # then set not valid and break
+                        if not rule(p, q, r, s):
+                            valid = False
+                            break
+                    # if a valid model, save it
+                    if valid:
+                        KB_models.append({
+                            'P': p,
+                            'Q': q,
+                            'R': r,
+                            'S': s
+                        })
+    # listing the models in M(KB)
+    print("Models in M(KB):")
+    for m in KB_models:
+        print(m)
+    
+    # testing queries
+    for query, query_lambda in QUERIES.items():
+        valid = True
+        for m in KB_models:
+            if not query_lambda(m['P'], m['Q'], m['R'], m['S']):
+                valid = False
+                print("The query '{0}' is not entailed by the KB: for P={1}, Q={2}, R={3}, S={4} KB is True but query is not".format(query, m['P'], m['Q'], m['R'], m['S']))
+                break
+        if valid:
+            print("The query '{0}' is  entailed by the KB".format(query))
diff --git a/week5/solution/fol_example.py b/week5/solution/fol_example.py
new file mode 100644
index 0000000000000000000000000000000000000000..93bf9343c78eba98974a271d89137d58975889e5
--- /dev/null
+++ b/week5/solution/fol_example.py
@@ -0,0 +1,23 @@
+from pyDatalog import pyDatalog
+
+pyDatalog.create_terms('IsFemale,IsMale,AreParents,IsHungry,Want,X,Y,Z')
+
+#+ IsFemale('Jane')
+#+ IsMale('Joe')
++ IsMale('Bob')
++ AreParents('Jane','Joe','Bob')
++ IsHungry('Bob')
+
+# adding last fact with .load method
+pyDatalog.load("+ Want('Bob','Milk')")
+
+# adding clauses (and commenting the two given facts above)
+IsFemale(X) <= AreParents(X, Y, Z)
+IsMale(Y) <= AreParents(X, Y, Z)
+
+result = IsMale(X)
+print(result)
+
+# asking with .ask method
+answer = pyDatalog.ask('IsMale(X) & IsHungry(X)')
+print(answer)
\ No newline at end of file
diff --git a/week5/solution/guess_who.csv b/week5/solution/guess_who.csv
new file mode 100644
index 0000000000000000000000000000000000000000..67fdf1c47177e731d7b4621a9c1949ea7eaf7ac1
--- /dev/null
+++ b/week5/solution/guess_who.csv
@@ -0,0 +1,25 @@
+Name,Gender,EyeColour,HairColour,HairLength,LipsSize,HasMoustache,HasBeard,WearHat,IsBald,WearGlasses,WearEarrings
+Alex,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No
+Alfred,Male,Blue,Red,Short,Small,Yes,No,No,No,No,No
+Anita,Female,Blue,White,Short,Small,No,No,No,No,No,No
+Anne,Female,Brown,Black,Short,Small,No,No,No,No,No,Yes
+Bernard,Male,Brown,Brown,Short,Small,No,No,Yes,No,No,No
+Bill,Male,Brown,Red,Short,Small,No,Yes,No,Yes,No,No
+Charles,Male,Brown,Blond,Short,Big,Yes,No,No,No,No,No
+Claire,Female,Brown,Red,Long,Small,No,No,Yes,No,Yes,No
+David,Male,Brown,Blond,Long,Small,No,Yes,No,No,No,No
+Eric,Male,Brown,Blond,Short,Small,No,No,Yes,No,No,No
+Frans,Male,Brown,Red,Short,Small,No,No,No,No,No,No
+George,Male,Brown,White,Short,Small,No,No,Yes,No,No,No
+Herman,Male,Brown,Red,Short,Small,No,No,No,Yes,No,No
+Joe,Male,Brown,Blond,Short,Small,No,No,No,No,Yes,No
+Maria,Female,Brown,Brown,Long,Small,No,No,Yes,No,No,Yes
+Max,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No
+Paul,Male,Brown,White,Short,Small,No,No,No,No,Yes,No
+Peter,Male,Blue,White,Short,Big,No,No,No,No,No,No
+Philip,Male,Brown,Black,Short,Small,No,Yes,No,No,No,No
+Richard,Male,Brown,Brown,Short,Small,Yes,Yes,No,Yes,No,No
+Robert,Male,Blue,Brown,Short,Small,No,No,No,No,No,No
+Sam,Male,Brown,White,Short,Small,No,No,No,Yes,Yes,No
+Susan,Female,Brown,Blond,Long,Big,No,No,No,No,No,Yes
+Tom,Male,Blue,Black,Short,Small,No,No,No,Yes,Yes,No
\ No newline at end of file
diff --git a/week5/solution/guess_who.py b/week5/solution/guess_who.py
new file mode 100644
index 0000000000000000000000000000000000000000..307e740f0b14e13a4c74cc4775a32449af6705c3
--- /dev/null
+++ b/week5/solution/guess_who.py
@@ -0,0 +1,116 @@
+import csv
+import random
+from pyDatalog import pyDatalog
+import guess_who_kb
+
+def find_best_question(remaining_characters, remaining_predicates):
+    best_question = None
+    max_avg_eliminated = 0
+    for predicate in remaining_predicates.keys():
+        for value in remaining_predicates[predicate]:
+            answer = pyDatalog.ask("{0}(X, '{1}')".format(predicate, value))
+
+            # characters I will be able to eliminate if answer is NO
+            names = [r[0] for r in answer.answers if r[0] in remaining_characters]
+            n_eliminated_no = len(names)
+            # whereas if answer is YES
+            n_eliminated_yes = len(remaining_characters) - len(names)
+
+            # we need to make sure that the question is not a silly one
+            # silly questions are questions that we already know the answer
+            # because all the remaining characters share the same common feature
+            # These questions lead to eliminating all the characters if the answer is YES or NO
+            if n_eliminated_no == len(remaining_characters) or n_eliminated_yes == len(remaining_characters):
+                continue
+
+            n_avg_eliminated = (n_eliminated_no + n_eliminated_yes)/2
+            if best_question is None or n_avg_eliminated > max_avg_eliminated:
+                best_question = (predicate, value)
+                max_avg_eliminated = n_avg_eliminated
+    
+    return best_question
+
+    
+if __name__ == '__main__':
+    # MAIN APPLICATION
+
+    names_answer = pyDatalog.ask("Gender(X, Y)")
+    names = []
+    for item in names_answer.answers:
+        names.append(item[0])
+    
+    with open('guess_who.csv', newline='', mode='r', encoding='utf-8-sig') as f:
+        reader = csv.reader(f)
+        properties = None
+        for i, row in enumerate(reader):
+            if i == 0:
+                # First row with the predicates
+                properties = row[1:]
+                break
+
+    # selecting a character randomly that the AI agent would need to guess
+    character_name = random.choice(names)
+    print("The AI agent will need to guess the character with name '{0}'".format(character_name))
+
+    # start with all the possible characters
+    available_characters = names.copy()
+
+    # start with all the predicates we can use for questions
+    available_predicates = {}
+    for prop in properties:
+        query_outputs = pyDatalog.ask("{0}(X, Y)".format(prop))
+        predicate_values = []
+        for output in query_outputs.answers:
+            if output[1] not in predicate_values:
+                predicate_values.append(output[1])
+        available_predicates[prop] = predicate_values
+    
+    # main body
+    print("AI starts the game with the following names on the board:")
+    print(available_characters)
+
+    # The game stops when we have only one character available or we have no more questions to ask
+    while len(available_characters) > 1 and len(available_predicates.keys()) > 0:
+        # looking for the best next question
+        best_question = find_best_question(available_characters, available_predicates)
+        if best_question is None:
+            print("Function find_best_question not implemented. Asking a random question")
+            predicate = random.choice(list(available_predicates.keys()))
+            value = random.choice(available_predicates[predicate])
+            best_question = (predicate, value)
+
+        question = "{0}(X, '{1}')".format(best_question[0], best_question[1])
+        print("AI Asking query: {0}".format(question))
+        # retrieving the names with the considered feature
+        names_with_feature = [r[0] for r in pyDatalog.ask(question).answers if r[0] in available_characters]
+
+        # checking if answer to the question is YES or NO
+        yes_no_answer = pyDatalog.ask("{0}('{1}','{2}')".format(best_question[0], character_name, best_question[1]))
+        yes_no_answer = "YES" if yes_no_answer is not None else "NO"
+        print("Opponent answering: The answer is {0}".format(yes_no_answer))
+
+        # removing predicate
+        available_predicates[best_question[0]].remove(best_question[1])
+        # if there is less than 2 options or the answer was YES, we can remove the predicate completely
+        if yes_no_answer == "YES" or len(available_predicates[best_question[0]]) < 2:
+            del available_predicates[best_question[0]]
+        
+        # updating the remaining characters on the board
+        if yes_no_answer == "YES":
+            available_characters = names_with_feature.copy()
+        else:
+            for name in names_with_feature:
+                available_characters.remove(name)
+        
+        print("AI updates the remaining names on the board. Remaining names:")
+        print(available_characters)
+    
+    # making a guess
+    if len(available_characters) == 1:
+        # we are sure of the guess
+        print("AI says: The hidden character is {0}".format(available_characters[0]))
+    else:
+        # we are not sure, random guess
+        print("AI says: I am not sure who the character is. I will make a guess.")
+        guess = random.choice(available_characters)
+        print("AI says: The hidden character is {0}".format(guess))
\ No newline at end of file
diff --git a/week5/solution/guess_who_kb.py b/week5/solution/guess_who_kb.py
new file mode 100644
index 0000000000000000000000000000000000000000..602ae93def87958206f4ad52463ccb8e270369bf
--- /dev/null
+++ b/week5/solution/guess_who_kb.py
@@ -0,0 +1,25 @@
+import csv
+from pyDatalog import pyDatalog
+
+# KNOWLEDGE BASE
+
+# loading the features of the guess who characters from the csv
+names = []
+with open('guess_who.csv', newline='', mode='r', encoding='utf-8-sig') as f:
+    reader = csv.reader(f)
+    properties = None
+    for i, row in enumerate(reader):
+        if i == 0:
+            # First row with the predicates
+            # create the terms to use (variables and predicates)
+            properties = row[1:]
+            predicates = ','.join(row[1:])
+            pyDatalog.create_terms('{0},X,Y,Z'.format(predicates))
+            continue
+        
+        # rows with names and values for the predicates
+        name = row[0]
+        names.append(name)
+        for idx, propval in enumerate(row[1:]):
+            # add a fact for each predicate of the character
+            pyDatalog.load("+ {0}('{1}', '{2}')".format(properties[idx], name, propval))
\ No newline at end of file