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
|
from operator import itemgetter
import socket
import prolog.engine
import prolog.util
from server.hints import Hint, HintPopup
id = 104
number = 5
visible = True
facts = None
solution = '''\
conc([], L, L).
conc([H|T], L2, [H|L]) :-
conc(T, L2, L).
'''
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'),
'timeout': Hint('timeout'),
'final_hint': Hint('final_hint'),
'second_list_iteration': Hint('second_list_iteration'),
'forcing_result_onto_recursion': Hint('forcing_result_onto_recursion'),
'two_heads_markup': HintPopup('two_heads_markup'),
'two_heads': Hint('two_heads'),
'insertion_into_second_list': Hint('insertion_into_second_list'),
'base_case_arbitrary': Hint('base_case_arbitrary'),
}
test_cases = [
('conc([j, b], [l], X)',
[{'X': '[j, b, l]'}]),
('conc([a, r, u, e], [c, f], X)',
[{'X': '[a, r, u, e, c, f]'}]),
('conc([a, r, u], [c, f], X)',
[{'X': '[a, r, u, c, f]'}]),
('conc(X, [], [g, r])',
[{'X': '[g, r]'}]),
('conc([], [], [])',
[{}]),
]
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)}}]
if n_correct == len(test_cases):
hints += [{'id': 'final_hint'}]
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'}]
# recursion is getting bigger and bigger
# base case succeeds with arbitrary result
if prolog.engine.ask_truthTO(engine_id, 'conc([], [a,b,c], L), var(L)'):
return [{'id': 'base_case_arbitrary'}]
# shortening the second list instead of first list
if prolog.engine.ask_truthTO(engine_id,
'conc([a,b,c], [1,2,3,4], L), (L = [1,2,3,4,a,b,c] ; L = [4,3,2,1,a,b,c])'):
return [{'id': 'second_list_iteration'}]
# inserting first list's head into the second list in recursive call: conc(T, [H\L2], L)
if prolog.engine.ask_truthTO(engine_id,
'conc([a,b,c,d,e], [1,2,3,4], [e,d,c,b,a,1,2,3,4])'):
return [{'id': 'insertion_into_second_list'}]
# forcing result onto recursion; case of conc(T, L2, [H|L])
if not prolog.engine.ask_truthTO(engine_id, 'conc([a,b,c,d,e], _, _)') and \
prolog.engine.ask_truthTO(engine_id,
'asserta( conc([b,c],[1,2],[yowza,b,c,1,2]) ), conc([yowza,b,c],[1,2],L), retract( conc([b,c],[1,2],[yowza,b,c,1,2]) )'):
return [{'id': 'forcing_result_onto_recursion'}]
# using two heads indicating that the user wants heads from both lists
# TODO: Tim, it would be nice/safer to catch this just for a single clause, not whole code.
if prolog.util.Token('NAME', 'H1') in tokens and \
(prolog.util.Token('NAME', 'H') in tokens or prolog.util.Token('NAME', 'H2') in tokens):
targets = [prolog.util.Token('NAME', 'H1'), prolog.util.Token('NAME', 'H2')]
marks = [(t.pos, t.pos + len(t.val)) for t in tokens if t in targets]
if marks:
return [{'id': 'two_heads_markup', 'start': m[0], 'end': m[1]} for m in marks] + \
[{'id': 'two_heads'}]
# missing/failed base case
if not prolog.engine.ask_one(engine_id, 'conc([], [qa,qb,qc], [qa,qb,qc])'):
return [{'id': 'base_case'}]
# target predicate seems to always be false
if not prolog.engine.ask_truth(engine_id, 'conc([_,_,_,_,_,_], [_,_,_], _)'):
return [{'id': 'predicate_always_false'}]
except socket.timeout as ex:
return [{'id': 'timeout'}]
finally:
if engine_id:
prolog.engine.destroy(engine_id)
return []
|