// Numbas version: finer_feedback_settings {"name": "Truth table", "extensions": ["jsxgraph", "sheets"], "custom_part_types": [{"source": {"pk": 242, "author": {"name": "Christian Lawson-Perfect", "pk": 7}, "edit_page": "/part_type/242/edit"}, "name": "Spreadsheet", "short_name": "spreadsheet", "description": "
An editable spreadsheet. Ranges of cells can be disabled, and you can specify ranges of cells to be marked. A cell is marked correct if its value is equal to the value in the expected answer spreadsheet.
", "help_url": "", "input_widget": "spread-sheet", "input_options": {"correctAnswer": "settings[\"correct_answer\"]", "hint": {"static": true, "value": ""}, "initial_sheet": {"static": false, "value": "disable_cells(settings[\"initial_sheet\"], settings[\"disable_ranges\"])"}}, "can_be_gap": true, "can_be_step": true, "marking_script": "correctAnswer:\nsettings[\"correct_answer\"]\n\nmark:\nif(sum(mark_ranges)=0,\n incorrect(),\n apply(mark_ranges)\n)\n\ninterpreted_answer:\nstudentAnswer\n\nrange_cells:\nmap(parse_range(ref),ref,values(settings[\"mark_ranges\"]))\n\ntotal_cells:\nlen(flatten(range_cells))\n\nrange_weights:\nswitch(\n settings[\"marking_method\"]=\"per_cell\",\n map(len(r)/total_cells, r, range_cells),\n // otherwise, mark per range\n repeat(1/len(range_cells), len(range_cells))\n)\n\nmark_ranges:\nmap(\n let(\n range_credit,\n sum(map(\n let(\n correctCellString, correctAnswer[c],\n correctCellNumber, parsenumber(correctCellString, notation_styles),\n studentCellString, studentAnswer[c],\n studentCellNumber, parsenumber(studentCellString, notation_styles),\n award(\n 1/len(cells), \n if(isnan(correctCellNumber) and correctCellString<>\"\",\n lower(correctCellString) = lower(studentCellString),\n abs(studentCellNumber - if(isnan(correctCellNumber),0,correctCellNumber)) <= settings[\"tolerance\"]\n )\n )\n ),\n c,\n cells\n )),\n message,\n switch(\n range_credit=0,\n if(len(cells)=1, \"This entry is incorrect.\", \"All entries in this range are incorrect.\"),\n range_credit=1,\n if(len(cells)=1, \"This entry is correct.\", \"All entries in this range are correct.\"),\n //otherwise\n \"Some entries in this range are correct.\"\n ),\n assert(len(cells)=0, add_credit(range_credit*w, \"{name}: \"+message)); \n range_credit\n ),\n [cells,w,name],\n zip(range_cells, range_weights, keys(settings[\"mark_ranges\"]))\n)\n\nnotation_styles:\n[\"plain\",\"si-en\"]", "marking_notes": [{"name": "correctAnswer", "description": "A spreadsheet representing the expected answer.
", "definition": "settings[\"correct_answer\"]"}, {"name": "mark", "description": "This is the main marking note. It should award credit and provide feedback based on the student's answer.", "definition": "if(sum(mark_ranges)=0,\n incorrect(),\n apply(mark_ranges)\n)"}, {"name": "interpreted_answer", "description": "A value representing the student's answer to this part.", "definition": "studentAnswer"}, {"name": "range_cells", "description": "For each range to be marked, the addresses of the cells in that range.
", "definition": "map(parse_range(ref),ref,values(settings[\"mark_ranges\"]))"}, {"name": "total_cells", "description": "The total number of cells to be marked. Cells in overlapping ranges will be counted once for each range they're in.
", "definition": "len(flatten(range_cells))"}, {"name": "range_weights", "description": "The weight of each range, as a proportion of the available credit.
", "definition": "switch(\n settings[\"marking_method\"]=\"per_cell\",\n map(len(r)/total_cells, r, range_cells),\n // otherwise, mark per range\n repeat(1/len(range_cells), len(range_cells))\n)"}, {"name": "mark_ranges", "description": "Mark each of the ranges specified by the question author.
", "definition": "map(\n let(\n range_credit,\n sum(map(\n let(\n correctCellString, correctAnswer[c],\n correctCellNumber, parsenumber(correctCellString, notation_styles),\n studentCellString, studentAnswer[c],\n studentCellNumber, parsenumber(studentCellString, notation_styles),\n award(\n 1/len(cells), \n if(isnan(correctCellNumber) and correctCellString<>\"\",\n lower(correctCellString) = lower(studentCellString),\n abs(studentCellNumber - if(isnan(correctCellNumber),0,correctCellNumber)) <= settings[\"tolerance\"]\n )\n )\n ),\n c,\n cells\n )),\n message,\n switch(\n range_credit=0,\n if(len(cells)=1, \"This entry is incorrect.\", \"All entries in this range are incorrect.\"),\n range_credit=1,\n if(len(cells)=1, \"This entry is correct.\", \"All entries in this range are correct.\"),\n //otherwise\n \"Some entries in this range are correct.\"\n ),\n assert(len(cells)=0, add_credit(range_credit*w, \"{name}: \"+message)); \n range_credit\n ),\n [cells,w,name],\n zip(range_cells, range_weights, keys(settings[\"mark_ranges\"]))\n)"}, {"name": "notation_styles", "description": "Accepted number notation styles for a value in an individual cell.
", "definition": "[\"plain\",\"si-en\"]"}], "settings": [{"name": "initial_sheet", "label": "Initial sheet", "help_url": "", "hint": "Aspreadsheet
object giving the initial state of the sheet that the student should fill in.", "input_type": "code", "default_value": "", "evaluate": true}, {"name": "correct_answer", "label": "Correct answer", "help_url": "", "hint": "A spreadsheet
object representing a correct answer to the part.", "input_type": "code", "default_value": "", "evaluate": true}, {"name": "disable_ranges", "label": "Ranges to disable", "help_url": "", "hint": "A list of cell or range references, denoting the cells that should not be editable.", "input_type": "code", "default_value": "[]", "evaluate": true}, {"name": "mark_ranges", "label": "Ranges to mark", "help_url": "", "hint": "A dictionary of cell or range references, mapping names to ranges of cells, denoting the cells that should be compared for equality with the expected answer.", "input_type": "code", "default_value": "dict()", "evaluate": true}, {"name": "marking_method", "label": "Marking method", "help_url": "", "hint": "", "input_type": "dropdown", "default_value": "per_cell", "choices": [{"value": "per_cell", "label": "Each cell has the same weight"}, {"value": "per_range", "label": "Each range has the same weight"}]}, {"name": "tolerance", "label": "Allowed margin of error", "help_url": "", "hint": "", "input_type": "code", "default_value": "0", "evaluate": true}], "public_availability": "always", "published": true, "extensions": ["sheets"]}], "resources": [], "navigation": {"allowregen": true, "showfrontpage": false, "preventleave": false, "typeendtoleave": false}, "question_groups": [{"pickingStrategy": "all-ordered", "questions": [{"name": "Truth table", "tags": [], "metadata": {"description": "Fill in a truth table: the values for \"A\" and \"B\" are filled in, and the student has to work out \"A or B\" and then \"(A or B) implies B\".
", "licence": "Creative Commons Attribution 4.0 International"}, "statement": "", "advice": "$A \\cup B$ is true if $A$ is true or $B$ is true, or both.
\nSo the corresponding column in the truth table is as follows:
\n{filled_table_or}
\nThe third column combines $A \\cup B$ and $B$. Use the truth table for logical implication, $\\implies$:
\n{implication_truth_table}
\nThe column $A \\cup B$ in our table corresponds to $A$ in the table for implication, and the column $B$ in our table corresponds to $B$ in the table for implication.
\nSo the completed truth table is as follows:
\n{filled_table}
", "rulesets": {}, "extensions": ["jsxgraph", "sheets"], "builtin_constants": {"e": true, "pi,\u03c0": true, "i": true}, "constants": [], "variables": {"truth_table": {"name": "truth_table", "group": "Ungrouped variables", "definition": "spreadsheet([[\"A\", \"B\", \"A \u222a B\", \"(A \u222a B) \u27f9 B\"]])\n |> fill_range(\"A2:B5\", product([\"T\",\"F\"],2))\n |> update_range(\"A1:D1\", font_style(\"bold\") |> border(\"bottom\", \"thin\"))", "description": "", "templateType": "anything", "can_override": false}, "values": {"name": "values", "group": "Ungrouped variables", "definition": "product([true,false],2)", "description": "The possible combinations of values of A and B.
", "templateType": "anything", "can_override": false}, "a_or_b": {"name": "a_or_b", "group": "Ungrouped variables", "definition": "map(a or b, [a,b], values)", "description": "", "templateType": "anything", "can_override": false}, "a_or_b_implies_b": {"name": "a_or_b_implies_b", "group": "Ungrouped variables", "definition": "map((a or b) implies b, [a,b], values)", "description": "", "templateType": "anything", "can_override": false}, "filled_table": {"name": "filled_table", "group": "Ungrouped variables", "definition": "truth_table\n |> fill_range(\"C2:D5\", map(map(if(x,\"T\",\"F\"),x,row),row,transpose([a_or_b, a_or_b_implies_b])))", "description": "", "templateType": "anything", "can_override": false}, "q": {"name": "q", "group": "Ungrouped variables", "definition": "map(a implies b, [a,b], values)", "description": "", "templateType": "anything", "can_override": false}, "implication_truth_table": {"name": "implication_truth_table", "group": "Ungrouped variables", "definition": "spreadsheet([[\"A\", \"B\", \"A \u27f9 B\"]])\n |> fill_range(\"A2:B5\", product([\"T\",\"F\"],2))\n |> update_range(\"A1:C1\", font_style(\"bold\") |> border(\"bottom\", \"thin\"))\n |> fill_range(\"C2:C5\", map(if(a implies b,\"T\",\"F\"), [a,b], product([true,false],2)))", "description": "The truth table for the logical implication operator.
", "templateType": "anything", "can_override": false}, "filled_table_or": {"name": "filled_table_or", "group": "Ungrouped variables", "definition": "truth_table\n |> fill_range(\"C2:C5\", map(if(x,\"T\",\"F\"),x,a_or_b))\n |> update_range(\"C2:C5\", bg_color(\"lightblue\"))", "description": "The truth table with the $A \\cup B$ column filled in.
", "templateType": "anything", "can_override": false}}, "variablesTest": {"condition": "", "maxRuns": 100}, "ungrouped_variables": ["truth_table", "values", "a_or_b", "a_or_b_implies_b", "filled_table", "q", "implication_truth_table", "filled_table_or"], "variable_groups": [], "functions": {}, "preamble": {"js": "", "css": ""}, "parts": [{"type": "spreadsheet", "useCustomName": false, "customName": "", "marks": "8", "scripts": {}, "customMarkingAlgorithm": "", "extendBaseMarkingAlgorithm": true, "unitTests": [], "showCorrectAnswer": true, "showFeedbackIcon": true, "variableReplacements": [], "variableReplacementStrategy": "originalfirst", "nextParts": [], "suggestGoingBack": false, "adaptiveMarkingPenalty": 0, "exploreObjective": null, "prompt": "Fill in this truth table.
", "settings": {"initial_sheet": "truth_table", "correct_answer": "filled_table", "disable_ranges": "[\"A1:D1\", \"A1:B5\"]", "mark_ranges": "[\"A \u222a B\": \"C2:C5\", \"(A \u222a B) \u27f9 B\": \"D2:D5\"]", "marking_method": "per_cell", "tolerance": "0"}}], "partsMode": "all", "maxMarks": 0, "objectives": [], "penalties": [], "objectiveVisibility": "always", "penaltyVisibility": "always", "type": "question", "contributors": [{"name": "Christian Lawson-Perfect", "profile_url": "http://clppc:8000/accounts/profile/1/"}, {"name": "Christian Lawson-Perfect", "profile_url": "https://numbas.mathcentre.ac.uk/accounts/profile/7/"}]}]}], "contributors": [{"name": "Christian Lawson-Perfect", "profile_url": "http://clppc:8000/accounts/profile/1/"}, {"name": "Christian Lawson-Perfect", "profile_url": "https://numbas.mathcentre.ac.uk/accounts/profile/7/"}]}