summaryrefslogtreecommitdiff
path: root/prolog/problems/family_relations/aunt_2/common.py
blob: 8cc644a5357b83477e7f55b731d05f5e53e7b0e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
from operator import itemgetter
import socket
import prolog.engine
import prolog.util
from server.hints import Hint
import server.problems

id = 98
number = 6
visible = True
facts = 'family_relations'

solution = '''\
sister98(X, Y) :-
  parent(P, X),
  parent(P, Y),
  female(X),
  X \== Y.

aunt(X, Y) :-
  sister98(X, Z),
  parent(Z, Y).
'''

hint_type = {
    'x_and_y_mixed_up': Hint('x_and_y_mixed_up'),
    'precedence_fail': Hint('precedence_fail'),
    'x_must_have_sibling': Hint('x_must_have_sibling'),
    'x_must_be_female': Hint('x_must_be_female'),
    'y_must_have_parent': Hint('y_must_have_parent'),
    'aunt_vs_mother': Hint('aunt_vs_mother'),
    'x_need_not_be_parent': Hint('x_need_not_be_parent'),
    'y_need_not_be_parent': Hint('y_need_not_be_parent'),
    'predicate_always_false': Hint('predicate_always_false'),
    'nephews_parent_can_be_male': Hint('nephews_parent_can_be_male'),
}

test_cases = [
    ('aunt(X, _)',
        [{'X': 'sally'}, {'X': 'melanie'}, {'X': 'vanessa'}, {'X': 'patricia'}]),
    ('aunt(_, X)',
        [{'X': 'vanessa'}, {'X': 'patricia'}, {'X': 'joanne'}, {'X': 'john'}, {'X': 'susan'}]),
    ('aunt(sally, X)',
        [{'X': 'vanessa'}, {'X': 'patricia'}]),
]

def test(code, aux_code):
    n_correct = 0
    engine_id = None
    try:
        engine_id, output = prolog.engine.create(code=code+aux_code, timeout=1.0)
        if engine_id is not None and 'error' not in map(itemgetter(0), output):
            # Engine successfully created, and no syntax error in program.
            for query, answers in test_cases:
                if prolog.engine.check_answers(engine_id, query=query, answers=answers, timeout=1.0):
                    n_correct += 1
    except socket.timeout:
        pass
    finally:
        if engine_id:
            prolog.engine.destroy(engine_id)

    hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_cases)}}]
    return n_correct, len(test_cases), hints

def hint(code, aux_code):
    tokens = prolog.util.tokenize(code)

    try:
        engine_id, output = prolog.engine.create(code=code+aux_code, timeout=1.0)

        # target predicate seems to always be false
        if not prolog.engine.ask_truth(engine_id, 'aunt(_, _)'):
            return [{'id': 'predicate_always_false'}]

        # X and Y mixed up
        # warning: knowledge base dependent
        # independent: match on findall(X/Y, (sister(Y, P), parent(P, X)), L)
        if prolog.engine.ask_truth(engine_id,
            'setof(X/Y, aunt(X, Y), [alessandro/daniela, alessandro/luana, jeffrey/andrew, \
             jeffrey/melanie, patricia/susan, vanessa/john, william/andrew, william/melanie])') or \
           prolog.engine.ask_truth(engine_id,
            'setof(X/Y, aunt(X, Y), [joanne/melanie, patricia/sally, susan/patricia, vanessa/sally])'):
            return [{'id': 'x_and_y_mixed_up'}]

        # precedence fail (AND block vs OR block)
        # case in point: female(X), parent(P, Y), brother(P, X) ; sister(P, X)
        # warning: knowledge base dependent
        if prolog.util.Token('SEMI', ';') in tokens and prolog.engine.ask_truth(engine_id,
            'findall(_, aunt(X, Y), L), length(L, 15)'):
            return [{'id': 'precedence_fail'}]

        # X must be female
        if prolog.engine.ask_truth(engine_id,
            'aunt(X, _), male(X)'):
            return [{'id': 'x_must_be_female'}]

        # nephew's parent can also be male
        # this mistake comes from: parent(P, Y), sister(P, X)...
        if prolog.engine.ask_truth(engine_id,
            'setof(X/Y, aunt(X, Y), [patricia/susan, vanessa/john])'):
            return [{'id': 'nephews_parent_can_be_male'}]

        # Y must have a parent
        # perhaps risky as only one such nephew exists in DB (susan)
        if prolog.engine.ask_truth(engine_id,
            'aunt(_, Y), \+ parent(_, Y)'):
            return [{'id': 'y_must_have_parent'}]

        # X and P can be the same person
        # this can occur if the problem is not solved using sister/2
        if prolog.engine.ask_truth(engine_id,
            'aunt(X, Y), female(X), parent(X, Y)'):
            return [{'id': 'aunt_vs_mother'}]

        # X must have sibling
        if prolog.engine.ask_truth(engine_id,
            'aunt(X, _), \+ (sister(X, _) ; brother(X, _))'):       # TODO: Tim, can I use sister/brother predicates here? Or sister98?
            return [{'id': 'x_must_have_sibling'}]

        # X does not necessarily need to be a parent
        # perhaps risky as only one aunt that is not a parent exists in DB (melanie)
        if prolog.engine.ask_one(engine_id,
            'aunt(X, _), \+ parent(X, _)') == 'false':
            return [{'id': 'x_need_not_be_parent'}]

        # Y does not necessarily need to be a parent
        # perhaps risky as only one such nephew exists in DB (susan)
        if prolog.engine.ask_one(engine_id,
            'aunt(_, Y), \+ parent(Y, _)') == 'false':
            return [{'id': 'y_need_not_be_parent'}]

    except socket.timeout as ex:
        pass

    finally:
        if engine_id:
            prolog.engine.destroy(engine_id)

    return None