diff --git a/dynamic/generators/comp2804/coefficient_of_term/__init__.py b/dynamic/generators/comp2804/coefficient_of_term/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dynamic/generators/comp2804/coefficient_of_term/main.py b/dynamic/generators/comp2804/coefficient_of_term/main.py new file mode 100644 index 00000000..6880e55d --- /dev/null +++ b/dynamic/generators/comp2804/coefficient_of_term/main.py @@ -0,0 +1,138 @@ +""" What is the coefficient of x^a * y^b for the expansion of (cx^s+dy^t)^n ? """ +""" This generator creates a randomized problem of figure out the coefficient +of a specific form x^{a}y^{b} for a given (cx^s+dy^t)^n """ + +import random + + +def generate_question(): + + s = random.randint(1, 5) + t = random.randint(1, 5) + + multiply_s = random.randint(5, 15) + multiply_t = random.randint(5, 15) + + a = s * multiply_s + b = t * multiply_t + + c = random.randint(2, 7) + d = random.randint(2, 7) + + n = multiply_s + multiply_t + + question_body = ( + "What is the coefficient of the term $x^{" + + str(a) + + "}y^{" + + str(b) + + "}$, in the expansion of " + + "$(" + + str(c) + + " \\cdot x^{" + + str(s) + + "} + " + + str(d) + + " \\cdot y^{" + + str(t) + + "})" + + "^{" + + str(n) + + "}$" + ) + + answer = ( + "$\\binom{" + + str(n) + + "}{" + + str(multiply_s) + + "} \\cdot " + + str(c) + + "^{" + + str(multiply_s) + + "} \\cdot " + + str(d) + + "^{" + + str(multiply_t) + + "}$" + ) + varied_answer_1 = ( + "$\\binom{" + + str(n) + + "}{" + + str(multiply_s) + + "} \\cdot " + + str(c) + + "^{" + + str(a) + + "} \\cdot " + + str(d) + + "^{" + + str(b) + + "}$" + ) + varied_answer_2 = ( + "$\\binom{" + + str(n) + + "}{" + + str(s) + + "} \\cdot " + + str(c) + + "^{" + + str(s) + + "} \\cdot " + + str(d) + + "^{" + + str(t) + + "}$" + ) + varied_answer_3 = ( + "$\\binom{" + + str(n) + + "}{" + + str(s) + + "} \\cdot " + + str(c) + + "^{" + + str(multiply_s) + + "} \\cdot " + + str(d) + + "^{" + + str(multiply_t) + + "}$" + ) + + # we will not randomize the order of these choices because that is handled in ruby side. + # random.shuffle(answerchoices) + return { + "title": "coefficient_of_term", + "body": question_body, + "bodyFormat": "mathjax", + "pseudocode": "", + "multipleChoiceAnswers": [ + { + "body": answer, + "bodyFormat": "mathjax", + "correct": "true", + }, + { + "body": varied_answer_1, + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": varied_answer_2, + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": varied_answer_3, + "bodyFormat": "mathjax", + "correct": "false", + }, + ], + } + + +def call(): + return generate_question() diff --git a/dynamic/generators/comp2804/coefficient_of_term/test_main.py b/dynamic/generators/comp2804/coefficient_of_term/test_main.py new file mode 100644 index 00000000..1801cadc --- /dev/null +++ b/dynamic/generators/comp2804/coefficient_of_term/test_main.py @@ -0,0 +1,11 @@ +from .test_out import test_output + +# import test_out +import sys + +# import main as main +from .main import call + + +def test(): + assert test_output(call()) diff --git a/dynamic/generators/comp2804/coefficient_of_term/test_out.py b/dynamic/generators/comp2804/coefficient_of_term/test_out.py new file mode 100644 index 00000000..9f1cd408 --- /dev/null +++ b/dynamic/generators/comp2804/coefficient_of_term/test_out.py @@ -0,0 +1,110 @@ +""" This file contains tools for test_main.py to use. """ + + +def check_mathjax_compatible(output_string): + # for now, checks that the answers start and end with the "$" to be conventionally compatible with mathjax + # could change the conditions to accommodate more types of mathjax compatible examples ... + return output_string[0] == "$" and output_string[len(output_string) - 1] == "$" + + +# returns True if the output dictionary is a valid output for the "ruby side" interpretation +def test_output(cache): + + bodyFormat_options = ["text", "mathjax"] # can add more options in the future + + if type(cache) != type({}): + return False + else: + + answer_type = { + "title": "", + "body": "", + "bodyFormat": "mathjax", # or "text" + "pseudocode": "", + "multipleChoiceAnswers": [ + { + "body": "", + "bodyFormat": "mathjax", + "correct": "true", + }, + { + "body": "", + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": "", + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": "", + "bodyFormat": "mathjax", + "correct": "false", + }, + ], + } + + keys_list = list( + answer_type.keys() + ) # the list of keys from the template answer_type dictionary + out_list = list( + cache.keys() + ) # the list of keys from the output dictionary that we are testing + + # checks if the output dictionary that is being tested has the correct type of keys as in the answer_type + for i in range(len(out_list)): + if not out_list[i] in keys_list: + return False + + # if there is "title" key in the ouput dictionary, check that the item of "title" is string type + if "title" in out_list: + if not type(cache["title"]) == type(""): + return False + + # check that the item of the key, "body", is string type + if not type(cache["body"]) == type(""): + return False + + # checks "bodyFormat" key has the correct item types available as in the bodyFormat_options + if not (cache["bodyFormat"] in bodyFormat_options): + return False + + # if there is "pseudocode" key in the output dictionary, check that the item of "pseudocode" is a string type + if "pseudocode" in out_list: + if not type(cache["pseudocode"]) == type(""): + return False + + # tests the item of "multipleChoiceAnswers" key + mult_choice_answers = cache[keys_list[4]] + if len(mult_choice_answers) != 4: + return False + else: + + for i in range(0, 4): + + if type(mult_choice_answers[i]) != type({}): + return False + elif len(mult_choice_answers[i]) != 3: + return False + + for j in range(len(list(mult_choice_answers[i].keys()))): + if not ( + list(mult_choice_answers[i].keys())[j] + in ["body", "bodyFormat", "correct"] + ): + return False + + if not check_mathjax_compatible(mult_choice_answers[i]["body"]): + return False + + if not (mult_choice_answers[i]["bodyFormat"] in bodyFormat_options): + return False + + if not ( + mult_choice_answers[i]["correct"] == "true" + or mult_choice_answers[i]["correct"] == "false" + ): + return False + + return True diff --git a/dynamic/generators/comp2804/probability_of_choice/__init__.py b/dynamic/generators/comp2804/probability_of_choice/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dynamic/generators/comp2804/probability_of_choice/main.py b/dynamic/generators/comp2804/probability_of_choice/main.py new file mode 100644 index 00000000..9f2d4892 --- /dev/null +++ b/dynamic/generators/comp2804/probability_of_choice/main.py @@ -0,0 +1,253 @@ +""" Given r number of red balls (or other randomly chosen noun), g number of green balls, and b number of blue balls, """ +""" and a uniformly random subset of n balls are chosen, """ +""" what is the probability that event E happens? (with description of E.)""" + +import random + +some_nouns = ["balls", "hats", "cards", "guitar picks", "books", "magic beans", "coins"] + + +class RGBEvent: + def __init__(self, r, g, b) -> None: + self.chosen_r = random.randint(1, r) + self.chosen_g = random.randint(2, g - 1) + self.chosen_b = random.randint(3, b - 1) + self.total = r + g + b + self.n = self.chosen_r + self.chosen_g + self.chosen_b + + +def generate_question(): + noun_choice = some_nouns[random.randint(0, len(some_nouns)-1)] + r = random.randint(5, 15) + g = random.randint(5, 15) + b = random.randint(5, 15) + + problem_difficulty = random.randint( + 1, 3 + ) # randomly choose from three difficulty for the generated problem + + rgb_choice_event = RGBEvent(r, g, b) + question_body = ( + "There are r number of red " + + noun_choice + + ", g number of green " + + noun_choice + + ", and b number of blue " + + noun_choice + + "; with $r = " + + str(r) + + ", g = " + + str(g) + + ", b = " + + str(b) + + "$. " + + " Let us choose " + + str(rgb_choice_event.n) + + "number of " + + noun_choice + + " from this collection. " + + "Evaluate $Pr(E)$ = ?, with event $E =$ " + + "(Exactly " + + str(r) + + " number of red " + + noun_choice + + "; " + + str(g) + + " number of green " + + noun_choice + + "; " + + str(b) + + " number of blue " + + noun_choice + + " have been chosen.)" + ) + + if problem_difficulty == 1: # (lowest difficulty; easiest to guess) + answer = ( + "$\\frac{" + + "\\binom{" + + str(r) + + "}{" + + str(rgb_choice_event.chosen_r) + + "} \\cdot " + + "\\binom{" + + str(g) + + "}{" + + str(rgb_choice_event.chosen_g) + + "} \\cdot " + + "\\binom{" + + str(b) + + "}{" + + str(rgb_choice_event.chosen_b) + + "}" + + "}{" + + "\\binom{" + + str(rgb_choice_event.total) + + "}{" + + str(rgb_choice_event.n) + + "}" + + "}$" + ) + elif ( + problem_difficulty == 2 + ): # (medium difficulty; might need a little extra thinking) + answer = ( + "$\\frac{" + + "\\binom{" + + str(r) + + "}{" + + str(rgb_choice_event.chosen_r) + + "} \\cdot " + + "\\binom{" + + str(g) + + "}{" + + str(rgb_choice_event.chosen_g) + + "} \\cdot " + + "\\binom{" + + str(b) + + "}{" + + str(b - rgb_choice_event.chosen_b) + + "}" + + "}{" + + "\\binom{" + + str(rgb_choice_event.total) + + "}{" + + str(rgb_choice_event.n) + + "}" + + "}$" + ) + else: # problem_difficulty == 3: (most difficult; might need to be careful when choosing) + answer = ( + "$\\frac{" + + "\\binom{" + + str(r) + + "}{" + + str(r - rgb_choice_event.chosen_r) + + "} \\cdot " + + "\\binom{" + + str(g) + + "}{" + + str(g - rgb_choice_event.chosen_g) + + "} \\cdot " + + "\\binom{" + + str(b) + + "}{" + + str(b - rgb_choice_event.chosen_b) + + "}" + + "}{" + + "\\binom{" + + str(rgb_choice_event.total) + + "}{" + + str(rgb_choice_event.total - rgb_choice_event.n) + + "}" + + "}$" + ) + + varied_answer_1 = ( + "$\\frac{" + + "\\binom{" + + str(r) + + "}{" + + str(rgb_choice_event.chosen_r) + + "} \\cdot " + + "\\binom{" + + str(g) + + "}{" + + str(rgb_choice_event.chosen_g) + + "} \\cdot " + + "\\binom{" + + str(b) + + "}{" + + str(b + 1 - rgb_choice_event.chosen_b) + + "}" + + "}{" + + "\\binom{" + + str(rgb_choice_event.total) + + "}{" + + str(rgb_choice_event.n) + + "}" + + "}$" + ) + varied_answer_2 = ( + "$\\frac{" + + "\\binom{" + + str(r) + + "}{" + + str(rgb_choice_event.chosen_r) + + "} + " + + "\\binom{" + + str(g) + + "}{" + + str(rgb_choice_event.chosen_g) + + "} + " + + "\\binom{" + + str(b) + + "}{" + + str(rgb_choice_event.chosen_b) + + "}" + + "}{" + + "\\binom{" + + str(rgb_choice_event.total) + + "}{" + + str(rgb_choice_event.total + 1 - rgb_choice_event.n) + + "}" + + "}$" + ) + varied_answer_3 = ( + "$\\frac{" + + "\\binom{" + + str(r) + + "}{" + + str(r - rgb_choice_event.chosen_r) + + "} + " + + "\\binom{" + + str(g) + + "}{" + + str(g - rgb_choice_event.chosen_g) + + "} + " + + "\\cdot " + + str(b) + + "}" + + "}{" + + "\\binom{" + + str(rgb_choice_event.total) + + "}{" + + str(rgb_choice_event.n) + + "}" + + "}$" + ) + + # we will not randomize the order of these choices because that is handled in ruby side. + # random.shuffle(answerchoices) + return { + "title": "probability_of_choice", + "body": question_body, + "bodyFormat": "mathjax", + "pseudocode": "", + "multipleChoiceAnswers": [ + { + "body": answer, + "bodyFormat": "mathjax", + "correct": "true", + }, + { + "body": varied_answer_1, + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": varied_answer_2, + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": varied_answer_3, + "bodyFormat": "mathjax", + "correct": "false", + }, + ], + } + + +def call(): + return generate_question() diff --git a/dynamic/generators/comp2804/probability_of_choice/test_main.py b/dynamic/generators/comp2804/probability_of_choice/test_main.py new file mode 100644 index 00000000..1801cadc --- /dev/null +++ b/dynamic/generators/comp2804/probability_of_choice/test_main.py @@ -0,0 +1,11 @@ +from .test_out import test_output + +# import test_out +import sys + +# import main as main +from .main import call + + +def test(): + assert test_output(call()) diff --git a/dynamic/generators/comp2804/probability_of_choice/test_out.py b/dynamic/generators/comp2804/probability_of_choice/test_out.py new file mode 100644 index 00000000..9f1cd408 --- /dev/null +++ b/dynamic/generators/comp2804/probability_of_choice/test_out.py @@ -0,0 +1,110 @@ +""" This file contains tools for test_main.py to use. """ + + +def check_mathjax_compatible(output_string): + # for now, checks that the answers start and end with the "$" to be conventionally compatible with mathjax + # could change the conditions to accommodate more types of mathjax compatible examples ... + return output_string[0] == "$" and output_string[len(output_string) - 1] == "$" + + +# returns True if the output dictionary is a valid output for the "ruby side" interpretation +def test_output(cache): + + bodyFormat_options = ["text", "mathjax"] # can add more options in the future + + if type(cache) != type({}): + return False + else: + + answer_type = { + "title": "", + "body": "", + "bodyFormat": "mathjax", # or "text" + "pseudocode": "", + "multipleChoiceAnswers": [ + { + "body": "", + "bodyFormat": "mathjax", + "correct": "true", + }, + { + "body": "", + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": "", + "bodyFormat": "mathjax", + "correct": "false", + }, + { + "body": "", + "bodyFormat": "mathjax", + "correct": "false", + }, + ], + } + + keys_list = list( + answer_type.keys() + ) # the list of keys from the template answer_type dictionary + out_list = list( + cache.keys() + ) # the list of keys from the output dictionary that we are testing + + # checks if the output dictionary that is being tested has the correct type of keys as in the answer_type + for i in range(len(out_list)): + if not out_list[i] in keys_list: + return False + + # if there is "title" key in the ouput dictionary, check that the item of "title" is string type + if "title" in out_list: + if not type(cache["title"]) == type(""): + return False + + # check that the item of the key, "body", is string type + if not type(cache["body"]) == type(""): + return False + + # checks "bodyFormat" key has the correct item types available as in the bodyFormat_options + if not (cache["bodyFormat"] in bodyFormat_options): + return False + + # if there is "pseudocode" key in the output dictionary, check that the item of "pseudocode" is a string type + if "pseudocode" in out_list: + if not type(cache["pseudocode"]) == type(""): + return False + + # tests the item of "multipleChoiceAnswers" key + mult_choice_answers = cache[keys_list[4]] + if len(mult_choice_answers) != 4: + return False + else: + + for i in range(0, 4): + + if type(mult_choice_answers[i]) != type({}): + return False + elif len(mult_choice_answers[i]) != 3: + return False + + for j in range(len(list(mult_choice_answers[i].keys()))): + if not ( + list(mult_choice_answers[i].keys())[j] + in ["body", "bodyFormat", "correct"] + ): + return False + + if not check_mathjax_compatible(mult_choice_answers[i]["body"]): + return False + + if not (mult_choice_answers[i]["bodyFormat"] in bodyFormat_options): + return False + + if not ( + mult_choice_answers[i]["correct"] == "true" + or mult_choice_answers[i]["correct"] == "false" + ): + return False + + return True diff --git a/dynamic/router.py b/dynamic/router.py index 4a71495f..d3b28893 100644 --- a/dynamic/router.py +++ b/dynamic/router.py @@ -13,6 +13,9 @@ import generators.comp2804.binomial_expansion_coefficient.main as binomial_expansion_coefficient_generator +import generators.comp2804.coefficient_of_term.main as coefficient_of_term_generator +import generators.comp2804.probability_of_choice.main as probability_of_choice_generator + router = APIRouter( prefix="/api", tags=["generate"], responses={404: {"description": "Not found"}} ) @@ -31,6 +34,8 @@ "set-theory-question": "/comp2804/set-theory", "num-of-functions": "/comp2804/num-of-functions", "let-m-and-n-question": "/comp2804/let-m-and-n", + "coefficient_of_term": "/comp2804/coefficient_of_term", + "probability_of_choice": "/comp2804/probability_of_choice", }, } @@ -90,6 +95,22 @@ async def generate_m_and_n_question(): async def generate_m_and_n_question(): return binomial_expansion_coefficient_generator.call() + @router.get(routes["comp2804"]["bitstrings-of-length"]) async def generate_bitstrings_of_length_question(): return bitstrings_of_length_generator.call() + + +@router.get(routes["comp2804"]["let-m-and-n-question"]) +async def generate_m_and_n_question(): + return m_and_n_generator.call() + + +@router.get(routes["comp2804"]["coefficient_of_term"]) +async def coefficient_of_term_question(): + return coefficient_of_term_generator.call() + + +@router.get(routes["comp2804"]["probability_of_choice"]) +async def probability_of_choice_question(): + return probability_of_choice_generator.call()