summaryrefslogtreecommitdiff
path: root/prolog/problems/family_relations/mother_2/common.py
blob: 503404fca49df2acf1986731f6c287786be7edef (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
from operator import itemgetter
import socket
import prolog.engine
import prolog.util
from server.hints import Hint
import server.problems

id = 94
number = 1
visible = True
facts = 'family_relations'

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

hint_type = {
    'or_instead_of_and': Hint('or_instead_of_and'),
    'or_instead_of_and_two_rules': Hint('or_instead_of_and_two_rules'),
    'x_must_be_female': Hint('x_must_be_female'),
    'x_must_be_parent': Hint('x_must_be_parent'),
    'y_can_be_of_any_gender': Hint('y_can_be_of_any_gender'),
    'y_need_not_be_parent': Hint('y_need_not_be_parent'),
    'predicate_always_false': Hint('predicate_always_false'),
}

test_cases = [
    ('mother(X, _)',
        [{'X': 'ana'}, {'X': 'elaine'}, {'X': 'estelle'}, {'X': 'helen'},
         {'X': 'jill'}, {'X': 'joanne'}, {'X': 'margaret'}, {'X': 'nevia'},
         {'X': 'patricia'}, {'X': 'sally'}, {'X': 'tina'}, {'X': 'vanessa'}]),
    ('mother(_, X)',
        [{'X': 'aleksander'}, {'X': 'alessandro'}, {'X': 'andrew'},
         {'X': 'anna'}, {'X': 'daniela'}, {'X': 'george'}, {'X': 'jerry'},
         {'X': 'joanne'}, {'X': 'john'}, {'X': 'kramer'}, {'X': 'luana'},
         {'X': 'melanie'}, {'X': 'nevia'}, {'X': 'steve'}, {'X': 'susan'},
         {'X': 'william'}]),
    ('mother(joanne, X)',
        [{'X': 'steve'}]),
]

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, 'mother(_, _)'):
            return [{'id': 'predicate_always_false'}]

        # OR (;) instead of AND (,)
        # this hint has to be before the next two
        # as otherwise those two would always override it
        # and not convey the same (amount of) help/information
        # warning: due to speed considerations this (50) is knowledge base dependent
        # independent: findall(_, (parent(X, Y) ; female(X)), L2)
        if prolog.util.Token('SEMI', ';') in tokens and prolog.engine.ask_truth(engine_id,
            'findall(_, mother(X, Y), L), length(L, 50)'):
            return [{'id': 'or_instead_of_and'}]

        # OR instead of AND written with two rules, namely:
        #    (r1) mother(X, Y):- female(X).  (r2) mother(X, Y):- parent(X, Y).
        if prolog.engine.ask_truth(engine_id,
            'findall(_, mother(X, Y), L), length(L, 50)'):
            return [{'id': 'or_instead_of_and_two_rules'}]

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

        # X must be a parent
        if prolog.engine.ask_truth(engine_id,
            'mother(X, _), \+ parent(X, _)'):
            return [{'id': 'x_must_be_parent'}]

        # Y can be of any gender, incl. male
        if prolog.engine.ask_truth(engine_id, 'mother(_, Y)') and \
           prolog.engine.ask_one(engine_id, 'mother(_, Y), male(Y)') == 'false':
            return [{'id': 'y_can_be_of_any_gender'}]

        # Y does not necessarily need to be a parent
        if prolog.engine.ask_truth(engine_id, 'mother(_, Y)') and \
           prolog.engine.ask_one(engine_id, 'mother(_, 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