WOLFRAM

Trivial Pursuits: Applications and Diversions with the Wolfram Language

Mark Greenberg is a retired educator and contributor to the Tech-Based Teaching blog, which explores the intersections between computational thinking, edtech and learning. He recounts his experience adapting old game code using the Wolfram Language and deployment through the Wolfram Cloud.

Chicken Scratch is an academic trivia game that I originally coded about 20 years ago. At the time I was the Academic Decathlon coach of a large urban high school, and I needed a fun way for my students to remember thousands of factoids for the Academic Decathlon competitions. The game turned out to be beneficial to our team, and so popular that other teams asked to buy it from us. I refreshed the questions each year and continued holding Chicken Scratch tournaments at the next two schools I worked in.

Chicken Scratch

When I retired a couple years ago, I assumed that Chicken Scratch would fade into the shadows of the past. Then I began tinkering with the Wolfram Language, with its functional syntax, awesome visualization tools and curated data, and realized its potential to take the game to the next level. I started developing a new version to leverage many of the Wolfram Language’s powerful features. I’m still adding to and refining the game, but Chicken Scratch is now a fully functional desktop application that generates billions of questions in 15 academic categories. I play it with friends at parties and occasionally still in school settings. I also share the code online via GitHub, which can be seen by visiting this link.

Designing the Interface

When I first sat down to remake Chicken Scratch, it wasn’t obvious how to build an application in the Wolfram Language. The options for deployment seemed to be geared toward creating interactive documents such as .cdf files, notebooks, presentations and demonstrations. Chicken Scratch has the structure of a more traditional standalone program or web app, with buttons that trigger actions in a rectangular part of the screen. Here’s how I was able to update the game using the Wolfram Language.

The previous version of Chicken Scratch was contained within a fixed rectangle. Wolfram has two functions that define rectangular areas: Panel and Pane. The main difference between them is that Panel has an opaque background, while Pane has a transparent one.

{Panel["Hello!"
&#10005

{Panel["Hello!",ImageSize->{100,50}],Pane["Hello!",{100,50}]}

I contained the whole game inside one framed Panel (shown 1/4-size here).

Framed[Panel[coverPic
&#10005

Framed[Panel[coverPic,Alignment->{Center,Top},ImageSize->{1000,730},Background->White,FrameMargins->None]]

To display images, text and other content inside the game panel, I used Pane.

Framed[Panel[Row
&#10005

Framed[Panel[Row[{Pane[Style["$220",48],197,Alignment->Center],Pane[hand1,180,Alignment->Center],Pane[Style["text\nexample",24,Blue,TextAlignment->Center],198,Alignment->Center],Pane[hand2,,180,Alignment->Center],Pane[Style["$104",48],197,Alignment->Center]},Alignment->Center],Alignment->{Center,Top},ImageSize->{1000,730},Background->White,FrameMargins->None]]

Now that I could control the appearance of Chicken Scratch, the next step was to control its behavior. In the previous version, the game had a cover image that would change to the main game interface when clicked. Clicking various parts of the interface would cause different reactions from the game. For instance, clicking a correct answer would cause a joyous sound to play and the game to display the categories.

The Wolfram Language has a dizzying array of interface objects that allow a program to react to the user’s actions, including Button, Slider, Checkbox, ListPicker and many more. Read the guide Control Objects in the documentation for a comprehensive list. Chicken Scratch only needs Button, so that’s all I’m going to discuss here. If you understand how to use Button, you can extend that to the other types of control objects.

In Chicken Scratch, clicking a button usually changes the content of a pane.

img=hand1; pane=Pane
&#10005

img=hand1;
pane=Pane[img];
Print[pane]
button=Button["do something",img=hand2;pane=Pane[img]]

If you try that code, though, it doesn’t work. The content of the pane needs to change in a visible manner. To accomplish this, wrap the content of the pane in the function Dynamic and then make the button reassign the symbol that’s inside the pane.

img=hand1; pane=Pane
&#10005

img=hand1;
pane=Pane[Dynamic[img]];
button=Button["Do Something",img=If[img==hand1,hand2,hand1]];
{pane,button}

With Panel and Pane as building blocks, and Dynamic and Button for the interactions, I was able to make a fully functional version of the game Chicken Scratch.

Including the Support Files

Chicken Scratch is a trivia game, so it needs lots of questions. In the Wolfram Language, I can write pods that generate new questions, often millions of unique ones, from a single block of a few dozen lines of code. Currently, the game has 260 such question pods. At first, the pods were part of the main notebook. However, this got unwieldy as the code grew in size. The desktop environment became sluggish, and it took forever to find any particular line of code. I decided to store the question pods in the cloud and have the main interface call them when needed. This worked extremely well.

The function that calls a question pod is URLExecute[podName]. Each question pod is designed to return the following pieces of information:

      1. Question (q) — usually a string or a StringForm
      2. Answer (ans) — an integer from 1 to 4
      3. Choices (mixed) — four possible answers, randomly ordered
      4. Graphic (pic) — optional 2D or 3D visual

The main interface file processes these and presents them to the players. Here is an example of the code in one of the question pods. To see the type of output it produces, just execute it as is. To deploy this in the cloud, remove the two commented parts of the code and change TraditionalForm[…] to InputForm[…].

(*CloudDeploy
&#10005

(*CloudDeploy[
Delayed[
APIFunction[{},*)
mat=Partition[RandomChoice[Range[-12,12],4],2];
det=Det[mat];
choices={det};
While[Length[choices]<4,try=Round[RandomVariate[NormalDistribution[0,120]]];
If[Not[MemberQ[choices,try]],choices=Append[choices,try]]];
q=HoldComplete[StringForm["If `1` and `2`=|`3`|, then what is the value of `2`?",A==h1,d,A]]/.{h1->matrix[mat]};
mixed=RandomSample[choices];
ans=Position[mixed,choices[[1]]][[1,1]];
TraditionalForm[{q,ans,mixed}](*&]],"CS_pack_Alge6",Permissions"Public"]*)

Here is the code from a question pod that returns a graphic.

(*CloudDeploy
&#10005

(*CloudDeploy[
Delayed[
APIFunction[{},*)
tot=RandomInteger[{100,1000}];
cums=Sort[RandomSample[Range[1,tot],3]];
quant={cums[[1]],cums[[2]]-cums[[1]],cums[[3]]-cums[[2]],tot-cums[[3]]};
pic=PieChart[quant,SectorOrigin->{90°,-1},ChartLegends->{Style["neither A nor B",18],Style["only A",18],Style["both A and B",18],Style["only B",18]}];
qOp=RandomChoice[{"against both measures"->quant[[1]],"for A but against B"->quant[[2]],
"for both measures"->quant[[3]],
"for B but against A"->quant[[4]],"for A"->quant[[2]]+quant[[3]],
"for B"->quant[[3]]+quant[[4]],"for any measure"->quant[[2]]+quant[[3]]+quant[[4]],"for only one measure"->quant[[2]]+quant[[4]]}];q=StringForm["If `1` people voted on measures A and B, how many people voted `2`?",tot,Keys[qOp]];
choices=Take[DeleteDuplicates[Prepend[RandomSample[Range[1,tot-1],4],Values[qOp]]],4];
mixed=RandomSample[choices];
ans=Position[mixed,choices[[1]]][[1,1]];
TraditionalForm[{q,ans,mixed,pic}](*&]],"CS_pack_Grap9",Permissions"Public"]*)

This system works beautifully, but I must add a couple of cautionary notes. Because the question pods are cloud objects, the data that they return must be web friendly. In other words, any characters that HTML wants in entity form will cause an error. For the question pods that require unusual characters, ToCharacterCode and FromCharacterCode get them safely through the web environment.

Likewise, if a question pod returns an image, it has to be handled in a different way. In this case, I use Hold to pass the commands that produce the image to the interface code where they can be safely executed. Questions can be slow to appear, on rare occasions up to 20 seconds, because the game retrieves them from the cloud. I have it on good authority that there is a better way for me to deploy Chicken Scratch that does not involve the cloud. I believe it, and that would surely speed up the display of questions. However, the advantages of generating questions from one cloud location currently outweigh the slight gain in download speed I’d get from generating them locally.

Feel free to try Chicken Scratch and use it as you see fit. The game is now geared toward general education and the high-school level, but adults enjoy playing as well. If you have any comments or questions, you may contact me.

Comments

Join the discussion

!Please enter your comment (at least 5 characters).

!Please enter your name.

!Please enter a valid email address.

2 comments