TMKR

TestMaKeR by Tom De Vries

Introduction and disclaimer

The idea behind these programs was to use Mathematica as a means to create math tests, at "random".  "Question generators" are written that have the computer pick values for a problem, display the problem, and then create an answer.

The motivation for this was the work I have been doing in online education.  I wanted to be able to create multiple versions of tests so I could reduce the ability to "cheat", send out answer keys for marked work with no fear they would be "shared", and finally, on a more positive note, to allow students opportunites to learn and improve their grades by taking a test over again.

I am NO programmer so much of this is "hacked" together.  I am currently using these programs, so they do work, but I had not thought I could put them together in a form that would allow me to easily share  with others.  Putting together some packages of questions has allowed me a way of doing that, but I make no claim that this is a flawless set of programs!  Let the user beware!

Setting things up

I created a new folder called TMKR to hold the banks of questions I created and am creating.  I put this folder in the AddOns --> ExtraPackages  folder that you should be able to find in the file directories created when you installed Mathematica .  I know that there are ways of telling Mathematica where to search for packages, but as far as I can tell you, you will need to create a new folder called TMKR, and put this in the ExtraPackages folder.  Place any TMKR package (name.m) files in that folder.

I am using Mathematica version 4, and I make no claims about downward compatability!

Loading in a bank of questions

If you set things up correctly, the command line below should load in a bank of questions to experiment with.

Needs["TMKR`Bank1`"]

Loading in this bank of questions creates three global variables.  One of them is called qnum.  This keeps track of numbering the questions on a test.   It starts with a value of 0.  Each time a question is created, it adds one to qnum so the questions are numbered sequentially as they are created.

Then there are two variables that represent notebook objects.  You will have noticed that two notebooks were created when you read in bank1.  The question generators write their information to "questions" and "answers", two notebooks.  You can actually write the questions and answers to the same file as I will demonstrate later, but for now, let's use things the way they are.

Some information about the bank

?Bank1

?bank1

All the questions in Bank1 are questions that sample the same mathematical concept of  finding the area of a geometric figure that is some combination of simple figures.  The way I built the banks was to create groups of questions that , "in theory", would all sample the same mathematical idea.  This bank of questions, at present, has 14 problems in it.  Each of the questions is "roughly" equivalent to the others, in that they all sample the same mathematical idea.  The questions are different, but related.

Using the question generators

Using the bank of questions is very simple.  There are three ways to do it, as illustrated below.

bank1[1];

Run the above line and you should see a question appear in the questions notebook, and an answer appear in the answers notebook.

bank1[1];

Evaluate the line again, and you get the "same" problem, with different values.

Do[bank1[1],  {5}];

5 versions of the same problem.

You can also give bank a list of questions to run, bank is a listable function

bank1[Range[13]];

bank1[{3, 6, 8, 8, 2}];

Each generator has a bank number and question number listed in the answer for the problem.  This allows for easy tracking of what the question is, and for making corrections to the programs where necessary.

Using bank1[ ]  has the computer choose one of the thirteen problems at random, and run it.

bank1[]

Using this bank of questions in this way provides TWO levels of "randomness".  Each question generator chooses parameters for the problem at random, so even if the same question number from the bank is chosen, the numbers will be different.  Secondly,  the computer can choose from 14 different problems.

Graphics in problems

Just a note.  Many of the generators use graphics, and sometimes they can look a little "funny"

Usually this is easily remedied by simply dragging the graphic to make it larger.

Dealing with "static" problems

There are many questions that can't be "randomized".  For example, something like "how many degrees are in a right angle?".  It may still be required to ask these kinds of questions on tests.  In order to handle this, I simply created a bank with MANY problems of the same type, all of them "static".  By static I mean that the question is the same each time.  This can be illustrated with bank2.

Needs["TMKR`Bank2`"]

?Bank2

?bank2

bank2[1]

If you evaluate the above line, you will see the question and answer appear in the questions and answers notebook.  If you run the line again, you will see the question is exactly the same.  This is a static problem.

bank2[1]

Even though the question is static, since there are 34 problems in the bank, you can use bank2[ ] and have the computer pick one at random.  Students will get different problems on their tests, and a student who retakes a test will have a good chance of getting a different problem each time they write (assuming you have enough questions in the bank!)  This allows for including "good" static problems on tests.

Do[bank2[], {5}]

TMKR utilities

This section contains a brief description of the utilities used by the TMKR banks.  I expect I will add to these, but at present, these are utility programs that I have used and wanted to have available to all banks of questions.

SETUP

The utilities are called up by the Setup package.  These utilities are used by all the question banks and are put here so they are available to them all.  Setup is called by any bank of questions loaded in.

Needs["TMKR`Setup`"]

Here is a list of  names and variables declared by the package

?TMKR`Setup`*

Why all the single letters?

All the individual letters are there because when I used symbols in equations and such like,  (for example, (x^2) - 2  x) , I got strange things in the questions.  Declaring these variables as part of the package did the trick.

create

I like this one!!  It allows for easy access to the question banks and the questions.  It allows for a VERY simple way to create sets of questions for a test.
There  are  Three flavors...

Before you go on, you might want to clear the stuff in the questions and answers window

SelectionMove[questions, All, Notebook];
NotebookDelete[questions];
SelectionMove[answers, All, Notebook];
NotebookDelete[answers];

Flavour 1

Flavour 1, just give a single number that represents a bank number
eg  create[1]   
This calls up bank 1 and runs a question at random from bank 1

create[1]

create[200]

Flavour 2

Flavour 2, give a list of two numbers as input
eg.   create [{1,2}]  
This calls up bank 1 and runs question 2 from bank 1

create[{1, 2}]

Flavour 3

Level 3   give a list as input, and the second part of the list is a list   
eg.  create [{1,{2,5,7}}]

This calls up bank1, and picks a question at random from the list {2,5,7} and runs it

create[{1, {2, 5, 7}}]

Creating a test

This function makes it VERY  easy to create a test or assignment.

First lets clear out the windows again

SelectionMove[questions, All, Notebook];
NotebookDelete[questions];
SelectionMove[answers, All, Notebook];
NotebookDelete[answers];

Reset the question number

qnum = 0;

The banks I have included with this demo are bank1, bank2, bank200 and bank201

Map[
        create,  {1, 1, 1, {1, 4},  {1, 13},  2, 2, 2,  
          200,  {200,  {1, 3}},  201}];

I think this is pretty slick!  What did this just do?  Look through the list one part at a time

{1,{1,4}, {1,13}, 2,2,2, 200, {200, {1,3}}, 201}
This 1 picks a question at random from all the questions in bank 1

{1,{1,4}, {1,13}, 2,2,2, 200, {200, {1,3}}, 201}
This runs question 4 from bank 1

{1,{1,4}, {1,13}, 2,2,2, 200, {200, {1,3}}, 201}
This runs question 13 from bank 1

{1,{1,4}, {1,13}, 2, 200, {200, {1,3}}, 201}
This 2 picks a question at random from all the questions in bank 2

{1,{1,4}, {1,13}, 2, 200, {200, {1,3}}, 201}
This 200 picks a question at random from all the questions in bank 200

{1,{1,4}, {1,13}, 2, 200, {200, {1,3}}, 201}
Choose a question from bank 200, but only pick from either question 1 or 3

{1,{1,4}, {1,13}, 2, 200, {200, {1,3}}, 201}
Choose a question at random from those in bank 201

You could now go through the questions and answers file and look at the question I.D.'s to see what the above resulted  in.

Creating a set of questions very randomly

Using create it would be possible to be able to select banks at random to create assignments.

Something like this...  Here is a possible list of banks that could be sampled from at some point in a math course.  Banks 1,2,3 are such that the students can do all the questions.  Bank 4, the students can only do question 2.  Banks 5 and 6, the students can do them all.  Bank 13, the students can only do questions 1,2,3 and 4.  Bank 70, the students can do them all

banklist  =  {1, 2, 3, {4, 2},  5, 6, {13, {1, 2, 3, 4}},  
          70};

pickrandom is defined in the Setup package (see further below)

pickrandom[banklist]

This would pick a bank or a "banklist" at random.  Feed it to Create.....  I can't illustrate this too well since I have only 4 banks of questions available!

pickrandom

This function takes a list as input and picks an item at random from the list

?pickrandom

pickrandom[{1, 2, 3, a, b, c}]

pickrandom[{1, 2, 3, a, b, c}]

It works by finding the length of the list, say 20  (I.E.) there are 20 members of the list.
Then, it picks a number at random from 1 to 20
Finally, it outputs that "part" of the list.

It does work with a single number...

pickrandom[{2}]

rand

?rand

Just to save typing!

Random[Integer,  {-5, 5}]

is the same thing as

rand[-5, 5]

randex

used to create random numbers EXCEPT FOR.....  The first number is the lower limit, the second is the upper limit, and the third is the number to exclude

Table[randex[-5, 5, 0], {50}] // Sort

No zeroes!

rot

?rot

This is the transformation matrix for a rotation by a given angle

rot[90]

rot[180]  . {3, 4}

This could be trouble since it changes the input to radians.  The input is in degrees.

rot[90] . # & /@ {{3, 4},  {5, 6}}

Map[rot[90] . # &,  {{3, 4},  {5, 6}}]

SimpleGrid

?SimpleGrid

©Wolfram Research, Inc. 1989
This code was written by Shawn C. Sheridan under the employment of Wolfram Research, Inc.  WRI holds all copyrights.  The code may be distributed freely and may be used for any purpose other than resale.

SimpleGrid takes two iterator specifications and an optional third list of line attributes.  The iterator specifications should be in the same format used by the Table function.

thegrid = 
        SimpleGrid[{x, -1, 3, 0.5}, {y, -4, 
            4}, {Thickness[ .001]}];

thegraph = 
        Plot[2 x (x - 1) (x - 2), {x, -1, 3}];

Show[thegrid, thegraph, PlotRange -> {-4, 4}];

spokes

?spokes

This is used to create a set of spokes to mark off a circle

\(\(Show[Graphics[
		spokes[8]
	], \ 
        AspectRatio -> Automatic];\)\)

The first one is always at an angle of 0 degrees

trialrun

?trialrun

Simply sets the selected notebook to be the one that the questions and answers will be written into.  I find this handy when I am creating a generator.

trialrun[];

bank1[1]

New question and answer windows

You can send the questions and answers to different places...  First, close the questions and answers windows...

You can create new notebooks for questions and answers in many different ways.  Try out the following

questions = 
        NotebookCreate[
          WindowTitle -> "More Questions"];

answers = 
        NotebookCreate[WindowTitle -> "More answers"];

qnum = 0;

bank1[]; bank2[]; bank200[];

Close the notebooks (if you want) and try this...

answers = questions = NotebookCreate[];

qnum = 0;

bank1[]; bank2[]; bank200[];

This writes the question and answer to the same notebook

Creating your own generators

My "dream" would be to have other teachers working together on this project and to create many question generators.  I know that with Mathematica, it should be possible to create some incredible questions.  I have created 600 so far!, but they are pretty "basic".  My programming skills need some time to develop.  I am confident that most high school math concepts could be sampled in this way, it just might take some time to develop good ways to ask the questions.

What follows are just a few simple hints on some things that help me to create these programs.

The underlying structure of a cell

One of the hardest things about creating these generators was getting things to display properly.  Mathematica can help a lot here though.  Simply type the expression you will use in the question, and then use the  Format menu to "Show Expression".  This will show you how Mathematica "sees" your expression.

This is a sample line, with 5 (Sqrt[ x]) and (y^2)and  θ

If you select the cell and use Format -->Show Expression you get what you see below.

Cell[TextData[{
  "This is a sample line, with ",
  Cell[BoxData[
      FormBox[
        RowBox[{"5", 
          SqrtBox["x"]}], TraditionalForm]]],
  " and ",
  Cell[BoxData[
      FormBox[
        SuperscriptBox["y", "2"], TraditionalForm]]],
  "and  \[Theta]"
}], "Text"]

This provides some hints for the box forms needed to create what you want

Writing a text cell to a "different" notebook

The function NotebookWrite allows you to write information to a notebook

?NotebookWrite

So if you create a notebook....

fred = 
        NotebookCreate[WindowTitle -> "Testing"];

...and copy the cell we just created above and paste it in as you can see below...

\(NotebookWrite[
	
	
      fred, \ 
	 (*the\ notebook\ to\ write\ to*) 
	

      Cell[TextData[{
\ \ "This is a sample line, with ", 
\ \ Cell[
              BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ RowBox[{"5", \ 
\ \ \ \ \ \ \ \ \ \ SqrtBox["x"]}], \ 
                  TraditionalForm]]], 
\ \ " and ", 
\ \ Cell[
              BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ SuperscriptBox["y", \ "2"], \ 
                  TraditionalForm]]], 
\ \ "and  θ"
}], \ "Text"]
	 (*what\ to\ write*) 
]\)

Now, if you do something like....

variable  =  Random[Integer,  {2, 20}];

And replace the "5" in the line above.... with the word variable... as shown below

\(NotebookWrite[
	
	
      fred, \ 
	 (*the\ notebook\ to\ write\ to*) 
	

      Cell[TextData[{
\ \ "This is a sample line, with ", 
\ \ Cell[
              BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ RowBox[{variable, \ 
\ \ \ \ \ \ \ \ \ \ SqrtBox["x"]}], \ 
                  TraditionalForm]]], 
\ \ " and ", 
\ \ Cell[
              BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ SuperscriptBox["y", \ "2"], \ 
                  TraditionalForm]]], 
\ \ "and  θ"
}], \ "Text"]
	 (*what\ to\ write*) 
]\)

You get a "random" line...

Writing a graphics cell to a "different" notebook

Create a graphic

\(\(p = 
        Show[Graphics[{
				Line[{{0, 0}, \ {2, 
                    2}}], 
				{RGBColor[0, 0, 1], \ 
                Disk[{3, 3}, \ 1]}
			}]];\)\)

Better yet, create it but tell the computer not to show it...

\(\(p = 
        Show[Graphics[{
				Line[{{0, 0}, \ {2, 
                    2}}], 
				{RGBColor[0, 0, 1], \ 
                Disk[{3, 3}, \ 1]}
			}], \ 
          DisplayFunction -> Identity];\)\)

And use this "magic" line.  I never would have figured this out, I saw it in a notebook created by a Mathematica "guru"

NotebookWrite[fred, 
        Cell[GraphicsData["PostScript",  
            DisplayString[p]]]];

The graphic gets sent to the notebook we created.

A basic template for a question generator

Here is a basic template I use for creating a question generator.  It includes the various kinds of lines that might occur in a problem.  I split the generator up into sections, and tried to describe each section

a)	Use Module so the variables are local

b)	I used a Label so send the generator back to the beginning so I could ensure variables didn't have values that I didn't want.

c)	Create variables for the problem...

d)	Go back to the beginning if the variables have values you don't want

e)	increment the question number variable qnum.  I did this before the question just to have all the variables worked out at the beginning of the problem.  It helped me to organize better

f)	Write the question number and a tab stop, and then start the question

g)	Write a graphic to the question file

h)	Write the question number and a tab stop, and then start the answer.  I have gotten into the habit of repeating the question so my answer key includes the question as well.  It has made grading easier.

i)	more of the answer

j)	a graphic in the answer

k)	the question id for easier tracking and reference

\(bankπ[
        42] := 

Module[{p, 
          variable},  (*a*) 

Label[
          start];  (*b*) 

variable\  = \ 
          Random[Integer, \ {1, 10}]; 
p = 
          Show[Graphics[{
				Line[{{0, 0}, \ {2, 
                      2}}], 
				{RGBColor[0, 0, 1], \ 
                  Disk[{3, 3}, \ 1]}
			}], \ 
            DisplayFunction -> 
              Identity];  (*c*) 

If[
          variable == 5, \ 
          Goto[start]];  (*d*) 

qnum = 
          qnum + 1;  (*e*) 

NotebookWrite[questions, 
          Cell[TextData[{qnum, \ ".	", 
\ \ "This is a sample line, with ", 
\ \ Cell[
                  BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ RowBox[{variable, \ 
\ \ \ \ \ \ \ \ \ \ SqrtBox["x"]}], \ 
                      TraditionalForm]]], 
\ \ " and ", 
\ \ Cell[
                  BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ SuperscriptBox["y", \ "2"], \ 
                      TraditionalForm]]], 
\ \ "and  θ"
}], \ "Text"]];  (*f*) 

NotebookWrite[
          questions, 
          Cell[GraphicsData["PostScript", \ 
              DisplayString[
                p]]]]; \ \  (*g*) 


NotebookWrite[answers, 
          Cell[TextData[{qnum, \ ".	", 
\ \ "This is a sample line, with ", 
\ \ Cell[
                  BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ RowBox[{variable, \ 
\ \ \ \ \ \ \ \ \ \ SqrtBox["x"]}], \ 
                      TraditionalForm]]], 
\ \ " and ", 
\ \ Cell[
                  BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ SuperscriptBox["y", \ "2"], \ 
                      TraditionalForm]]], 
\ \ "and  θ"
}], \ "Text"]];  (*h*) 

NotebookWrite[
          answers, 
          Cell[TextData[{
\ \ "This is not really an answer, with ", 
\ \ Cell[BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ RowBox[{variable\ , \ 
\ \ \ \ \ \ \ \ \ \ SqrtBox["x"]}], \ 
                      TraditionalForm]]], 
\ \ " and ", 
\ \ Cell[
                  BoxData[
\ \ \ \ \ \ FormBox[
\ \ \ \ \ \ \ \ SuperscriptBox["y", \ variable], \ 
                      TraditionalForm]]], 
\ \ "and  θ"
}], \ "Text"]];  (*i*) 

NotebookWrite[
          answers, 
          Cell[GraphicsData["PostScript", \ 
              DisplayString[
                p]]]]; \ \  (*j*) 


NotebookWrite[answers, \ 
          Cell[TextData[{"This question is from Bank π, question 42"}]]]; \  (*k*) 
]\)

If you don't have them, create windows to write our question generator to.

questions = 
        NotebookCreate[
          WindowTitle -> "Test the question"];

answers = 
        NotebookCreate[WindowTitle -> "Test the answer"];

Try it out!!

Do[bankπ[42], {5}]

Template Package

One of the files that I have included with this demo is a template package.  This gets me in pretty far over my depth, but I use it and it works.  I create the questions first OUTSIDE of the package, and make sure they are running and doing what I would like.  Once I have several questions that sample some topic, I put them together into the package.  If you name your procedures "right" when in the development phase,  say for example,  bank50[1]  , bank50[2], bank50[3]  etc.  Then you can paste them right in, change the cells to initialization cells, save the notebook, put the notebook.m  file into the TMKR folder, and you are in business!  If anyone reading this really does want to get into writing these kinds of generators, please contact me and we can communicate further  (see below)

Contact information

At this point, I am just getting started on this project.  I am currently teaching Algebra I, Geometry, Algebra II, and Functions and Trigonometry.  I have about 600 question generators that I am in the process of putting into packages.  I have been using these programs to create tests since the beginning of September, so far, so good.  Some weeks I spend a lot of time trying to write programs to sample a certain idea, but I figure the investment of time will pay off.

I teach for an online school called NorthStar Academy , on th web at   www.northstar-academy.org
This programs have helped to create a more secure and compassionate testing environment.

I live in Edmonton, Alberta, Canada, and I work out of my home, spending most of the day in front of my (Macintosh) computer, writing notes and examples and answering emails from students all over the world, from Kazakhstan to Peru!

I would be interested in working with others on this project, or hearing any comments you have about it.

Tom De Vries
tdevries@mail2.westworld.ca

Tom De Vries

T De Vries
11003 - 132 st.
Edmonton, AB
Canada
T5M 1E4
(780)452-5958  phone
(780)454-0173  fax