diff --git a/doc/en/Topics/Matching.md b/doc/en/Topics/Matching.md index 066adb1f9b33332565d3d8513fbbcf4b722084d6..dee8e85b9ee602f27f46bba9479010c50a53aaeb 100644 --- a/doc/en/Topics/Matching.md +++ b/doc/en/Topics/Matching.md @@ -2,18 +2,31 @@ The drag-and-drop functionality of the [Parson's](Parsons.md) block in STACK can also be used to write matching and other grid-based problems, which can be answered by drag and drop. -This page provides a quick-start guide to authoring matching problems with the `parsons` block. As of STACK v4.6.0, the `parsons` block has three main configurations (each of which support further customisation) which can be achieved by setting appropriate block header parameters `columns` and `rows` as appropriate: +This page provides a quick-start guide to authoring matching problems with the `parsons` block. +As of STACK v4.6.0, the `parsons` block has three main configurations (each of which support further customisation) which can be achieved by setting appropriate block header parameters `columns` and `rows` as appropriate: 1. **Proof** (Example usage: `[[parsons]] ... [[/parsons]]`) : This was introduced in STACK v4.5.0, and a full guide can be found [here](Parsons.md). -2. **Grouping** (Example usage: `[[parsons columns="3"]] ... [[/parsons]]`) : This will set up a number of columns, each of which behave similarly to the single left-hand column of the **Proof** configuration, where the student may drag and drop items starting at the top of each column. This is useful when we are only interesting in grouping items together, and specific row positions do not matter, or when each column may have variable length. -3. **Grid** (Example usage: `[[parsons columns="3" rows="2"]] ... [[/parsons]]`) : This will set up a grid of shape `(columns, rows)`, where the student may drag and drop items to any position in the grid. +2. **Grouping** (Example usage: `[[parsons columns="3"]] ... [[/parsons]]`) : +This will set up a number of columns, each of which behave similarly to the single left-hand column of the **Proof** configuration, where the student may drag and drop items starting at the top of each column. +This is useful when we are only interesting in grouping items together, and specific row positions do not matter, or when each column may have variable length. +3. **Grid** (Example usage: `[[parsons columns="3" rows="2"]] ... [[/parsons]]`) : +This will set up a grid of shape `(columns, rows)`, where the student may drag and drop items to any position in the grid. + +Note that many **Grid** style questions can also be written using the **Grouping** setup. +The main difference between them is that **Grid** allows the student to drag any item to any position in the grid, regardless +of whether the item above it has been filled; **Grouping** on the other hand only allows students to drag items to the +end of the list within a column. ## Switching orientation -Parsons blocks will display columns vertically by default. The student has the option to flip the orientation so that the columns become horizontal rows, and back again, through a button on the question page. If you wish the columns to be horizontal rows as the question display default, then simply add `transpose="true"` to the block header (e.g., `[[parsons columns="3" transpose="true"]] ... [[/parsons]]`, will load a question with 3 horizontal rows). +Parsons blocks will display columns vertically by default. +The student has the option to flip the orientation so that the columns become horizontal rows, and back again, through a button on the question page. +If you wish the columns to be horizontal rows as the question display default, then simply add `transpose="true"` to the block header (e.g., `[[parsons columns="3" transpose="true"]] ... [[/parsons]]`, will load a question with 3 horizontal rows). ## Providing a model answer -_The format of the model answer is fixed and independent of the orientation discussed in the previous section_. It should be defined in `Question variables` as a two-dimensional array that is always column-grouped. For example if a model answer looks like +_The format of the model answer is fixed and independent of the orientation discussed in the previous section_. +It should be defined in _Question variables_ as a two-dimensional array that is always column-grouped. +For example if a model answer looks like ``` a | b | c ---|---|--- @@ -29,21 +42,54 @@ where, e.g., `"A"` is the key identifier for string `"a"`. ## Cloning items -The `clone` header parameter has been available in `parsons` since v4.5.0 of STACK. This can be set to `"true"` to allow items to be used more than once (e.g., `[[parsons columns="3" clone="true"]]...[[/parsons]]`). +The `clone` header parameter has been available in `parsons` since v4.5.0 of STACK. +This can be set to `"true"` to allow items to be used more than once (e.g., `[[parsons columns="3" clone="true"]]...[[/parsons]]`). ## Headers and index -Column headers default to `0`, `1`, ..., `columns` when using **Grouping** or **Grid** configurations of the Parson's block, so we recommend to always set custom headers for these cases. See the examples below for how to do this. +Column headers default to `0`, `1`, ..., `columns` when using **Grouping** or **Grid** configurations of the Parson's block, so we recommend to always set custom headers for these cases. +See the examples below for how to do this. -There is also the option of setting the index, which is possible in both **Grouping** and **Grid** configurations, but is more likely to be useful for the latter. This will appear as a specially styled left-most column and fixes labels for the rows. The index does not count as a column, so you should _decrease columns by one in the header parameters_. The item that appears top-left in both the index and header should be included in the _index only_. See the examples below for more details. +There is also the option of setting the index, which is possible in both **Grouping** and **Grid** configurations, but is more likely to be useful for the latter. +This will appear as a specially styled left-most column and fixes labels for the rows. +The index does not count as a column, so you should _decrease columns by one in the header parameters_. +The item that appears top-left in both the index and header should be included in the _index only_. +See the examples below for more details. ## Item style -In **Grouping** and **Grid** configurations, the height and width of individual items can be changed via the `item-height` and `item-width` header parameters. Parameter values should be a string containing a number, and this will set the pixels of the height/width. The default is `50px` for height, and the width is automatically deduced from the page layout and number of columns for the vertical orientation, or it is `100px` for the horizontal configuration. This may be needed if you have images or other large overflowing items (including header titles). +In **Grouping** and **Grid** configurations, the height and width of individual items can be changed via the `item-height` and `item-width` header parameters. Parameter values should be a string containing a number, and this will set the pixels of the height/width. +The default is `50px` (`item-height="50"`) for height, and the width is automatically deduced from the page layout and number of columns for the vertical orientation, or it is `100px` (`item-width="100"`) for the horizontal configuration. +This may be needed if you have images or other large overflowing items (including header titles). ## The matching library -The `matchlib.mac` Maxima library contains a small number of functions that are required for basic functionality of assessing matching problems in STACK. Essentially they translate the author answers to and back from JSON format expected by the drag-and-drop engine. Be sure to include this and make use of it as detailed in the examples below. +The `matchlib.mac` Maxima library contains a small number of functions that are required for basic functionality of assessing matching problems in STACK. +Essentially they translate the author answers to and back from JSON format expected by the drag-and-drop engine. +Be sure to include this and make use of it as detailed in the examples below. + +We include some basic helper functions that can allow the author to specify whether they care or not about the order within and between rows or columns as follows. +- `ans: [["A", "B"], ["C", "D"], ["E", "F"]]` +- `match_column_set` : I don't care about the order within a column -> +``` +match_column_set(ans) = [{"A", "B"}, {"C", "D"}, {"E", "F"}] +``` +- `match_row_set` : I don't care about the order within a row (unlikely to be required but included for completeness) -> +``` +match_row_set(ans) = [{"A", "C", "E"}, {"B", "D", "F"}] +``` +- `match_set_column` : I don't care about the order of the columns (unlikely to be required but included for completeness) -> +``` +match_set_column(ans) = {["A", "B"], ["C", "D"], ["E", "F"]} +``` +- `match_set_row` : I don't care about the order of the rows -> +``` +match_set_rows(ans) = {["A", "C", "E"], ["B", "D", "F"]} +``` +- `match_transpose` : I would like to turn my answer into a row-grouped array -> +``` +match_transpose(ans) = [["A", "C", "E"], ["B", "D", "F"]] +``` ## Example 1 : Grouping example @@ -53,12 +99,14 @@ In our first example, the student is asked to place functions, given as equation As a minimum it is recommended to include: - Load the matching library. -- Define all items in the available list as a two-dimensional array, where each item is an array of the form `["<ID>", "<actual item contents>"]`. You will use the `"<ID>"` string to write solutions and assess student inputs; the second item is what is displayed to the student. +- Define all items in the available list as a two-dimensional array, where each item is an array of the form `["<ID>", "<actual item contents>"]`. +You will use the `"<ID>"` string to write solutions and assess student inputs; the second item is what is displayed to the student. - Randomly permute the available items. - The headers that will appear on top of the answer columns. -- The correct answer as a two-dimensional array. This should be column grouped. +- The correct answer as a two-dimensional array. +This should be column grouped. -For our example, the `Question variables` field looks as follows. +For our example, the _Question variables_ field looks as follows. ``` stack_include("contribl://matchlib.mac"); @@ -71,14 +119,14 @@ steps : [ ["sgn", "\\(f(x) = \\text{sgn}(x)\\)"] ]; +steps: random_permutation(steps); + headers: [ "Differentiable", "Continuous, not differentiable", "Discontinuous" ]; -steps: random_permutation(steps); - ans: [ ["sq", "sin"], ["abs", "sqrt"], @@ -108,14 +156,22 @@ to their appropriate category. </p> <p style="display:none">[[input:ans1]] [[validation:ans1]]</p> ``` +### Question note + +A question note is required due to the random permutation of `steps`. We use: +``` +{@map(first, steps)@} +``` + ### Input: ans1 1. The _Input type_ field should be **String**. 2. The _Model answer_ field should construct a JSON object from the teacher's answer `ta` using `match_correct(ans, steps)`. -3. Set the option "Student must verify" to "no". -4. Set the option "Show the validation" to "no". +3. Set the option _Student must verify_ to "no". +4. Set the option _Show the validation_ to "no". +5. Add `hideanswer` to _Extra options_. -Steps 3 and 4 are recommended, otherwise the student will see unhelpful code representing the underlying state of their answer. +Steps 3, 4 and 5 are strongly recommended, otherwise the student will see unhelpful code representing the underlying state of their answer. ### Potential response tree: prt1 @@ -123,17 +179,256 @@ Define the feedback variable ``` sans: match_interpret(ans1); ``` -This provides the student response as a two-dimensional array of the same format as `ans`. At this point the author may choose to assess by comparing `sans` and `ans` as they see fit. In this case, the order _within_ a column really doesn't matter, but the order of the columns does of course. So we may convert the columns of `sans` and `ans` to sets in feedback variables using `match_column_setify` from `matchlib.mac`. +This provides the student response as a two-dimensional array of the same format as `ans`. +At this point the author may choose to assess by comparing `sans` and `ans` as they see fit. +In this case, the order _within_ a column really doesn't matter, but the order of the columns does of course. +So we may convert the columns of `sans` and `ans` to sets in feedback variables using `match_column_set` from `matchlib.mac`. ``` -sans: match_column_setify(sans); -ans: match_column_setify(ans); +sans: match_column_set(sans); +ans: match_column_set(ans); ``` We can then do a regular algebraic equivalence test between `sans` and `ans`. You should turn the node to `Quiet: Yes`, otherwise the student will see unhelpful code if they the answer wrong. -## Example 2 : Grid example using an index +## Example 2 : Grid example + +Here, the student is asked to drag functions and their derivatives to relevant columns and rows. +This particular example could work as a grouping example in the vein of Example 1 above, however the key difference +here is that the student can drag an item to any position in the grid, whereas in grouping items can only be added +to the end of a growing column list. + +Much of this example is very similar to Example 1 above, with the following key differences: +- The `parsons` block should include a specified `rows` parameter. +- The `match_correct` function should use `true` as a third parameter inside _Model answer_. +- The `match_interpret` function should use `true` as a third parameter inside the PRT. +- We also define our PRT answer test differently, since we care only about the order within a row being preserved. +However this difference is not _required_ and is due only to the nature of the question (i.e., what we want to assess from this question is +different from the one in Example 1), rather than from any system requirements. + +### Question variables + +As a minimum it is recommended to include: +- Load the matching library. +- Define all items in the available list as a two-dimensional array, where each item is an array of the form `["<ID>", "<actual item contents>"]`. +You will use the `"<ID>"` string to write solutions and assess student inputs; the second item is what is displayed to the student. +- Randomly permute the available items. +- The headers that will appear on top of the answer columns. +- The correct answer as a two-dimensional array. This should always be column grouped. + +For our example, the `Question variables` field looks as follows. +``` +stack_include("contribl://matchlib.mac"); + +steps : [ + ["f", "\\(y = x^2\\)"], + ["g", "\\(y = x^3\\)"], + ["dfdx", "\\(y' = 2x\\)"], + ["dgdx", "\\(y' = 3x^2\\)"], + ["df2d2x", "\\(y'' = 2\\)"], + ["dg2d2x", "\\(y'' = 6x\\)"] +]; + +steps: random_permutation(steps); + +headers: [ + "Function", + "\\(d/dx\\)", + "\\(d^2/d^2x\\)" +]; + +ans: [ + ["f", "g"], + ["dfdx", "dgdx"], + ["df2d2x", "dg2d2x"] +]; +``` + +### Question text + +Here we should: +- Write the question text itself. +- Open the `parsons` block with `input`, `columns` and `rows` header parameters. +- Transfer the variables from `Question variables` into a JSON inside the `parsons` block as appropriate. +- Close the `parsons` block. +- Set `style="display:none"` in the input div to hide the messy state from the student. + +``` +<p>Drag the items to match up the functions with their derivatives. </p> +[[parsons input="ans1" columns="3" rows="2"]] +{ + "steps" : {#stackjson_stringify(steps)#}, + "headers" : {#headers#} +} +[[/parsons]] +<p style="display:none">[[input:ans1]] [[validation:ans1]]</p> +``` + +### Question note + +A question note is required due to the random permutation of `steps`. We use: +``` +{@map(first, steps)@} +``` + +### Input: ans1 + +1. The _Input type_ field should be **String**. +2. The _Model answer_ field should construct a JSON object from the teacher's answer `ta` using `match_correct(ans, steps, true)`. +3. Set the option _Student must verify_ to "no". +4. Set the option _Show the validation_ to "no". +5. Add `hideanswer` to _Extra options_. + +Steps 3, 4 and 5 are strongly recommended, otherwise the student will see unhelpful code representing the underlying state +of their answer. + +### Potential response tree: prt1 + +Define the feedback variable +``` +sans: match_interpret(ans1, true); +``` +This provides the student response as a two-dimensional array of the same format as `ans`. +At this point the author may choose to assess by comparing `sans` and `ans` as they see fit. In this case, the _order of the rows themselves_ really doesn't matter, but the order of the rows does of course. So we may convert the list of rows of `sans` and `ans` to a set in feedback variables using `match_set_row` from `matchlib.mac`. +``` +sans: match_set_row(sans); +ans: match_set_row(ans); +``` +We can then do a regular algebraic equivalence test between `sans` and `ans`. +You should turn the node to `Quiet: Yes`, otherwise the student will see unhelpful code if they the answer wrong. + +## Example 3 : Grid example with an index + +One can add a left-hand index to the grid in Example 2 by defining an `index` array in _Question variables_ and passing this through in the JSON inside the `parsons` block. +This will fix the row order and simplify assessment. + +Important points: +- An item that appears in both the header and the index is **required**. +This item should appear in the index and not in the header. +- Reduce the value of the `columns` parameter in the `parsons` block by one: this corresponds only to the number of answer columns. +- Pass the index as the value of key `"index"` inside the JSON within the `parsons` block. + +### Question variables + +The question variables for Example 2 with an index is as follows. +``` +stack_include("contribl://matchlib.mac"); + +steps : [ + ["dfdx", "\\(y' = 2x\\)"], + ["dgdx", "\\(y' = 3x^2\\)"], + ["df2d2x", "\\(y'' = 2\\)"], + ["dg2d2x", "\\(y'' = 6x\\)"] +]; + +steps: random_permutation(steps); + +headers: [ + "\\(d/dx\\)", + "\\(d^2/d^2x\\)" +]; + +index: [ + "Function", + "\\(y = x^2\\)", + "\\(y = x^3\\)" +] + +ans: [ + ["dfdx", "dgdx"], + ["df2d2x", "dg2d2x"] +]; +``` + +### Question text + +``` +<p>Drag the items to match up the functions with their derivatives. </p> +[[parsons input="ans1" columns="2" rows="2"]] +{ + "steps" : {#stackjson_stringify(steps)#}, + "headers" : {#headers#}, + "index" : {#index#} +} +[[/parsons]] +<p style="display:none">[[input:ans1]] [[validation:ans1]]</p> +``` + +### Question note + +A question note is required due to the random permutation of `steps`. We use: +``` +{@map(first, steps)@} +``` + +### Input + +This is exactly the same as Example 2. + +1. The _Input type_ field should be **String**. +2. The _Model answer_ field should construct a JSON object from the teacher's answer `ta` using `match_correct(ans, steps, true)`. +3. Set the option _Student must verify_ to "no". +4. Set the option _Show the validation_ to "no". +5. Add `hideanswer` to _Extra options_. + +### PRT + +As in Example 2, we first extract the two-dimensional array of used items from the students input. +``` +sans: match_interpret(ans1, true); +``` +At this point the author may choose to assess by comparing `sans` and `ans` as they see fit. +Since we have fixed the order of both dimensions, there is only one correct answer which is given by `ans`. +Hence we have a basic PRT which tests only algebraic equivalence between `sans` and `ans`. +As always, turn the node to `Quiet: Yes`, otherwise the student will see unhelpful code if they the answer wrong. + +## Example 4 : Using images + +Through the use of STACK's `plot` function, which wraps Maxima's `plot2d`, static images can also be included within items. +Apart from modifying the content of the steps of Example 2, the key difference here is that the width and height of the items must also be specified in the block parameter, to make sure that plots fit inside. +This can be done by specifying the `item-width` and `item-height` parameters within the block header of `parsons`. +Because of this, it is recommended to always specify the `[size, x, y]` option within `plot`, and add some padding to `x` and `y` to define the values of `item-width` and `item-height`. +Example 2 with plots rather than equations is given below. + +### Question variables + +``` +stack_include("contribl://matchlib.mac"); + +steps: [ + ["f", plot(x^2,[x,-1,1], [size, 200, 200])], + ["g", plot(x^3,[x,-1,1], [size, 200, 200])], + ["dfdx", plot(2*x,[x,-1,1], [size, 200, 200])], + ["dgdx", plot(3*x^2,[x,-1,1], [size, 200, 200])], + ["df2d2x", plot(2,[x,-1,1], [size, 200, 200])], + ["dg2d2x", plot(6*x,[x,-1,1], [size, 200, 200])] +]; + +steps: random_permutation(steps); + +headers: ["Function", "\\(d/dx\\)", "\\(d^2/d^2x\\)"]; + +ans: [ + ["f", "g"], + ["dfdx", "dgdx"], + ["df2d2x", "dg2d2x"] +]; +``` + +### Question text + +``` +<p>Drag the items to match up the functions with their derivatives. </p> +[[parsons input="ans1" columns="3" rows="2" item-height="250" item-width="250"]] +{ + "steps" : {#stackjson_stringify(steps)#}, + "headers" : {#headers#}, +} +[[/parsons]] +<p style="display:none">[[input:ans1]] [[validation:ans1]]</p> +``` + +### Question note, Inputs and PRT -## Example 4 : Grid example without an index +These are exactly the same as Example 2. -## Example 3 : Using images