def correct_nb_iterations(self, actual, **args): """ """ return_code, text_output = actual coms = [rap.title("Iterations count")] C = coms.append R = rap.remark D = rap.disp_msg if return_code == 124: coms.append( rap.remark( "AInfinity", 300, "The execution of your code generates an infinite loop: correction impossible!\n" )) return 0, coms elif return_code != 0: coms.append( rap.remark("AAssist", 200, "The code execution failed due to an error.\n")) return 0, coms if not self.ok_code: C( D("There is no point in testing this if the code is not correct...\n" )) return 0, coms lines = text_output.splitlines() N = int(lines[0].split()[2]) M = int(lines[1].split()[2]) nb_it_stu = max(int(i) for i in lines[7].split()) A = lines[2] B = lines[3] Disp_tab = "".join(("Avec N = ", str(N), ", et M = ", str(M), "\nA : [", A, "]\nB : [", B, "]\n")) if nb_it_stu > N + M: C( R( "ATours", 50, "{}\nYour code generates {} iterations while the maximal limit is {}\n" .format(Disp_tab, nb_it_stu, N + M))) return 0, coms else: coms.append( rap.disp_msg("If N = {}, M = {}, there are {} iterations: {}" "".format(N, M, nb_it_stu, self.get_positive()))) return 1, coms
def statement_respect(self, code, **args): """ """ coms = [rap.title("Compliance with the instructions and constrains")] self.report = None try: self.report = self.stats.compute_report(code, 0, False) except Exception: coms.append( rap.remark("AACode", 1500, "An error occured during the code analysis\n")) return 0, coms rep = self.report if sum((rep.while_count, rep.for_count, rep.dowhile_count)) > 1: coms.append( rap.remark( "ALoopInv", 500, "The code does not comply with the number of loop constrain!\n\n" "Therefore, your grade for the implementation part is 0.\n" )) return 0, coms if sum((rep.return_in_loop, rep.continue_in_loop, rep.break_in_loop, rep.goto_count)) != 0: coms.append( rap.remark( "AGotoEtc", 500, "The code contains break-like instructions like \"break\", \"continue\" or \"goto\"\n\n" "Therefore, your grade for the implementation part is 0.\n" )) return 0, coms if self.report.called_func: coms.append( rap.remark( "AEnonce", 500, "The code calls the following functions: {}\nThis is forbidden for this Challenge!\n" .format(",".join((s for s in self.report.called_func))))) return 0, coms coms.append(rap.disp_msg(self.get_positive())) return 1, coms
def check_sum(actual, **args): """Check the terms in a sum representation The string parameter should represent a sum of integer. This code check if the terms of the sum are correct and give a grade. Args: actual (string): a string representing a sum (numbers separed by '+' sign. Keywords Args: terms (list of int): sorted list of terms """ #FIXME com = [rap.title(args['title'], 1)] grade = 1 if debug: print repr(actual) terms = args['terms'] try: actual_term = [int(s.strip()) for s in actual.split('+')] except ValueError: grade = 0 com.append( rap.remark( "C0.4.2", 50, "Error in the sum expression. You must type numbers that are" "separated with a '+'. Check your answer!\n", 0)) return (grade, com) if terms == sorted(actual_term): com.append(rap.disp_msg(ran.choice(GT_Homework0.POSITIVE), 0)) else: grade = 0 com.append(rap.remark("C0.4.2", 50, "The sum is not correct\n", 0)) return (grade, com)
def check_string(actual, **args): """ Comparation of string """ # First display the title i = 1 com = [rap.title(args['title'], 1)] ground_truth = args['correct'] i += 1 grade = 0 if (ground_truth == actual.lower()): com.append(rap.disp_msg(ran.choice(GT_Homework0.POSITIVE), i)) grade = 1 else: com.append(rap.remark(*(args['remark']))) return (grade, com)
def check_russian_mul(actual, **args): """Check russian multiplication correctness This check the russian multiplication proposed by the student. The proposition is intended to be a string that is parsed by this function. Checking computed: * Correct parsing * Proposition answers to the question * Table size * Multiplication per 2 of multiplicand * Reminders calculation * Table correctness * Final result correctness Args: actual (string): Keyword Args: russia (list of list of int): representing a 2D array (list of lines) of size n * 4. n is the number of lines and can vary whereas the number of column (4) should be fixed. The table a | b | c | d e | f | g | h is hence represented as [[a,b,c,d],[e,f,g,h]] Returns: (int): a grade (over 1). (string): a french commentary that gives feedback about the correctness oth the student's proposal """ #FIXME com = [rap.title(args['title'], 1)] append = com.append # Get the structure from the answer russia_table = args['russia'] """ PARTIE I : data parsing and elementary tests """ if debug: print repr(actual) # Recover the proposed table from the string try: actual_table = [[int(i.strip()) for i in t.split('|')] for t in actual.splitlines()] except ValueError: append( rap.remark( "AEnonce", 200, "An error occured with the Russian peasant multiplication\n" "You typed:\n\"{}\"\n".format(actual), 0)) return (0, com) # Test 1 : premier multiplicande et multiplicateur correct try: if not ((actual_table[0][0] == russia_table[0][0] and actual_table[0][1] == russia_table[0][1]) or (actual_table[0][0] == russia_table[0][1] and actual_table[0][1] == russia_table[0][0])): append( rap.remark( "AEnonce", 200, "The first multiplier and/or the first multiplicand" " are incorrect. Check you are computing the right operation." "\nYou were asked: {} x {}\n".format( russia_table[0][0], russia_table[0][1]), 0)) return (0, com) except IndexError: append( rap.remark( "AEnonce", 200, "Format error for the Russian peasant multiplication\n" "You typed:\n\"{}\"\n".format(actual), 0)) return (0, com) length = int(math.floor(math.log(actual_table[0][1], 2)) + 1) # Test 2 : tables de longueur correcte : if len(actual_table) != length: append( rap.remark( "AEnonce", 200, "The table length is not correct. Do you divide properly the multiplier?\n", 0)) return (0, com) # Test 2b : vérifier qu'il y a bien 4 valeur par ligne : if not all(len(line) == 4 for line in actual_table): append( rap.remark( "AEnonce", 200, "The table width is incorrect: there must be 4 columns:" " Multiplicand, multiplier, Remainder and partial sum. In that order!", 0)) return (0, com) """ PARTIE II : deeper tests """ score = 10 #com = "" # Test 3 : multiplicande est multiplié par 2 à chaque ligne mul2 = all(actual_table[i][0] == 2 * actual_table[i - 1][0] for i in range(1, length)) if not mul2: append( rap.remark( "ARUS_MUL", 75, "The multiplicand must be multiplied by 2 from a row to another.\n", 0)) score -= 2.5 # Calcul des reste sont corrects reminders = all(line[2] == line[1] % 2 for line in actual_table) if not reminders: append( rap.remark("ARUS_MUL", 75, "The remainders are not properly computed\n", 0)) score -= 2.5 # Calcul des multiplicateurs divisés multi = all(actual_table[i][1] == actual_table[i - 1][1] // 2 for i in range(1, length)) # Vérification des sommes partielles sum_part = (all((actual_table[i][3] == actual_table[i - 1][3] + actual_table[i][2] * actual_table[i][0]) for i in range(1, length)) and actual_table[0][3] == actual_table[0][2] * actual_table[0][0]) if not (multi and sum_part): append( rap.remark( "ARUS_MUL", 75, "There are error in the table (concerning multiplicands or partial sum)\n", 0)) score -= 2.5 else: append(rap.disp_msg("Partial sums are correct.\n", 0)) # Résultat final est correct # Index -1 is the last elem... if actual_table[length - 1][3] != russia_table[-1][-1]: append( rap.remark("ARUS_MUL", 75, "The final result is incorrect.\n", 0)) score -= 2.5 else: append(rap.disp_msg("The final result is correct.\n", 0)) return (score / 10.0, com)
def correct_fct_term(self, actual, **args): coms = [rap.title("Loop Variant")] if not self.report: coms.append( rap.disp_msg( "The code analysis has failed. This was previously mentioned.\n" )) return (0, coms) TOTAL = 3.0 grade = 3.0 fb_fct_t = 0 FB_STEP = 20 C = coms.append R = rap.remark D = rap.disp_msg ids = re.findall(r"[a-zA-Z_]\w*", actual) ids_in_guard = self.report.loops_list[0]['ids'] go_on = True # Une fonction t est construite sur base des variables du programme. for i in ids: if i not in ids_in_guard: C( D("The identifier {} does not seem to appear in the loop condition.\n" .format(i))) fb_fct_t += FB_STEP go_on = False varA = self.vars['A'] varB = self.vars['B'] if not (varA in ids and varB in ids): C( D("The variables {} and/or {} does not appear in the Loop Variant expression,\n" "this is a problem\n".format(varA, varB))) fb_fct_t += FB_STEP go_on = False if not go_on: C( R("AFCTT", fb_fct_t, "Continuing to test the Loop Variant is not possible.\n")) return 0, coms # Computaiotn of several values. def compute_t(values): try: val = eval(actual, {}, values) except Exception as e: if DEBUG: print actual, values return None return val tests = ( { varA: 0, varB: 0, 'N': 100.0, 'M': 50.0, 'L': 50.0 }, { varA: 99, varB: 49, 'N': 100.0, 'M': 50.0, 'L': 50.0 }, { varA: 10, varB: 10, 'N': 100.0, 'M': 50.0, 'L': 50.0 }, { varA: 11, varB: 10, 'N': 100.0, 'M': 50.0, 'L': 50.0 }, { varA: 10, varB: 11, 'N': 100.0, 'M': 50.0, 'L': 50.0 }, ) results = [compute_t(v) for v in tests] if any(r is None for r in results): C( R( "AAssist", 1000, "Something went wrong when calculating the value of the Loop Variant\n." )) return 0, coms integer_test = [ isinstance(v, float) and v.is_integer() for v in results ] # Entier if not all(integer_test): fb_fct_t += 2 * FB_STEP C(D("Your Loop Variant does not seem to be an integer function.\n") ) # Positif si B if not all(r > 0 for r in results): fb_fct_t += 2 * FB_STEP C( D("Your Loop variant does not seem to be always positive if the loop condition is true!\n" "-> Check all the possible cases.\n")) # Décroissance (I love that) if not (results[3] < results[2]): fb_fct_t += 3 * FB_STEP C( D("If between 2 iterations, the value of {} is increasing, the value of the Loop Variant should be decreasing.\n" .format(varA))) if not (results[4] < results[2]): fb_fct_t += 3 * FB_STEP C( D("If between 2 iterations, the value of {} is increasing, the value of the Loop Variant should be decreasing.\n" .format(varB))) if fb_fct_t: C(rap.add_ffwd("AFCTT", fb_fct_t)) return 0, coms else: C(D(self.get_positive())) return grade / TOTAL, coms
def correct_invariant(self, actual, **args): """ """ #Some decl. NO = Limit.NO MINUS = Limit.MINUS TOTAL = 10.0 grade = TOTAL BAD_INV_PRIO = 40 # If #failure is high, will be event. > 350 raw_limits = actual coms = [rap.title("Graphical Loop Invariant Correction")] C = coms.append R = rap.remark D = rap.disp_msg limits_list, com_lim = make_limits_list_wFB(raw_limits) C( D("\n".join(( "Here is how the system understood your Invariant:\n", affichage_invariant(limits_list), '', )))) if com_lim: C(R("AEncodage", 100, com_lim)) #var decl. to be used in the following Astart = limits_list[0] Aprev = limits_list[1] Aindex = limits_list[2] Alast = limits_list[3] Asize = limits_list[4] Bstart = limits_list[5] Bprev = limits_list[6] Bindex = limits_list[7] Blast = limits_list[8] Bsize = limits_list[9] Cstart = limits_list[10] Cprev = limits_list[11] Cindex = limits_list[12] Clast = limits_list[13] Csize = limits_list[14] epith = limits_list[15] src1 = limits_list[16] src2 = limits_list[17] inter = limits_list[18] # Invariant's correction fb_inv_needed = 0.0 self.vars = {"A": None, "B": None, "C": None} if len(limits_list) > 19: coms.append( rap.remark("AEncodage", 100, "You must encode 19 boxes.\n")) return (0, coms) def test_one_tab(start, prev, index, last, size, correct_size, name, offset): C(D("\nAbout the array {}:\n──────────────────\n".format(name))) TOTAL = 3.0 grade = TOTAL fb_inv_needed = 0 # mandatory if start != Limit(0, NO): C( D("Box {}: Wrong content! Array indeces all start at...\n". format(0 + offset))) grade -= 1 fb_inv_needed += BAD_INV_PRIO if size != Limit(correct_size, NO): C( D("Box {}: Wrong content! The array size is expected.\n". format(4 + offset))) grade -= 1 fb_inv_needed += BAD_INV_PRIO if not (index.is_var() and index.modif == NO): C( D("Box {}: An array index is expected here!\n".format( 2 + offset))) grade -= 1 fb_inv_needed += BAD_INV_PRIO else: C( D("Box {}: [INFO] A variable was detected: {}\n".format( 2 + offset, index.symbol))) self.vars[name] = index.symbol if prev.is_var() and prev.modif == NO: C( D("Box {}: Considering the arrays sizes types (unsigned int), it is highly recommended to NOT\n" " put the variable on this side of the division bar!\n" .format(1 + offset))) # not mandatory if not (prev.is_unspecified() or (prev == Limit(index.symbol, MINUS))): C( D("Box {}: The content of this Box MUST be related to the content of the Box {}\n" " (and this last one should be correct too).\n".format( 1 + offset, 2 + offset))) grade -= 0.5 fb_inv_needed += BAD_INV_PRIO / 2 if not (last.is_unspecified() or (last == Limit(correct_size, MINUS))): C( D("Box {}: Wrong content! The last array index is expected here.\n" .format(5 + offset))) grade -= 0.5 fb_inv_needed += BAD_INV_PRIO / 2 C(D("\n")) return max(0.0, grade / TOTAL), fb_inv_needed test, fbn = test_one_tab(Astart, Aprev, Aindex, Alast, Asize, "N", "A", 1) fb_inv_needed += fbn grade -= (1 - test) * 2 test, fbn = test_one_tab(Bstart, Bprev, Bindex, Blast, Bsize, "M", "B", 6) fb_inv_needed += fbn grade -= (1 - test) * 2 test, fbn = test_one_tab(Cstart, Cprev, Cindex, Clast, Csize, "L", "C", 11) fb_inv_needed += fbn grade -= (1 - test) * 2 C(D("\nBoxs remainder:\n───────────────\n")) # Boxs 16 to 19 relevance test clean = True if epith != Limit(7, NO): C( D("Box 16: The instructions does not mention \"{}\" values.\n". format(fake_dict(DICO_1, epith.symbol)))) grade -= 0.25 fb_inv_needed += BAD_INV_PRIO / 4.0 clean = False if not ((src1 == Limit(1, NO) and src2 == Limit(3, NO)) or (src2 == Limit(1, NO) and src1 == Limit(3, NO))): C( D("Box 17 and 18: The parts already treated (according to the drawing in A and B) must be specified.\n" )) grade -= 0.5 fb_inv_needed += BAD_INV_PRIO / 2.0 clean = False if inter != Limit(5, NO): C(D("Box 19: Where is stored the result?\n")) grade - 0.25 fb_inv_needed += BAD_INV_PRIO / 4.0 clean = False if clean: C(D(self.get_positive() + "\n")) if fb_inv_needed: coms.append(rap.add_ffwd("AInvFail", fb_inv_needed)) # Test des variables dans le code C(D("\nCode analysis:\n──────────────")) if not self.report: coms.append( rap.disp_msg( "The code analysis has failed. This was previously mentioned.\n" )) grade -= 2 return (grade / TOTAL, coms) else: init_dict = self.report.init_values def check_init(name, val_init): init = self.vars[name] malus = 0.0 if init: if init not in init_dict: C( R( "AInvInit", 200, "\n-> The variable {} does not seem to be present in the code.\n" .format(init))) malus += 1 elif init_dict[init] != val_init: C( R( "AInvInit", 100, "\n-> The variable {} does not seem to be initialized according to the Loop Invariant.\n" .format(init))) malus += 0.5 else: C( D("\n=> The code and the Loop Invariant matches, as the initialisation of {} is concerned.\n" .format(init))) else: C( D("\nAs it stands, your Invariant cannot be confronted with the code for the array {}.\n" .format(name))) malus += 1 return malus # Variable parcourant A grade -= check_init("A", '0') # Variable parcourant B grade -= check_init("B", '0') # Variable parcourant C grade -= check_init("C", '0') return (grade / TOTAL, coms)