summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Možina <martin.mozina@fri.uni-lj.si>2016-10-06 12:55:26 +0200
committerMartin Možina <martin.mozina@fri.uni-lj.si>2016-10-06 12:55:26 +0200
commit969812724912512740dbf037940a9e6770df19ed (patch)
tree7b738a757a9e425eefac4721b96ee9a482d42f90
parent15871f8f3982fc7c5de03a68229235ea616c6419 (diff)
Added recursion exercises.
-rw-r--r--python/problems/introduction/ballistics/sl.py4
-rw-r--r--python/problems/introduction/leap_year/common.py93
-rw-r--r--python/problems/introduction/leap_year/en.py15
-rw-r--r--python/problems/introduction/leap_year/sl.py65
-rw-r--r--python/problems/recursion/common.py2
-rw-r--r--python/problems/recursion/depth_to/common.py107
-rw-r--r--python/problems/recursion/depth_to/en.py13
-rw-r--r--python/problems/recursion/depth_to/sl.py31
-rw-r--r--python/problems/recursion/en.py3
-rw-r--r--python/problems/recursion/family_tree_name/common.py103
-rw-r--r--python/problems/recursion/family_tree_name/en.py13
-rw-r--r--python/problems/recursion/family_tree_name/sl.py42
-rw-r--r--python/problems/recursion/family_tree_name_list/common.py104
-rw-r--r--python/problems/recursion/family_tree_name_list/en.py13
-rw-r--r--python/problems/recursion/family_tree_name_list/sl.py28
-rw-r--r--python/problems/recursion/family_tree_youngest/common.py104
-rw-r--r--python/problems/recursion/family_tree_youngest/en.py13
-rw-r--r--python/problems/recursion/family_tree_youngest/sl.py29
-rw-r--r--python/problems/recursion/novak.pngbin0 -> 100267 bytes
-rw-r--r--python/problems/recursion/path_to/common.py108
-rw-r--r--python/problems/recursion/path_to/en.py13
-rw-r--r--python/problems/recursion/path_to/sl.py30
-rw-r--r--python/problems/recursion/sl.py3
-rw-r--r--python/problems/recursion/slovar.html44
24 files changed, 978 insertions, 2 deletions
diff --git a/python/problems/introduction/ballistics/sl.py b/python/problems/introduction/ballistics/sl.py
index b0f33e7..d4e03eb 100644
--- a/python/problems/introduction/ballistics/sl.py
+++ b/python/problems/introduction/ballistics/sl.py
@@ -15,7 +15,7 @@ bo letela krogla.</p>
Poskusi, kako daleč gre pod kotom 45 in kako daleč pod 46 stopinj -- po 45 mora leteti dlje.
Preveri tudi kot 50 stopinj: če pod tem kotom leti nazaj (razdalja je negativna),
si ga gotovo nekje polomil.
-<p><i>Opomba: za pravilno delovanje moraš v programu najprej prebrati kot in potem hitrost.</i></p>
+<p><i>Opomba: za pravilno testiranje moraš v programu najprej prebrati kot in potem hitrost.</i></p>
'''
eval_expression = ['''\
@@ -56,7 +56,7 @@ hint = {
'radians': ['''\
<p>Poskusi <code>sin(30)</code>. Zakaj je rezultat negativen?</p>''',
'''\
-<p>Vse triginometrične funkcije sprejemajo kot v radianih in ne v stopinjah.</p>''',
+<p>Vse triginometrične funkcije sprejemajo kot v radianih in ne v stopinjah.</p>''',
''' <p>V stopinjah ima cel krog 360°, v radianih pa \( 2 \\pi \). </p>''',
''' <p>Formula za pretvorbo med stopinjami in radiani je: </p>
<pre>
diff --git a/python/problems/introduction/leap_year/common.py b/python/problems/introduction/leap_year/common.py
new file mode 100644
index 0000000..7dc7318
--- /dev/null
+++ b/python/problems/introduction/leap_year/common.py
@@ -0,0 +1,93 @@
+from python.util import has_token_sequence, string_almost_equal, \
+ string_contains_number, get_tokens, get_numbers, get_exception_desc
+from server.hints import Hint
+import re
+
+id = 20107
+number = 7
+visible = True
+
+solution = '''\
+leto = int(input("Vnesi leto: "))
+if leto % 4 == 0 and (leto % 100 != 0 or leto % 400 == 0):
+ print("Je prestopno")
+else:
+ print("Ni prestopno")
+'''
+
+hint_type = {
+ 'mod_expression': Hint('mod_expression'),
+ 'printing': Hint('printing'),
+ 'name_error': Hint('name_error'),
+ 'unsupported_operand': Hint('unsupported_operand'),
+ 'final_hint': Hint('final_hint'),
+}
+
+def test(python, code):
+ # List of inputs: (expression to eval, stdin).
+ test_in = [
+ (None, '1900\n'),
+ (None, '1950\n'),
+ (None, '2000\n'),
+ (None, '2016\n'),
+ (None, '2100\n'),
+ ]
+ test_out = [
+ "Ni prestopno",
+ "Ni prestopno",
+ "Je prestopno",
+ "Je prestopno",
+ "Ni prestopno"
+ ]
+
+ # List of outputs: (expression result, stdout, stderr, exception).
+ answers = python(code=code, inputs=test_in, timeout=1.0)
+ outputs = [ans[1] for ans in answers]
+
+ n_correct = 0
+ tin = None
+ for i, (output, correct) in enumerate(zip(outputs, test_out)):
+ if correct in output:
+ n_correct += 1
+ else:
+ tin = test_in[i][1]
+ tout = correct
+
+ passed = n_correct == len(test_in)
+ hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}]
+ if tin:
+ hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}})
+ if passed:
+ hints.append({'id': 'final_hint'})
+ return passed, hints
+
+def hint(python, code):
+ tokens = get_tokens(code)
+
+ # run one test first to see if there are any exceptions
+ test_in = [(None, '212\n')]
+ answer = python(code=code, inputs=test_in, timeout=1.0)
+ exc = answer[0][3]
+ exc_hint = get_exception_desc(answer[0][3])
+ if exc:
+ if 'NameError' in exc:
+ return [{'id':'name_error', 'args': {'message': exc}}]
+ elif 'not callable' in exc:
+ return [{'id':'not_callable', 'args': {'message': exc}}]
+ elif 'unsupported operand' in exc:
+ return [{'id':'unsupported_operand', 'args': {'message': exc}}]
+ elif 'TypeError' in exc:
+ return [{'id':'unsupported_operand', 'args': {'message': exc}}]
+ else:
+ return exc_hint
+
+ # if token % not in code, we have to teach them how to
+ # evaluate expressions.
+ if (not has_token_sequence(tokens, ['%'])):
+ return [{'id' : 'mod_expression'}]
+
+ # student is not using print function
+ if not has_token_sequence(tokens, ['print']):
+ return [{'id' : 'printing'}]
+
+ return None
diff --git a/python/problems/introduction/leap_year/en.py b/python/problems/introduction/leap_year/en.py
new file mode 100644
index 0000000..a2419f9
--- /dev/null
+++ b/python/problems/introduction/leap_year/en.py
@@ -0,0 +1,15 @@
+name = '*Leap year'
+
+description = '''\
+<p>Write a program that reads a number representing a year and determines whether
+this is a leap year. A leap year is divisible by 4, but not divisible by 100,
+except if it is divisible also by 400. Thence years 2004, 2008, and 2000 are
+leap years, while 2005 and 2100 are not.</p>'''
+
+hint = {
+ 'plan': '''\
+<p>(translation missing)</p>''',
+
+ 'no_input_call': '''\
+<p>(translation missing)</p>''',
+}
diff --git a/python/problems/introduction/leap_year/sl.py b/python/problems/introduction/leap_year/sl.py
new file mode 100644
index 0000000..236e761
--- /dev/null
+++ b/python/problems/introduction/leap_year/sl.py
@@ -0,0 +1,65 @@
+import server
+mod = server.problems.load_language('python', 'sl')
+
+name = '*Prestopno leto'
+
+description = '''\
+<p> Napiši program, ki preveri ali je leto prestopno. Leto je prestopno,
+ če je deljivo s 4, razen v primeru, ko je deljivo s 100,
+ takrat leto ni prestopno, razen takrat, ko je deljivo s 400,
+ takrat je leto prestopno. Tako so leta 2004, 2008 in 2000 prestopna,
+ leti 2005 in 2100 pa ne. </p>
+
+ <p> Če je leto prestopno, naj izpiše "Je prestopno", če ni, "Ni prestopno". '''
+
+printing = ['''\
+<p>V Pythonu izpisujemo s funkcijo <code>print</code>.</p>''',
+ '''\
+</p>Če želimo izpisati več elementov, jih ločimo z vejico.
+Imejmo spremenljivko <code>ime</code>, ki vsebuje naše ime, potem:
+<pre>
+print("Ime mi je", ime, ".")
+</pre>''']
+
+mod_expression = ['''\
+<p>Ostanek pri deljenju dobiš z operatorjem %.''']
+
+plan = ['''\
+<p>1. Preberi vrednost 2.izračunaj 3.izpiši.</p>''',
+ mod_expression,
+ printing
+
+]
+hint = {
+
+ 'mod_expression': mod_expression,
+
+ 'printing': printing,
+
+ 'name_error' : [
+ mod.general_msg['error_head'],
+ mod.general_msg['general_exception'],
+ mod.general_msg['name_error'],
+ '''\
+<p>Verjetno uporabljaš spremenljivko, ki nima vrednosti. Ali v izrazu za
+izračun uporabljaš napačno spremenljivko? Ali pri izpisu morda poskušaš
+izpisati napačno spremenljivko?</p>'''
+ ],
+
+ 'unsupported_operand' : [
+ mod.general_msg['error_head'],
+ mod.general_msg['general_exception'],
+ mod.general_msg['type_error'],
+ '''\
+<p>Verjetni razlog: funkcija <code>input</code> vrača vrednost tipa niz, ki jo
+moramo najprej pretvoriti v tip <code>int</code>, če želimo z njo
+računati:</p>
+<pre>
+v = int(input(" ...
+</pre>''',],
+
+ 'final_hint' : [
+ '''\
+<p>Odlično, program deluje!<br>
+''']
+}
diff --git a/python/problems/recursion/common.py b/python/problems/recursion/common.py
new file mode 100644
index 0000000..f04b38b
--- /dev/null
+++ b/python/problems/recursion/common.py
@@ -0,0 +1,2 @@
+id = 2008
+number = 8 \ No newline at end of file
diff --git a/python/problems/recursion/depth_to/common.py b/python/problems/recursion/depth_to/common.py
new file mode 100644
index 0000000..3aad348
--- /dev/null
+++ b/python/problems/recursion/depth_to/common.py
@@ -0,0 +1,107 @@
+import re
+from python.util import has_token_sequence, string_almost_equal, \
+ string_contains_number, get_tokens, get_numbers, get_exception_desc, \
+ all_tokens, has_comprehension, has_loop, almost_equal, get_ast
+from server.hints import Hint
+
+id = 20804
+number = 4
+visible = True
+
+solution = '''\
+def depthto(fr, to):
+ if fr == to:
+ return 0
+ for c in children[fr]:
+ d = depthto(c, to)
+ if d is not None:
+ return d + 1
+'''
+
+precode = """
+children = {
+ "Adam": ["Matjaž", "Cilka", "Daniel", "Erik"],
+ "Aleksander": [],
+ "Alenka": [],
+ "Barbara": [],
+ "Cilka": [],
+ "Daniel": ["Elizabeta", "Hans"],
+ "Erik": [],
+ "Elizabeta": ["Ludvik", "Jurij", "Barbara", "Herman", "Mihael"],
+ "Franc": [],
+ "Herman": ["Margareta"],
+ "Hans": [],
+ "Jožef": ["Alenka", "Aleksander", "Petra"],
+ "Jurij": ["Franc", "Jožef"],
+ "Ludvik": [],
+ "Margareta": [],
+ "Matjaž": ["Viljem"],
+ "Mihael": [],
+ "Petra": [],
+ "Tadeja": [],
+ "Viljem": ["Tadeja"],
+}
+
+age = {
+ "Adam": 111, "Matjaž": 90, "Cilka": 88, "Daniel": 85, "Erik": 83,
+ "Viljem": 58, "Tadeja": 20, "Elizabeta": 67, "Hans": 64, "Ludvik": 50,
+ "Jurij": 49, "Barbara": 45, "Herman": 39, "Mihael": 32, "Franc": 30,
+ "Jožef": 29, "Margareta": 10, "Alenka": 5, "Aleksander": 7, "Petra": 9
+}"""
+
+hint_type = {
+ 'final_hint': Hint('final_hint')
+}
+
+def test(python, code):
+ code = precode + "\n" + code
+ func_name = 'depthto'
+ tokens = get_tokens(code)
+ if not has_token_sequence(tokens, ['def', func_name]):
+ return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}]
+
+ in_out = [
+ (('Hans', 'Hans'), 0),
+ (('Daniel', 'Hans'), 1),
+ (('Adam', 'Hans'), 2),
+ (('Adam', 'Franc'), 4),
+ (('Adam', 'Petra'), 5),
+ (('Elizabeta', 'Aleksander'), 3),
+ (('Adam', 'Tadeja'), 3),
+ (('Daniel', 'Ludvik'), 2)
+ ]
+
+ test_in = [('{0}{1}'.format(func_name, str(l[0])), None)
+ for l in in_out]
+ test_out = [l[1] for l in in_out]
+
+ answers = python(code=code, inputs=test_in, timeout=1.0)
+ n_correct = 0
+ tin, tout = None, None
+ for i, (ans, to) in enumerate(zip(answers, test_out)):
+ res = ans[0]
+ corr = res == to
+ n_correct += corr
+ if not corr:
+ tin = test_in[i][0]
+ tout = to
+
+ passed = n_correct == len(test_in)
+ hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}]
+ if tin:
+ hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}})
+ if passed:
+ hints.append({'id': 'final_hint'})
+
+ return passed, hints
+
+
+def hint(python, code):
+ tokens = get_tokens(code)
+
+ # run one test first to see if there are any exceptions
+ answer = python(code=code, inputs=[(None, None)], timeout=1.0)
+ exc = get_exception_desc(answer[0][3])
+ if exc: return exc
+
+ return None
diff --git a/python/problems/recursion/depth_to/en.py b/python/problems/recursion/depth_to/en.py
new file mode 100644
index 0000000..9cb993d
--- /dev/null
+++ b/python/problems/recursion/depth_to/en.py
@@ -0,0 +1,13 @@
+id = 20804
+name = 'Family tree: depth to'
+
+description = '''\
+<p>(translation missing)</p>'''
+
+hint = {
+ 'plan': '''\
+<p>(translation missing)</p>''',
+
+ 'no_input_call': '''\
+<p>(translation missing)</p>''',
+}
diff --git a/python/problems/recursion/depth_to/sl.py b/python/problems/recursion/depth_to/sl.py
new file mode 100644
index 0000000..e4315c0
--- /dev/null
+++ b/python/problems/recursion/depth_to/sl.py
@@ -0,0 +1,31 @@
+import server
+mod = server.problems.load_language('python', 'sl')
+
+id = 20804
+name = 'Družinsko drevo: globina do'
+
+description = '''\
+<p>
+Napišite funkcijo <code>depthto(fr, to)</code>, ki pove kako globoko v rodbini
+osebe <code>fr</code> je oseba z imenom <code>to</code>.
+Uporabite družinsko
+drevo (<a target="_blank" href="[%@resource novak.png%]">slika</a> in
+<a target="_blank" href="[%@resource slovar.html%]">slovar</a>). </p>
+<pre>
+>>> depthto('Daniel', 'Hans')
+1
+>>> depthto('Adam', 'Hans')
+2
+>>> depthto('Adam', 'Franc')
+4
+</pre>
+'''
+
+plan = []
+
+hint = {
+ 'final_hint': ['''\
+<p>Program je pravilen! <br>
+</p>
+'''],
+}
diff --git a/python/problems/recursion/en.py b/python/problems/recursion/en.py
new file mode 100644
index 0000000..a1e8f91
--- /dev/null
+++ b/python/problems/recursion/en.py
@@ -0,0 +1,3 @@
+name = 'Recursion'
+description = ''
+
diff --git a/python/problems/recursion/family_tree_name/common.py b/python/problems/recursion/family_tree_name/common.py
new file mode 100644
index 0000000..d924665
--- /dev/null
+++ b/python/problems/recursion/family_tree_name/common.py
@@ -0,0 +1,103 @@
+import re
+from python.util import has_token_sequence, string_almost_equal, \
+ string_contains_number, get_tokens, get_numbers, get_exception_desc, \
+ all_tokens, has_comprehension, has_loop, almost_equal, get_ast
+from server.hints import Hint
+
+id = 20801
+number = 1
+visible = True
+
+solution = '''\
+def printnames(name):
+ print(name)
+ for c in children[name]:
+ printnames(c)
+'''
+
+hint_type = {
+ 'final_hint': Hint('final_hint')
+}
+
+precode = """
+children = {
+ "Adam": ["Matjaž", "Cilka", "Daniel", "Erik"],
+ "Aleksander": [],
+ "Alenka": [],
+ "Barbara": [],
+ "Cilka": [],
+ "Daniel": ["Elizabeta", "Hans"],
+ "Erik": [],
+ "Elizabeta": ["Ludvik", "Jurij", "Barbara", "Herman", "Mihael"],
+ "Franc": [],
+ "Herman": ["Margareta"],
+ "Hans": [],
+ "Jožef": ["Alenka", "Aleksander", "Petra"],
+ "Jurij": ["Franc", "Jožef"],
+ "Ludvik": [],
+ "Margareta": [],
+ "Matjaž": ["Viljem"],
+ "Mihael": [],
+ "Petra": [],
+ "Tadeja": [],
+ "Viljem": ["Tadeja"],
+}
+
+age = {
+ "Adam": 111, "Matjaž": 90, "Cilka": 88, "Daniel": 85, "Erik": 83,
+ "Viljem": 58, "Tadeja": 20, "Elizabeta": 67, "Hans": 64, "Ludvik": 50,
+ "Jurij": 49, "Barbara": 45, "Herman": 39, "Mihael": 32, "Franc": 30,
+ "Jožef": 29, "Margareta": 10, "Alenka": 5, "Aleksander": 7, "Petra": 9
+}"""
+
+def test(python, code):
+ code = precode + "\n" + code
+
+ func_name = 'printnames'
+ tokens = get_tokens(code)
+ if not has_token_sequence(tokens, ['def', func_name]):
+ return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}]
+
+ in_out = [
+ ('Hans', ['Hans']),
+ ('Herman', ['Herman', 'Margareta']),
+ ('Jurij', ['Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra']),
+ ('Viljem', ['Viljem', 'Tadeja']),
+ ('Daniel', ['Daniel', 'Elizabeta', 'Ludvik', 'Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra', 'Barbara', 'Herman', 'Margareta', 'Mihael', 'Hans']),
+ ('Adam', ['Adam', 'Matjaž', 'Viljem', 'Tadeja', 'Cilka', 'Daniel', 'Elizabeta', 'Ludvik', 'Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra', 'Barbara', 'Herman', 'Margareta', 'Mihael', 'Hans', 'Erik'])
+ ]
+
+ test_in = [('{0}("{1}")'.format(func_name, str(l[0])), None)
+ for l in in_out]
+ test_out = [l[1] for l in in_out]
+
+ answers = python(code=code, inputs=test_in, timeout=1.0)
+ n_correct = 0
+ tin, tout = None, None
+ for i, (ans, to) in enumerate(zip(answers, test_out)):
+ res = ans[1].splitlines()
+ corr = len(res) == len(to) and set(res) == set(to)
+ n_correct += corr
+ if not corr:
+ tin = test_in[i][0]
+ tout = to
+
+ passed = n_correct == len(test_in)
+ hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}]
+ if tin:
+ hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}})
+ if passed:
+ hints.append({'id': 'final_hint'})
+
+ return passed, hints
+
+
+def hint(python, code):
+ tokens = get_tokens(code)
+
+ # run one test first to see if there are any exceptions
+ answer = python(code=code, inputs=[(None, None)], timeout=1.0)
+ exc = get_exception_desc(answer[0][3])
+ if exc: return exc
+
+ return None
diff --git a/python/problems/recursion/family_tree_name/en.py b/python/problems/recursion/family_tree_name/en.py
new file mode 100644
index 0000000..b5621df
--- /dev/null
+++ b/python/problems/recursion/family_tree_name/en.py
@@ -0,0 +1,13 @@
+id = 20801
+name = 'Family tree: printing names'
+
+description = '''\
+<p>(translation missing)</p>'''
+
+hint = {
+ 'plan': '''\
+<p>(translation missing)</p>''',
+
+ 'no_input_call': '''\
+<p>(translation missing)</p>''',
+}
diff --git a/python/problems/recursion/family_tree_name/sl.py b/python/problems/recursion/family_tree_name/sl.py
new file mode 100644
index 0000000..ff42c50
--- /dev/null
+++ b/python/problems/recursion/family_tree_name/sl.py
@@ -0,0 +1,42 @@
+import server
+mod = server.problems.load_language('python', 'sl')
+
+id = 20801
+name = 'Družinsko drevo: izpis imen'
+
+description = '''\
+<p>
+Napišite funkcijo <code>printnames(name)</code>, ki izpiše vsa imena v določeni rodbini.
+Uporabite družinsko
+drevo (<a target="_blank" href="[%@resource novak.png%]">slika</a> in
+<a target="_blank" href="[%@resource slovar.html%]">slovar</a>),
+ki ste ga spoznali na predavanjih. </p>
+<pre>
+>>> printnames('Hans')
+Hans
+>>> printnames('Daniel')
+Daniel
+Elizabeta
+Ludvik
+Jurij
+Franc
+Jožef
+Alenka
+Aleksander
+Petra
+Barbara
+Herman
+Margareta
+Mihael
+Hans
+</pre>
+'''
+
+plan = []
+
+hint = {
+ 'final_hint': ['''\
+<p>Program je pravilen! <br>
+</p>
+'''],
+}
diff --git a/python/problems/recursion/family_tree_name_list/common.py b/python/problems/recursion/family_tree_name_list/common.py
new file mode 100644
index 0000000..880d8b1
--- /dev/null
+++ b/python/problems/recursion/family_tree_name_list/common.py
@@ -0,0 +1,104 @@
+import re
+from python.util import has_token_sequence, string_almost_equal, \
+ string_contains_number, get_tokens, get_numbers, get_exception_desc, \
+ all_tokens, has_comprehension, has_loop, almost_equal, get_ast
+from server.hints import Hint
+
+id = 20802
+number = 2
+visible = True
+
+solution = '''\
+def names(name):
+ xs = [name]
+ for c in children[ime]:
+ xs.extend(names(c))
+ return xs
+'''
+
+hint_type = {
+ 'final_hint': Hint('final_hint')
+}
+
+precode = """
+children = {
+ "Adam": ["Matjaž", "Cilka", "Daniel", "Erik"],
+ "Aleksander": [],
+ "Alenka": [],
+ "Barbara": [],
+ "Cilka": [],
+ "Daniel": ["Elizabeta", "Hans"],
+ "Erik": [],
+ "Elizabeta": ["Ludvik", "Jurij", "Barbara", "Herman", "Mihael"],
+ "Franc": [],
+ "Herman": ["Margareta"],
+ "Hans": [],
+ "Jožef": ["Alenka", "Aleksander", "Petra"],
+ "Jurij": ["Franc", "Jožef"],
+ "Ludvik": [],
+ "Margareta": [],
+ "Matjaž": ["Viljem"],
+ "Mihael": [],
+ "Petra": [],
+ "Tadeja": [],
+ "Viljem": ["Tadeja"],
+}
+
+age = {
+ "Adam": 111, "Matjaž": 90, "Cilka": 88, "Daniel": 85, "Erik": 83,
+ "Viljem": 58, "Tadeja": 20, "Elizabeta": 67, "Hans": 64, "Ludvik": 50,
+ "Jurij": 49, "Barbara": 45, "Herman": 39, "Mihael": 32, "Franc": 30,
+ "Jožef": 29, "Margareta": 10, "Alenka": 5, "Aleksander": 7, "Petra": 9
+}"""
+
+def test(python, code):
+ code = precode + "\n" + code
+
+ func_name = 'names'
+ tokens = get_tokens(code)
+ if not has_token_sequence(tokens, ['def', func_name]):
+ return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}]
+
+ in_out = [
+ ('Hans', ['Hans']),
+ ('Herman', ['Herman', 'Margareta']),
+ ('Jurij', ['Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra']),
+ ('Viljem', ['Viljem', 'Tadeja']),
+ ('Daniel', ['Daniel', 'Elizabeta', 'Ludvik', 'Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra', 'Barbara', 'Herman', 'Margareta', 'Mihael', 'Hans']),
+ ('Adam', ['Adam', 'Matjaž', 'Viljem', 'Tadeja', 'Cilka', 'Daniel', 'Elizabeta', 'Ludvik', 'Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra', 'Barbara', 'Herman', 'Margareta', 'Mihael', 'Hans', 'Erik'])
+ ]
+
+ test_in = [('{0}("{1}")'.format(func_name, str(l[0])), None)
+ for l in in_out]
+ test_out = [l[1] for l in in_out]
+
+ answers = python(code=code, inputs=test_in, timeout=1.0)
+ n_correct = 0
+ tin, tout = None, None
+ for i, (ans, to) in enumerate(zip(answers, test_out)):
+ res = ans[0]
+ corr = len(res) == len(to) and set(res) == set(to)
+ n_correct += corr
+ if not corr:
+ tin = test_in[i][0]
+ tout = to
+
+ passed = n_correct == len(test_in)
+ hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}]
+ if tin:
+ hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}})
+ if passed:
+ hints.append({'id': 'final_hint'})
+
+ return passed, hints
+
+
+def hint(python, code):
+ tokens = get_tokens(code)
+
+ # run one test first to see if there are any exceptions
+ answer = python(code=code, inputs=[(None, None)], timeout=1.0)
+ exc = get_exception_desc(answer[0][3])
+ if exc: return exc
+
+ return None
diff --git a/python/problems/recursion/family_tree_name_list/en.py b/python/problems/recursion/family_tree_name_list/en.py
new file mode 100644
index 0000000..9d1820a
--- /dev/null
+++ b/python/problems/recursion/family_tree_name_list/en.py
@@ -0,0 +1,13 @@
+id = 20802
+name = 'Family tree: list of names'
+
+description = '''\
+<p>(translation missing)</p>'''
+
+hint = {
+ 'plan': '''\
+<p>(translation missing)</p>''',
+
+ 'no_input_call': '''\
+<p>(translation missing)</p>''',
+}
diff --git a/python/problems/recursion/family_tree_name_list/sl.py b/python/problems/recursion/family_tree_name_list/sl.py
new file mode 100644
index 0000000..261e401
--- /dev/null
+++ b/python/problems/recursion/family_tree_name_list/sl.py
@@ -0,0 +1,28 @@
+import server
+mod = server.problems.load_language('python', 'sl')
+
+id = 20802
+name = 'Družinsko drevo: seznam imen'
+
+description = '''\
+<p>
+Podobno kot pri prejšnji nalogi napišite funkcijo <code>names(name)</code>, ki
+tokrat vrne vse imena v seznamu. Uporabite družinsko
+drevo (<a target="_blank" href="[%@resource novak.png%]">slika</a> in
+<a target="_blank" href="[%@resource slovar.html%]">slovar</a>). </p>
+<pre>
+>>> names('Hans')
+['Hans']
+>>> names('Daniel')
+['Daniel', 'Elizabeta', 'Ludvik', 'Jurij', 'Franc', 'Jožef', 'Alenka', 'Aleksander', 'Petra', 'Barbara', 'Herman', 'Margareta', 'Mihael', 'Hans']
+</pre>
+'''
+
+plan = []
+
+hint = {
+ 'final_hint': ['''\
+<p>Program je pravilen! <br>
+</p>
+'''],
+}
diff --git a/python/problems/recursion/family_tree_youngest/common.py b/python/problems/recursion/family_tree_youngest/common.py
new file mode 100644
index 0000000..099c1d3
--- /dev/null
+++ b/python/problems/recursion/family_tree_youngest/common.py
@@ -0,0 +1,104 @@
+import re
+from python.util import has_token_sequence, string_almost_equal, \
+ string_contains_number, get_tokens, get_numbers, get_exception_desc, \
+ all_tokens, has_comprehension, has_loop, almost_equal, get_ast
+from server.hints import Hint
+
+id = 20803
+number = 3
+visible = True
+
+solution = '''\
+def youngest(name):
+ xs = [(age[name], name)]
+ for c in children[name]:
+ xs.append(youngest(c))
+ return min(xs)
+'''
+
+hint_type = {
+ 'final_hint': Hint('final_hint')
+}
+
+precode = """
+children = {
+ "Adam": ["Matjaž", "Cilka", "Daniel", "Erik"],
+ "Aleksander": [],
+ "Alenka": [],
+ "Barbara": [],
+ "Cilka": [],
+ "Daniel": ["Elizabeta", "Hans"],
+ "Erik": [],
+ "Elizabeta": ["Ludvik", "Jurij", "Barbara", "Herman", "Mihael"],
+ "Franc": [],
+ "Herman": ["Margareta"],
+ "Hans": [],
+ "Jožef": ["Alenka", "Aleksander", "Petra"],
+ "Jurij": ["Franc", "Jožef"],
+ "Ludvik": [],
+ "Margareta": [],
+ "Matjaž": ["Viljem"],
+ "Mihael": [],
+ "Petra": [],
+ "Tadeja": [],
+ "Viljem": ["Tadeja"],
+}
+
+age = {
+ "Adam": 111, "Matjaž": 90, "Cilka": 88, "Daniel": 85, "Erik": 83,
+ "Viljem": 58, "Tadeja": 20, "Elizabeta": 67, "Hans": 64, "Ludvik": 50,
+ "Jurij": 49, "Barbara": 45, "Herman": 39, "Mihael": 32, "Franc": 30,
+ "Jožef": 29, "Margareta": 10, "Alenka": 5, "Aleksander": 7, "Petra": 9
+}"""
+
+def test(python, code):
+ code = precode + "\n" + code
+
+ func_name = 'youngest'
+ tokens = get_tokens(code)
+ if not has_token_sequence(tokens, ['def', func_name]):
+ return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}]
+
+ in_out = [
+ ('Hans', (64, 'Hans')),
+ ('Daniel', (5, 'Alenka')),
+ ('Herman', (10, 'Margareta')),
+ ('Viljem', (20, 'Tadeja')),
+ ('Aleksander', (7, 'Aleksander')),
+ ('Adam', (5, 'Alenka'))
+ ]
+
+ test_in = [('{0}("{1}")'.format(func_name, str(l[0])), None)
+ for l in in_out]
+ test_out = [l[1] for l in in_out]
+
+ answers = python(code=code, inputs=test_in, timeout=1.0)
+ n_correct = 0
+ tin, tout = None, None
+ for i, (ans, to) in enumerate(zip(answers, test_out)):
+ res = ans[0]
+ corr = res == to
+ n_correct += corr
+ if not corr:
+ tin = test_in[i][0]
+ tout = to
+
+ passed = n_correct == len(test_in)
+ hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}]
+ if tin:
+ hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}})
+ if passed:
+ hints.append({'id': 'final_hint'})
+
+ return passed, hints
+
+
+def hint(python, code):
+ tokens = get_tokens(code)
+
+ # run one test first to see if there are any exceptions
+ answer = python(code=code, inputs=[(None, None)], timeout=1.0)
+ exc = get_exception_desc(answer[0][3])
+ if exc: return exc
+
+ return None
diff --git a/python/problems/recursion/family_tree_youngest/en.py b/python/problems/recursion/family_tree_youngest/en.py
new file mode 100644
index 0000000..ba3f813
--- /dev/null
+++ b/python/problems/recursion/family_tree_youngest/en.py
@@ -0,0 +1,13 @@
+id = 20803
+name = 'Family tree: the youngest'
+
+description = '''\
+<p>(translation missing)</p>'''
+
+hint = {
+ 'plan': '''\
+<p>(translation missing)</p>''',
+
+ 'no_input_call': '''\
+<p>(translation missing)</p>''',
+}
diff --git a/python/problems/recursion/family_tree_youngest/sl.py b/python/problems/recursion/family_tree_youngest/sl.py
new file mode 100644
index 0000000..04c2700
--- /dev/null
+++ b/python/problems/recursion/family_tree_youngest/sl.py
@@ -0,0 +1,29 @@
+import server
+mod = server.problems.load_language('python', 'sl')
+
+id = 20803
+name = 'Družinsko drevo: najmlajši'
+
+description = '''\
+<p>
+Funkcija <code>youngest(name)</code> naj vrne starost in ime
+najmlajšega člana določene rodbine.
+Uporabite družinsko
+drevo (<a target="_blank" href="[%@resource novak.png%]">slika</a> in
+<a target="_blank" href="[%@resource slovar.html%]">slovar</a>). </p>
+<pre>
+>>> youngest('Hans')
+(64, 'Hans')
+>>> youngest('Daniel')
+(5, 'Alenka')
+</pre>
+'''
+
+plan = []
+
+hint = {
+ 'final_hint': ['''\
+<p>Program je pravilen! <br>
+</p>
+'''],
+}
diff --git a/python/problems/recursion/novak.png b/python/problems/recursion/novak.png
new file mode 100644
index 0000000..6bfc520
--- /dev/null
+++ b/python/problems/recursion/novak.png
Binary files differ
diff --git a/python/problems/recursion/path_to/common.py b/python/problems/recursion/path_to/common.py
new file mode 100644
index 0000000..cf63b6c
--- /dev/null
+++ b/python/problems/recursion/path_to/common.py
@@ -0,0 +1,108 @@
+import re
+from python.util import has_token_sequence, string_almost_equal, \
+ string_contains_number, get_tokens, get_numbers, get_exception_desc, \
+ all_tokens, has_comprehension, has_loop, almost_equal, get_ast
+from server.hints import Hint
+
+id = 20805
+number = 5
+visible = True
+
+solution = '''\
+def pathto(fr, to):
+ if fr == to:
+ return [fr]
+ for c in children[fr]:
+ d = pathto(c, to)
+ if d is not None:
+ return [fr] + d
+'''
+
+hint_type = {
+ 'final_hint': Hint('final_hint')
+}
+
+precode = """
+children = {
+ "Adam": ["Matjaž", "Cilka", "Daniel", "Erik"],
+ "Aleksander": [],
+ "Alenka": [],
+ "Barbara": [],
+ "Cilka": [],
+ "Daniel": ["Elizabeta", "Hans"],
+ "Erik": [],
+ "Elizabeta": ["Ludvik", "Jurij", "Barbara", "Herman", "Mihael"],
+ "Franc": [],
+ "Herman": ["Margareta"],
+ "Hans": [],
+ "Jožef": ["Alenka", "Aleksander", "Petra"],
+ "Jurij": ["Franc", "Jožef"],
+ "Ludvik": [],
+ "Margareta": [],
+ "Matjaž": ["Viljem"],
+ "Mihael": [],
+ "Petra": [],
+ "Tadeja": [],
+ "Viljem": ["Tadeja"],
+}
+
+age = {
+ "Adam": 111, "Matjaž": 90, "Cilka": 88, "Daniel": 85, "Erik": 83,
+ "Viljem": 58, "Tadeja": 20, "Elizabeta": 67, "Hans": 64, "Ludvik": 50,
+ "Jurij": 49, "Barbara": 45, "Herman": 39, "Mihael": 32, "Franc": 30,
+ "Jožef": 29, "Margareta": 10, "Alenka": 5, "Aleksander": 7, "Petra": 9
+}"""
+
+def test(python, code):
+ code = precode + "\n" + code
+
+ func_name = 'pathto'
+ tokens = get_tokens(code)
+ if not has_token_sequence(tokens, ['def', func_name]):
+ return False, [{'id' : 'no_func_name', 'args' : {'func_name' : func_name}}]
+
+ in_out = [
+ (('Hans', 'Hans'), ['Hans']),
+ (('Daniel', 'Hans'), ['Daniel', 'Hans']),
+ (('Adam', 'Hans'), ['Adam', 'Daniel', 'Hans']),
+ (('Adam', 'Franc'), ['Adam', 'Daniel', 'Elizabeta', 'Jurij', 'Franc']),
+ (('Adam', 'Petra'), ['Adam', 'Daniel', 'Elizabeta', 'Jurij', 'Jožef', 'Petra']),
+ (('Elizabeta', 'Aleksander'), ['Elizabeta', 'Jurij', 'Jožef', 'Aleksander']),
+ (('Adam', 'Tadeja'), ['Adam', 'Matjaž', 'Viljem', 'Tadeja']),
+ (('Daniel', 'Ludvik'), ['Daniel', 'Elizabeta', 'Ludvik'])
+ ]
+
+ test_in = [('{0}{1}'.format(func_name, str(l[0])), None)
+ for l in in_out]
+ test_out = [l[1] for l in in_out]
+
+ answers = python(code=code, inputs=test_in, timeout=1.0)
+ n_correct = 0
+ tin, tout = None, None
+ for i, (ans, to) in enumerate(zip(answers, test_out)):
+ res = ans[0]
+ corr = res == to
+ n_correct += corr
+ if not corr:
+ tin = test_in[i][0]
+ tout = to
+
+ passed = n_correct == len(test_in)
+ hints = [{'id': 'test_results', 'args': {'passed': n_correct, 'total': len(test_in)}}]
+ if tin:
+ hints.append({'id': 'problematic_test_case', 'args': {'testin': str(tin), 'testout': str(tout)}})
+ if passed:
+ hints.append({'id': 'final_hint'})
+
+ return passed, hints
+
+
+def hint(python, code):
+ tokens = get_tokens(code)
+
+ # run one test first to see if there are any exceptions
+ answer = python(code=code, inputs=[(None, None)], timeout=1.0)
+ exc = get_exception_desc(answer[0][3])
+ if exc: return exc
+
+ return None
diff --git a/python/problems/recursion/path_to/en.py b/python/problems/recursion/path_to/en.py
new file mode 100644
index 0000000..703d818
--- /dev/null
+++ b/python/problems/recursion/path_to/en.py
@@ -0,0 +1,13 @@
+id = 20805
+name = 'Family tree: path to'
+
+description = '''\
+<p>(translation missing)</p>'''
+
+hint = {
+ 'plan': '''\
+<p>(translation missing)</p>''',
+
+ 'no_input_call': '''\
+<p>(translation missing)</p>''',
+}
diff --git a/python/problems/recursion/path_to/sl.py b/python/problems/recursion/path_to/sl.py
new file mode 100644
index 0000000..b53bd26
--- /dev/null
+++ b/python/problems/recursion/path_to/sl.py
@@ -0,0 +1,30 @@
+import server
+mod = server.problems.load_language('python', 'sl')
+
+id = 20805
+name = 'Družinsko drevo: pot do'
+
+description = '''\
+<p>
+Reši podobno nalogo kot je prejšnja, le da naj funkcija ne vrne razdalje do
+določene osebe, temveč pot do nje. Uporabite družinsko
+drevo (<a target="_blank" href="[%@resource novak.png%]">slika</a> in
+<a target="_blank" href="[%@resource slovar.html%]">slovar</a>). </p>
+<pre>
+>>> pathto('Daniel', 'Hans')
+['Daniel', 'Hans']
+>>> pathto('Adam', 'Hans')
+['Adam', 'Daniel', 'Hans']
+>>> pathto('Adam', 'Franc')
+['Adam', 'Daniel', 'Elizabeta', 'Jurij', 'Franc']
+</pre>
+'''
+
+plan = []
+
+hint = {
+ 'final_hint': ['''\
+<p>Program je pravilen! <br>
+</p>
+'''],
+}
diff --git a/python/problems/recursion/sl.py b/python/problems/recursion/sl.py
new file mode 100644
index 0000000..be7d05a
--- /dev/null
+++ b/python/problems/recursion/sl.py
@@ -0,0 +1,3 @@
+name = 'Rekurzija'
+description = 'Vaje iz rekurzije'
+
diff --git a/python/problems/recursion/slovar.html b/python/problems/recursion/slovar.html
new file mode 100644
index 0000000..ed80a01
--- /dev/null
+++ b/python/problems/recursion/slovar.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="sl">
+<head>
+ <meta charset="utf-8" />
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="/css/codeq.css" />
+ <link rel="stylesheet" type="text/css" href="../../style.css" />
+</head>
+<body>
+
+<pre>
+children = {
+ "Adam": ["Matjaž", "Cilka", "Daniel", "Erik"],
+ "Aleksander": [],
+ "Alenka": [],
+ "Barbara": [],
+ "Cilka": [],
+ "Daniel": ["Elizabeta", "Hans"],
+ "Erik": [],
+ "Elizabeta": ["Ludvik", "Jurij", "Barbara", "Herman", "Mihael"],
+ "Franc": [],
+ "Herman": ["Margareta"],
+ "Hans": [],
+ "Jožef": ["Alenka", "Aleksander", "Petra"],
+ "Jurij": ["Franc", "Jožef"],
+ "Ludvik": [],
+ "Margareta": [],
+ "Matjaž": ["Viljem"],
+ "Mihael": [],
+ "Petra": [],
+ "Tadeja": [],
+ "Viljem": ["Tadeja"],
+}
+
+age = {
+ "Adam": 111, "Matjaž": 90, "Cilka": 88, "Daniel": 85, "Erik": 83,
+ "Viljem": 58, "Tadeja": 20, "Elizabeta": 67, "Hans": 64, "Ludvik": 50,
+ "Jurij": 49, "Barbara": 45, "Herman": 39, "Mihael": 32, "Franc": 30,
+ "Jožef": 29, "Margareta": 10, "Alenka": 5, "Aleksander": 7, "Petra": 9
+}
+</pre>
+
+</body>
+</html>