Multiple choice

Allow students to select from multiple options.

Simple example

A simple implementation of this example only requires writing a question.html file. Note that the question parameters, such as the ball mass mm, the angle θ\theta, the height hh, the initial velocity v0v_0 and the distance dd, have fixed values. Consequently, the correct answer is also fixed.

<pl-question-panel>
<p>
A cannon ball with mass $m = 1.8 \rm\ kg$ is fired downward from a cliff at a height
$h = 2.67 \rm\ m$, at an angle $\theta = 31^o$ with respect to the horizontal, and
an initial velocity $v_0 = 20 \rm\ m/s$, as illustrated in the figure below.
</p>
<p><pl-figure file-name="image.png" directory="clientFilesQuestion" width="300"></pl-figure></p>
<p>
Suppose the ball hits the ground a distance $d = 4 \rm\ m$ from the base of the cliff. How long is the ball in the air?
Assume the acceleration due to gravity is $g=9.8 \rm\ m/s^2$.
</p>
</pl-question-panel>
<pl-multiple-choice answers-name="t" none-of-the-above="true">
<pl-answer correct="false">$t = 0.2 \rm\ s$</pl-answer>
<pl-answer correct="false">$t = 0.388 \rm\ s$</pl-answer>
<pl-answer correct="true" >$t = 0.233 \rm\ s$</pl-answer>
<pl-answer correct="false">$t = 0.259 \rm\ s$</pl-answer>
<pl-answer correct="false">$t = 0.738 \rm\ s$</pl-answer>
</pl-multiple-choice>

This question uses the attribute none-of-the-above="true" in the pl-multiple-choice element. This attribute adds the option "None of the above" as an alternative among the other options defined by the pl-answer tags. The answer "None of the above" will be true (replacing the correct answer) with 50%50\% probability.

Unfortunately, this implementation only creates one unique version of the question, with the same set of parameters and answers. The only level of randomization comes from the order in which the answers are displayed and the choice of the "None of the above" option as correct answer.

Complex example

To add variability to the question, we can include dynamically-generated values in question.html using Mustache template syntax. In this example, we can randomly generate the parameters mm, hh, θ\theta, v0v_0, and dd and compute the corresponding correct answers and distractors.

The modified question.html file that supports the randomization is:

<pl-question-panel>
<p>
A cannon ball with mass $m ={{params.m}}\rm\ kg$ is fired downward from a cliff at a height
$h = {{params.h}}\rm\ m$, at an angle $\theta = {{params.theta}}^o$ with respect to the horizontal, and
an initial velocity $v_0 = {{params.v0}} \rm\ m/s$, as illustrated in the figure below.
</p>
<p><pl-figure file-name="image.png" directory="clientFilesQuestion" width="300"></pl-figure></p>
<p>
Suppose the ball hits the ground a distance $d = {{params.d}} \rm\ m$ from the base of the cliff.
How long is the ball in the air?
Assume the acceleration due to gravity is $g=9.8 \rm\ m/s^2$.
</p>
</pl-question-panel>
<pl-multiple-choice answers-name="t" none-of-the-above={{params.none}}>
<pl-answer correct="true" >$t = {{params.t_c}}\rm\ s$</pl-answer>
<pl-answer correct="false">$t = {{params.t_x1}}\rm\ s$</pl-answer>
<pl-answer correct="false">$t = {{params.t_x2}}\rm\ s$</pl-answer>
<pl-answer correct="false">$t = {{params.t_x3}}\rm\ s$</pl-answer>
<pl-answer correct="false">$t = {{params.t_x4}}\rm\ s$</pl-answer>
</pl-multiple-choice>

To generate the parameters, we can use the following Python code in server.py:

import random, math
def generate(data):
# gravity (m/s^2)
g = 9.8
# mass of the ball (kg)
m = random.choice([3, 1.4, 1.6, 1.8])
# horizontal distance (m)
d = random.randint(4,16)
# angle with horizontal (in degrees)
theta = random.randint(20,40)
# initial velocity (m/s)
v0 = random.randint(18,25)
# initial velocity components (m/s)
v0x = v0*math.cos(theta*math.pi/180)
v0y = v0*math.sin(theta*math.pi/180)
# time in the air (s)
t = round(d/v0x)
# height of the cliff (m)
h = round(v0y*t + 0.5*g*t**2,3)
# storing the parameters
data["params"]["m"] = m
data["params"]["h"] = h
data["params"]["d"] = d
data["params"]["v0"] = v0
data["params"]["theta"] = theta
# determines if the option "none of the above" will be used or not
data["params"]["none"] = random.choice(["false","true"])
# this is the correct answer
data["params"]["t_c"] = t
# these are the distractors
data["params"]["t_x1"] = round(math.sqrt(2*h/g), 3)
data["params"]["t_x2"] = round(d/v0, 3)
data["params"]["t_x3"] = round(d/v0y, 3)
data["params"]["t_x4"] = round(h/v0y, 3)

The above script randomizes and computes several aspects of the question:

  • The parameters mm, dd, θ\theta, and v0v_0 are generated using the Python module random
  • The parameter hh is computed based on mm, dd, θ\theta, and v0v_0
  • The choice of using the attribute none-of-the-above is selected at random
  • The correct answer and distractors are computed using the input parameters

These values are stored in the data["params"] dictionary, which are used in the question.html template. With the addition of server.py and templated values in question.html, we can now generate many unique variants of this question.

More randomization!

Feeling adventurous? We can add even more variation to this question:

  • We can dynamically create an image to reflect the randomly selected parameters.
  • In addition to asking about the length of time the ball is in the air, we can add a second problem statement that asks the student to compute the distance the ball travels. We can then also randomly pick between the original problem statement and this new problem statement.

The modified question.html file that supports this additional randomization is:

<pl-question-panel>
<p>
A cannon ball with mass $m ={{params.m}}\rm\ kg$ is fired downward from a cliff at a height
$h = {{params.h}}\rm\ m$, at an angle $\theta = {{params.theta}}^o$ with respect to the horizontal, and
an initial velocity $v_0 = {{params.v0}} \rm\ m/s$, as illustrated in the figure below.
</p>
<p>
<pl-drawing gradable="false" grid-size="0" width="300" height=300>
<pl-drawing-initial>
<pl-rectangle x1=20 y1=180 width="40" height="240" color="brown"></pl-rectangle>
<pl-circle x1=40 y1=50 radius="10" color="blue"></pl-circle>
<pl-vector x1=40 y1=50 angle={{params.theta}} width="80" label="v_0"></pl-vector>
<pl-arc-dimensions x1=40 y1=50 radius="40" start-angle="0" end-angle={{params.theta}} label="\\theta" offsetx="10" offsety="5" start-support-line="true"></pl-arc-dimensions>
<pl-dimensions x1="40" y1="300" x2="40" y2="50" dim-offset="200" end-support-line="true" label="h" ></pl-dimensions>
<pl-dimensions x1="40" y1="200" x2="100" y2="200" label="d" ></pl-dimensions>
</pl-drawing-initial>
</pl-drawing>
</p>
<p>
{{params.question_text}} Assume the acceleration due to gravity is $g=9.8 \rm\ m/s^2$.
</p>
</pl-question-panel>
<pl-multiple-choice answers-name="t" none-of-the-above={{params.none}}>
<pl-answer correct="true" >{{params.t_c}}</pl-answer>
<pl-answer correct="false">{{params.t_x1}}</pl-answer>
<pl-answer correct="false">{{params.t_x2}}</pl-answer>
<pl-answer correct="false">{{params.t_x3}}</pl-answer>
<pl-answer correct="false">{{params.t_x4}}</pl-answer>
</pl-multiple-choice>

By using the <pl-drawing> element instead of <pl-figure>, we can create a dynamic image where the orientation of the arrow is consistent with the provided angle θ\theta. The image could be easily adapted such that the height of the cliff would also vary with the value of the parameter hh.

Also note the use of {{params.question_text}} to display the randomly-picked problem statement.

As before, we'll use server.py to generate parameters for the question:

import random, math
def generate(data):
# gravity (m/s^2)
g = 9.8
# mass of the ball (kg)
m = random.choice([3, 1.4, 1.6, 1.8])
# angle with horizontal (in degrees)
theta = random.randint(20,40)
# initial velocity (m/s)
v0 = random.randint(18,25)
# initial velocity components (m/s)
v0x = v0*math.cos(theta*math.pi/180)
v0y = v0*math.sin(theta*math.pi/180)
# storing the parameters
data["params"]["m"] = m
data["params"]["v0"] = v0
data["params"]["theta"] = theta
# determines if the option "none of the above" will be used or not
data["params"]["none"] = "false" #random.choice(["false","true"])
if random.choice([0,1]): # This variant provides the distance and asks for the time
# horizontal distance (m)
d = random.randint(4,16)
# time in the air (s)
t = d/v0x
# height of the cliff (m)
h = round(v0y*t + 0.5*g*t**2,3)
data["params"]["h"] = h
# question statement
data["params"]["question_text"] = 'Suppose the ball hits the ground a distance $d = ' + str(d) + '\\rm\\ m$ from the base of the cliff. How long is the ball in the air?'
# this is the correct answer
data["params"]["t_c"] = '$t = ' + str(round(t,3)) + '\\rm\\ s$'
# these are the distractors
data["params"]["t_x1"] = '$t = ' + str(round(math.sqrt(2*h/g), 3)) + '\\rm\\ s$'
data["params"]["t_x2"] = '$t = ' + str(round(d/v0, 3)) + '\\rm\\ s$'
data["params"]["t_x3"] = '$t = ' + str(round(d/v0y, 3)) + '\\rm\\ s$'
data["params"]["t_x4"] = '$t = ' + str(round(h/v0y, 3)) + '\\rm\\ s$'
else: # This variant provides the time and asks for the distance
# time in the air (s)
t = round(random.uniform(0.5,0.8),2)
# horizontal distance (m)
d = v0x*t
# height of the cliff (m)
h = round(v0y*t + 0.5*g*t**2,3)
data["params"]["h"] = h
# question statement
data["params"]["question_text"] = 'Suppose the ball hits the ground after $t = ' + str(t) + '\\rm\\ s$. What is the distance from the base of the cliff that the ball hits the ground?'
# this is the correct answer
data["params"]["t_c"] = '$d = ' + str(round(d,3)) + '\\rm\\ m$'
# these are the distractors
data["params"]["t_x1"] = '$d = ' + str(round(v0*t,3)) + '\\rm\\ m$'
data["params"]["t_x2"] = '$d = ' + str(round(v0y*t,3)) + '\\rm\\ m$'
data["params"]["t_x3"] = '$d = ' + str(round(0.5*g*t**2,3)) + '\\rm\\ m$'
data["params"]["t_x4"] = '$d = ' + str(round(d + 0.5*g*t**2,3)) + '\\rm\\ m$'

Note the addition of code to generate a question and answers for the secondary problem statement.

Here's one instance of this fully randomized question: