summaryrefslogtreecommitdiff
path: root/prolog/problems/sorting/pivoting_4/common.py
blob: aeb430f2b5608c109e7ba1656a0c642596e88772 (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
142
143
144
145
146
147
148
from operator import itemgetter
import socket

import prolog.engine
from server.hints import Hint, HintPopup

id = 124
number = 50
visible = False
facts = None

solution = '''\
pivoting(_, [], [], []).
pivoting(P, [H|T], [H|S], G) :-
  H =< P,
  pivoting(P, T, S, G).
pivoting(P, [H|T], S, [H|G]) :-
  H > P,
  pivoting(P, T, S, G).
'''

hint_type = {
    'eq_instead_of_equ_markup': HintPopup('eq_instead_of_equ_markup'),
    'eq_instead_of_equ': Hint('eq_instead_of_equ'),
    'predicate_always_false': Hint('predicate_always_false'),
    'base_case': Hint('base_case'),
    'recursive_case': Hint('recursive_case'),
    'timeout': Hint('timeout'),
    '>_and_<_mixed_up': Hint('>_and_<_mixed_up'),
    'duplicates_not_considered': Hint('duplicates_not_considered'),
    'all_elements_in_either_S_or_G': Hint('all_elements_in_either_S_or_G'),
    'arbitrary_solution': Hint('arbitrary_solution'),
    'unprotected_branch': Hint('unprotected_branch'),
    'forcing_result_onto_recursion': Hint('forcing_result_onto_recursion'),
    'no_recursion_in_one_branch': Hint('no_recursion_in_one_branch'),
}

test_cases = [
    ('pivoting(5, [], A, B)',
        [{'A': '[]', 'B': '[]'}]),
    ('pivoting(5, [6, 7, 6], A, B)',
        [{'A': '[]', 'B': '[6, 7, 6]'}]),
    ('pivoting(4, [2, 1, 8, 9, 3, 4, 2], A, B)',
        [{'A': '[2, 1, 3, 4, 2]', 'B': '[8, 9]'}]),
    ('pivoting(4, [22, 1, 0, 8, 3, 5, 7, -2], A, B)',
        [{'A': '[1, 0, 3, -2]', 'B': '[22, 8, 5, 7]'}]),
]

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)

        # strict equality testing instead of simple matching
        # this is usually (but not necessarily) wrong
        targets = [prolog.util.Token('EQ', '==')]
        marks = [(t.pos, t.pos + len(t.val)) for t in tokens if t in targets]
        if marks:
            return [{'id': 'eq_instead_of_equ_markup', 'start': m[0], 'end': m[1]} for m in marks] + \
                   [{'id': 'eq_instead_of_equ'}]

        if prolog.engine.ask_truthTO(engine_id, '''\
                pivoting(4, [3, 5, 9, 6, 3, 7, 2], A, B),
                msort(A, [5, 6, 7, 9]),
                msort(B, [2, 3, 3])'''):
            return [{'id': '>_and_<_mixed_up'}]

        if prolog.engine.ask_truthTO(engine_id, '''\
                pivoting(4, [2, 1, 3], [2, 1, 3], []),
                pivoting(4, [3, 5, 9, 5, 2], [3 | yowza], [5 | brix])
                ;
                pivoting(4, [6, 5, 7], [], [6, 5, 7]),
                pivoting(4, [3, 5, 9, 5, 2], [3 | yowza], [5 | brix])'''):
            return [{'id': 'no_recursion_in_one_branch'}]

        if prolog.engine.ask_truthTO(engine_id, '''\
                pivoting(4, [3, 5, 9, 6, 3, 7, 2], [3, 3, 2], [5, 9, 6, 7]),
                findall(q, pivoting(4, [3, 5, 9, 6, 3, 7, 2], _, _), [q, q | _])'''):
            return [{'id': 'unprotected_branch'}]

        if prolog.engine.ask_truthTO(engine_id, '''\
                pivoting(4, [3, 5, 9, 5, 2], [3, 2], [5, 9, 5]),
                \+ pivoting(4, [3, 5, 4, 5, 2], _, _)'''):
            return [{'id': 'duplicates_not_considered'}]

        if prolog.engine.ask_truthTO(engine_id, '''\
                pivoting(4, [5, 3], _, [5|yowza])
                ;
                pivoting(4, [3, 5], [3|yowza], _)'''):
            return [{'id': 'arbitrary_solution'}]

        if prolog.engine.ask_truthTO(engine_id,
                'pivoting(4, [3, 5, 9, 5, 2], S, G), (S = [] ; G = [])'):
            return [{'id': 'all_elements_in_either_S_or_G'}]

        if prolog.engine.ask_truthTO(engine_id, '''\
                \+ pivoting(4, [3, 5], _, _),
                (asserta(pivoting(4, [5], [3, 5], [yowza])),
                 pivoting(4, [3, 5], [5], [yowza]),
                 retract(pivoting(4, [5], [3, 5], [yowza]))
                 ;
                 asserta(pivoting(4, [3], [yowza], [5, 3])),
                 pivoting(4, [5, 3], [yowza], [3]),
                 retract(pivoting(4, [3], [yowza], [5, 3])))'''):
            return [{'id': 'forcing_result_onto_recursion'}]

        # missing/failed base case
        if not prolog.engine.ask_truthTO(engine_id, 'pivoting(_, [], [], [])'):
            return [{'id': 'base_case'}]

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

        # base case works, the recursive doesn't (but it doesn't timeout)
        # this may be left as the last, most generic hint
        if not prolog.engine.ask_truth(engine_id,
                'pivoting(4, [2, 1, 8, 9, 3, 4, 2], [2, 1, 3, 4, 2], [8, 9])'):
            return [{'id': 'recursive_case'}]

    except socket.timeout as ex:
        return [{'id': 'timeout'}]

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

    return []