Saturday, December 7, 2013

LiveCode 6.5 Now Offers Automatic Scaling

LiveCode 6.5 was just released with a new feature that certainly has my attention -- automatic scaling of applications on mobile platforms. The more precise term for this, or at least the one the company is using, is resolution independence.

To learn more, check out the following web site:

http://livecode.com/livecode-6-5/

Better yet, check out this short video:

https://www.youtube.com/watch?v=idctzDFoGj8#t=13

This is a rather big deal. This means that one's application, designed for one screen size such as the iPhone, will now scale automatically if used on the iPad, iPad-mini, or on the myriad of screen sizes found on the Android platform.

Disclaimer: I have not yet tried working with this new feature, so I'm just trusting the company's information at this point.

To take advantage of this feature, only one line of code is needed within a preOpenStack handler:

on preOpenStack
     set the fullscreenmode of this stack to "exactFit"
end preOpenStack

Below is a copy and paste of excerpts about the fullscreenmode property from the LiveCode dictionary:

And I quote...

fullscreenmode

Syntax:
set the fullscreenmode of stack to {empty|"exactFit"|"letterbox"|"noBorder"|"noScale"}
 

Summary:
Sets the full screen scaling mode of a stack.

Examples:
set the fullscreenmode of this stack to empty
set the fullscreenmode of this stack to "noScale"

Use the fullscreenmode property to choose the most appropriate full screen scaling mode for the application.
 

Parameters:
empty - The stack is resized (not scaled) to fit the screen. (default) This is the existing behavior.
"exactFit" - Scale the stack to fill the screen. This stretches the stack if the aspect ratio of the screen does not match that of the stack.
"letterbox" - Scale the stack, preserving the aspect ratio, so all content is visible. Some blank space may remain if the screen and stack aspect ratios do not match.
"noBorder" - Scale the stack to fill the screen, preserving the aspect ratio. If the stack and screen aspect ratios do not match, the left / right or top / bottom extremes of the stack are not visible.
"noScale" - The stack is not scaled, but is centered on the screen instead.

Value:
The fullscreenmode returns the mode to which this property is set.

Comments:
There are multiple ways in which a stack can be resized or scaled to take full advantage of the available screen space. fullscreenmode allows the developer to choose the most appropriate for their application.

Note: The fullscreenmode only takes affect when a stack is full screen. This is the case on mobile platforms where stacks are always full screen, or on the desktop when the fullscreen property of the stack is set to true.

The full screen scaling mode is available on all desktop and mobile platforms and operates independently from Hi-DPI support.


Monday, November 25, 2013

Creating an Autofill Option for a Text Entry

At the University of Georgia we are in the last few weeks of the semester. The project of one of the students working with LiveCode - Russ Palmer, an instructional designer at UGA's College of Pharmacy - uses a text entry for an interesting game involving the memorization of pharmaceutical information. The player must get an exact match in order to be successful, which can be rather daunting given the complexity of drug names. Russ is really not interested in requiring the player to spell everything exactly right. In previous programming languages I've used, there have always been ways to allow for simple spelling errors. LiveCode has two functions that could be used here, namely matchChunk and matchText.

We also thought about either creating a pull-down menu or a list of all the possible terms. That way the player could just choose or click on one. But choosing among a list of items is a very different learning outcome as compared to recalling a certain drug name. But as we continued to talk, we decided what we would really like to have is an autofill function. That way, the player is forced to at least begin typing the appropriate response, but could then choose among the possible entries as the player typed successive letters. Alas, I told Russ, there is no such function available in LiveCode.

Over the last few days, I've been thinking about this autofill option. Given LiveCode's excellent list processing functions I thought it was worth trying my hand at building it. I think I've created an example that does a pretty good job. In fact, I think Russ and I have invented a new kind of question type. So, you've heard of the Likert-type question? Well, now you have the Rieber-Palmer-type question!

Here is a screen snapshot of the program:
[ Get the free LiveCode Community version. ]



The field on the far right shows the "dictionary" for the autofill using a field titled "words" (yes, in hindsight I should have titled it "dictionary"). These are the only words that will appear in the autofill, but the list can be lengthened or shortened as needed. Plus, it would be very easy to make the list dynamic, such as by adding words to it as the program executes.

As the player begins to type a word, LiveCode begins a matching process after each letter is typed. All words in the dictionary that begin with that letter, then two letters, then three letters, etc. appear in a field just below the answer field. The player can then choose to just click on one of the words in the autofill list and that word is then pasted into the answer field.

To demonstrate a working example, I created a little quiz where LiveCode chooses one of the words in the dictionary at random and makes it the answer to a (bogus) question. (This code is in the card script, should you want to take a look.)

Building the Autofill function


There are two main scripts at work here: one that generates the autofill list and the other that tracks what line the player clicks on in the autofill list.

The first script is in the field "answer":

global varWord

on returninfield
   if line 1 of me = varWord then
      show field "correct"
   else
      show field "wrong"
   end if
  
   wait 1 second
   put empty into me
   hide field "autofill"
   opencard
end returninfield

on keyDown theKey
   show field "autofill"
   put theKey after me
   put empty into field "autofill"
  
   repeat with i = 1 to number of lines in field "words"
      put the number of characters in line 1 of me into varChar
      if line 1 of me = char 1 to varChar of line i of field "words" then
         put line i of field "words"&return after field "autofill"
      end if
   end repeat
  
   if the number of lines in field "autofill" > 5 then
      set the vScrollbar of field "autofill" to true
   else
      set the vScrollbar of field "autofill" to false
   end if
  
end keyDown

The first code block - "on returninfield" - is not related to the autofill function. It's only used to take whatever text is currently in the answer field when the user presses the Return key and evaluate it to see if it is a match. Appropriate feedback is given if it is or isn't a match. Then a new word is chosen for the next "question" by executing the openCard command.

 

on keyDown


The "on keyDown" script is the heart of the autofill function. Everytime the player types a key, it is triggered and the identity of the key pressed is stored in the local variable "theKey". When the first key is pressed, it is so noted and the autofill field is made visible (and the autofill field is emptied before going any further, just in case some residual text is there from a previous question).

Every time a key is pressed, a repeat loop is triggered that repeats for as many words as there are in the dictionary (again, the field titled "words" located on the right-hand side of the screen). The loop uses the variable i as a counter starting at 1 and ending with the total number of lines in the dictionary. For example, when i is 4, line i would refer to the word "basket."

So, let's pretend that the letter "b" was pressed. The next line says to "put the number of characters in line 1 of me into varChar." Of course, "me" refers to the answer field itself. So, at this point, varChar = 1 because only one letter has been typed.

Consider the next line:
if line 1 of me = char 1 to varChar of line i of field "words"

This line looks at each word in the dictionary (line i) and basically asks: Does the word begin with the letters entered so far in the answer text field? Remember, so far, only the letter "b" has been pressed. So, "char 1 to varChar" asks the computer to look at each word in each line starting with the first letter and going to letter varChar. Since varChar = 1, the match simply starts and ends with the first character. So, all words starting with "b" are considered matches. It then puts a copy of each "b" word (along with a return) into the field "autofill," which is located just below the field "answer."

Next, let's pretend that the player typed the letter "e". The same repeat loop is again triggered. This time, varChar = 2 because the letters "be" now appear in the answer field. So, "char 1 to varChar" now asks the computer to look at each word in each line starting with the first letter and going to the second letter. So, all the words starting with "be" are matches.

This pattern continue as the player types in successive letters.

The second script: Clickline


The second script is in the field "autofill". This script allows the player to click on any word in the autofill list and have it entered into the answer field. The script for this is pretty simple and takes advantage of a nifty LiveCode function named "clickline":

on mousedown
   select the clickline
   put the value of the clickLine into line 1 of field "answer"
   hide me
   put empty into me
   focus on card field "answer"
end mousedown

First, it's important to note that the field needs to be set to "lock text" for this script to work. (This is something I did not know at first. It took me about 15 frustrating minutes to discover this fact.) The easiest way to lock the text is to open the property inspector for the field (just double-clicking on the field opens the property inspector), then choose "lock text" in  Basic Properties.

The function "clickline" monitors if a line in the field has been clicked and records which line it is. I then put the value of that line into the field "answer."

The autofill field then is emptied and hidden, and the focus (as denoted by the blinking I-beam) is put back on the field "answer."

One Bug Remains


I am happy with how this autofill function works and I think it does the job. However, one bug remains that I admit I've not been able to solve. When the player deletes a letter, the autofill field does not update as I thought it would. That is, all words currently in the autofill list remain. I'm sure there is a simple explanation to this, but so far it continues to elude me. Fortunately, once you start typing letters again, the function works properly. I think I may need to trap for the delete key with some special code. Perhaps someone reading this blog posting will suggest some other solution.

Addendum 

November 27, 2013

I posted a note about this autofill file to the "Talking LiveCode" forum - here's a link to the topic thread:


Craig Newman and Mark Schonewille replied to it and provided some very valuable assistance and insights. The first, and the simplest, suggestion by Craig is to add the following code to the field "answer" completely resolved the bug:

on backspacekey
   delete the last char of me
   send "keydown" to me
end backspacekey

(I still get confused about how the "send" command works.)

Craig also provided his own version of how to accomplish the autofill feature. I'll be reviewing that approach carefully.

Mark suggested using the "filter" command, a command I did not know existed. It's a very elegant and powerful way to extract elements from a list.

I also noticed another little bug in my program that was easily fixed. I noticed that the entire list of words from the dictionary appear if you backspace all the way to the beginning. So, I added this line of code to the very end of the keydown procedure:

   if me is empty then hide field "autofill"

I've revised my autofill program. Here's the link to it:


Thanks again to Craig and Mark.

Saturday, November 2, 2013

Report from Today's LiveCode Workshop at the AECT Conference in Anaheim

Many thanks to all of the people who came to my LiveCode workshop today at the AECT conference in Anaheim. I really enjoyed teaching the workshop and sharing the "joy of programming" with everyone. Every time I teach LiveCode, I get a little better at it, so I appreciate the friendly attitude of the entire group. I enjoyed and benefited so much by meeting and getting to know such a great group of educational technology professionals. I hope everyone felt their time spent today at the workshop was worthwhile.

Special thanks go to Guanhua Chen, one of our very talented students in our research master's program at the University of Georgia, who graciously volunteered his time to assist me today. He did a fantastic job of jumping from person to person as little problems popped up. I definitely owe Guanhua several lunches back home in Athens.

I've updated my workshop web site in several ways, so I hope others who are learning LiveCode will check it out and use it as a learning tool:


I added several new resources to the site. One thing in particular stands out. In teaching the workshop previously, I found it is difficult for people to really grasp how even version one of Lunar Hotel Shuttle works. I think the obstacle is that there is a lot of code related to the physics of the simulation, plus I show this at the end of the day when everyone's brains are already pretty full of thoughts of coding and scripting. Consequently, people have a hard time seeing the underlying, elegant, structure. So, I put together a much simpler game called "Catch a Number" that uses exactly the same looping structure, but which needs very little code.

This little game demonstrates a very powerful model of scripting for projects where you have some basic script or "engine" being run continuously in the background while the user gets to interact with it various ways. This is in contrast to the more common and simpler programs that "wait" for the user to do something before anything happens.
Here is a screen snapshot of this simple game:
[ Get the free LiveCode Community version. ]


The program immediately begins counting when it is opened, starting at 1. The user then tries to "catch" a number as it flies by, with the number they caught being shown in the bottom text window. You can also pause or restart the game.

Here is a visual of the programming model or logic:


If you go to the Lunar Hotel Shuttle project section of my workshop site, you'll find this corresponds exactly to the model I use in that program.

So, I had a great time today and I hope to present this workshop again next year at the AECT conference in Jacksonville, as well as other venues as well if the opportunities present themselves.




Saturday, October 19, 2013

PersuadeMe: Finally, A Chance to Use My Random Code Generator


For the past two months, I've been totally consumed with a new project called PersuadeMe, funded (very modestly) by the Bill & Melinda Gates Foundation. It's a very cool project, involving a collaboration with many smart, talented people in the College of Education at the University of Georgia:

  • Dr. Donna Alvermann, Professor of Language and Literacy Education
  • Dr. Michael Hannafin, Professor of Educational Technology
  • Larry McCalla, Learning, Design, and Technology Doctoral Student
  • Eunbae Lee, Learning, Design, and Technology Doctoral Student
  • Joseph Johnson, Language and Literacy Education Doctoral Student

Donna is the PI on the project and is a world renowned scholar in language and literacy education. Mike Hannafin is likewise probably among the top five scholars in the instructional technology field and, of course, needs no further introduction to most people reading this blog.

PersuadeMe is a Web-based literacy tool that is designed to help students in grades four through eight engage in writing arguments on issues of interest to them. The tool features an on-line role-playing game in which students act as "Idea Innovators" and "Idea Investors." Innovators must back up their opinions with evidence. We use the stock market as a metaphor for the project where investors choose to fund those ideas they think are the most persuasive, thus driving up the "value" of those ideas. No opinion is considered right or wrong, merely more or less persuasive based on the evidence that is presented.

Here is a 3-minute conceptual video about the project that was a required part of the proposal to the Gates Foundation:


The voice you hear is that of Larry McCalla's, and he gets credit for designing and producing the video itself. (Although I think our proposal was strong, I have a hunch that the reviewers likely started with these videos and based much of their initial judgment on them. Larry deserves much credit for creating a very clear and persuasive video.)

I have many roles on the project, but my dominant role - and the one most pertinent to this blog posting - is as the programmer of the project. Yes, I know what you are thinking: "The project is doomed!" But, not so fast. I think I'm actually doing a pretty good job, even though it doesn't involve LiveCode at all. Instead, I'm programming this in PHP, supported with a mySQL database (with, of course, lots of HTML along the way). This is a proof-of-concept project to build and evalute a working prototype of the concept to show that additional funding is warranted.

Well, we are about to hold our first field test on Monday with a group of 8th graders. So, there is lots of work to do to prepare. One of my tasks is to create a bunch of generic user log-ins that we can put on index cards and distribute to students when they walk in the door. These need to be age and gender neutral, so I'm going to use animal names. I thought I would use the state mammals of the United States -- click here for Web site that I consulted. I took the liberty of reducing the list by generalizing across common types (such as the various dogs, bears, and deer in the list), and here are the resulting 28 animals:
  • moose
  • cat
  • deer
  • bear
  • whale
  • sheep
  • fox
  • panther
  • manatee
  • dolphin
  • horse
  • seal
  • bison
  • squirrel
  • raccoon
  • dog
  • mule
  • sheep
  • beaver
  • coyote
  • armadillo
  • longhorn
  • bat
  • elk
  • marmot
  • orca
  • badget
  • cow

OK, doing that research was fun and I can use these as the user names for all of the participants in our various field-tests. But, I need to also have passwords for each. Now, I could just try to make up some passwords given that I don't need very many -- we will probably have about eight students in our first field test, and maybe 10 or so more in our other field tests. But, it occurred to me that I could use my LiveCode project "Generate Random Codes" (see blog post from April 13, 2013) as a tool to generate these.

I decided to have passwords of seven characters, using lowercase letters and numbers as the raw ingredients (the server we are using doesn't distinguish between upper and lower case letters). I chose seven because the password plus the animal name comes to eight pieces of information, thus making it very unlikely other students would remember it should they happen to glance at another student's card. ("7 plus/minus 2" is the well-known limit of short-term memory.) This will allow a total of unique 78,364,164,096 passwords. I'd say this is more than sufficient! In reality, I only need about 18 for the field tests, so I decided to turn my LiveCode file loose for about 30 minutes and it generated about 5000 of these unique passwords, which I then copied and pasted to a text file.

Here is a screen shot of just some of the passwords generated by the program:

What I will do next is simply start adding the usernames to each line as I create them, saving the text file as I go. If I ever have a need for more passwords (such as assuming we get more funding from Bill and Melinda -- yes, we are on a first name basis), I'll just copy and paste the list back into LiveCode and have it start generating new passwords from there. It's great to finally have some return on my investment in learning LiveCode.

Of course, I've been spending every spare evening and weekend for the past two months working on this project, so please join me in hoping that Bill and Melinda continue to support this project by choosing to fund our next grant proposal. But, even if they don't, my investment in PersuadeMe will be returned many times over by the number of 4th-8th graders who I'm confident will find this to be an engaging and authentic place to write about the issues that matter to them.

Sunday, October 6, 2013

Creating a Classroom Observation Research Data Collection App

I'm teaching a doctoral seminar this semester titled "Developing Learning Analytics and Digital Assessment Tools for an Educational Research Program." We are demonstrating a bunch of ways to build research tools that collect data. One of the first tools I showed was LiveCode in the context of creating an app that would assist a researcher in systematically collecting data during classroom observations.

So, imagine you are sitting in a classroom and you want to make keen observations about what is going on. It's easy to imagine exchanges between the teacher and students.  Examples of teacher activities might include giving directions, lecturing, asking questions, leading a class discussion, and so on. Similarly, examples of student activities might include individual work, working in pairs, group work, making presentations, reading, and so on. And, lots more categories could be included such as tools that both teachers and students use, such as the Internet, PowerPoint, Smartboard, word processing, and so on.

Now, you might think it would be just fine to sit there with a notebook and pen and write down what you see. If you think this, then it's probably been a long time since you were in a classroom. There is no way you could keep up with what is going on. Plus, we humans are not very good are taking accurate notes when it comes to time and duration. You could try to look at your watch and note what time one activity started and ended and who was involved, but the number of errors you would make would likely make your notes not very usable.

Enter the computer. It does a marvelous job of noting what time it is, down to the millisecond if you wish. And wouldn't it be nice to simply click a button called "Teacher" or "Asking Question" for the computer to note who did what and at exactly what time. That's basically what this app does.

During week 2 of the seminar I showed a classroom observation app prototype I quickly built with LiveCode to give everyone a simple example what LiveCode could do, particularly in relation to coding time and duration. In the weeks that have gone by since, several of the students have considered using an adaptation of this first prototype as the basis of their course project. Just this past week I was conferring with Lindy Johnson, a very talented doctoral student in Language and Literary Education who is taking the course. We played around with her version of the app. In the day or two after talking with Lindy, I came up with an idea for an enhanced version of the app. This is the version I'm sharing here.

Here's a screen shot of the app:

[ Get the free LiveCode Community version. ]
 


Notice the bright red line going down the middle. This is a graphic object named "dividing line," and it figures prominently in the scripts to follow.

I built the app using the dimensions of the iPad (1024 by 768) because I think a mobile device such as this would be the definitive platform for conducting this type of research in a school setting.

Here is the script of the stack's second card titled "Researcher Interface":

global gActor, gActivity, gTimeStamp, gDataString, varStudentZoneX

on addData
   if gDataString is empty then
      put the date&","&the time into gTimeStamp
      put the seconds into varTimeStart
      put gTimeStamp&","&varTimeStart into gDataString
   end if   

   put gDataString&","&gActivity into gDataString
   put gDataString into line 1 of field "datastream"
end addData

on openCard
   put item 1 of the location of graphic "dividing line" into varStudentZoneX
   put empty into gActor
   put empty into gActivity
   put empty into gDataString
   put empty into field "datastream"
end openCard


A variety of global variables are declared at the start. I then define a new procedure (or command) called "AddData." We'll come back to this procedure and how it works later in this post.


Then, some script is executed when the card opens. The first line notes the location of the graphic "dividing line" and puts its horizontal, or x, coordinate into the variable "varStudentZoneX." (Recall that "the location" of an object will result in two items indicating the horizontal and vertical screen coordinates of the object. Think of these as the x,y coordinates of the object (and remember that the 0,0 origin is in the upper left-hand corner of the screen). All we care about here is the x coordinate of the dividing line.

The other code in the openCard script simply puts "empty" into a bunch of variables, thus initializing them (i.e. getting them ready) for use in the card.

Button Properties and Script

Let's take a look at one of the buttons. As it turns out, each is identical with two critical exceptions -- the name and label of each button. Here is a snapshot of the properties of the button "Gives Direction":



An interesting feature of a button is that its name and label do not have to match. The name of the button is what appears to the user, but the label can be something entirely different. I chose to make both the name and label the same, but I wanted to show how to use this feature in this app. So, the label could be something much shorter, such as a special code such as "GD" for "Give Directions," or whatever you like.

Here is the script for this button (again, all the buttons share this exact same code):

global gActivity, gActor, varStudentZoneX

on mouseUp
   if item 1 of the location of me > varStudentZoneX then
      put "student" into gActor
   else
      put "teacher" into gActor
   end if
  
   put gActor&":"&the label of me into gActivity
   addData
end mouseUp


Notice the first block of code after "on MouseUp." We have a simple If-Then-Else structure that looks to see if item 1 of the location of "me" (that is, the x coordinator of this button) is greater than varStudentZoneX, which you will recall is the x coordinator of the red dividing line. If it is, then it must be a student-related button and "student" is put inside of the global variable gActor. If it is not, then it must be a teacher-related button and "teacher" is put inside that variable instead.

So, this is a cool way to have buttons on the screen self-determine if they are on the left or right of the screen, and hence are related to what the teacher or student is doing.

Let's look at the last two lines of the button script (just above the "end MouseUp"). Here's the first:

   put gActor&":"&the label of me into gActivity

This puts a concatenation of the following into the global variable "gActivity" (recall that the & symbol concatenates, or "adds together" a string of items):  gActor (either "student" or "teacher"), a colon, and the label of "me" (which again is the label of this particular button).

The last line executes the custom procedure "addData" defined above in the card script. So, let's take a closer look at that:

on addData
   if gDataString is empty then
      put the date&","&the time into gTimeStamp
      put the seconds into varTimeStart
      put gTimeStamp&","&varTimeStart into gDataString
   end if      

   put gDataString&","&gActivity into gDataString
   put gDataString into line 1 of field "datastream"
end addData


You'll notice a whole lot of concatenation going on in this, so look for those &s. It first looks to see if the variable "gDataString" is empty. If it is, this indicates a new event is about to be coded and it puts the date and time into gTimeStamp, each separated with a comma.

Next, it puts the seconds into varTimeStart. I've talked about "the seconds" in previous blog posts. It's basically the current time also, but given in seconds. This is particularly handy when one wants to compute how much time has passed, such as with a timer. (Revisit my blog posting on making of the game "Crack the Code" for a full explanation.) So, these various functions take note of the current time, down to the exact second, when the classroom event began.

The next lines concatenates gTimeStamp and varTimeStart (with a comma in-between) and puts it all into the global variable gDataString.

The next line concatenates gActivity -- the value of which came from the button just pressed -- with the contents of what was already in gDataString.

The contents of gDataString are then displayed in the field "datastream," which is the field located toward the bottom of the card window. So, as the researcher clicks on more buttons, the continually building stream of data will be shown on the screen.

Add a Button: Go Ahead and Try It

If you want to add a button to the list of events, just copy any of the buttons currently available on the card. Paste it anywhere you want, then just change the button's name and label. If you put the button to the left of the red dividing line, it will be counted as a teacher event. Put it on the right of the dividing line and it will be counted as a student event. Give it a try.

Yikes! I Clicked the Wrong Button

OK, this is good so far. The researcher is clicking away and seeing the data of events unfolding before his/her eyes. But, what happens if the researcher clicks the wrong button? Well, the button "Erase Last Data Point" acts as an undo button by removing the last data point from the data stream. 

Let's take a look at how this button works. Here the button's script:

global gActor, gActivity, gTimeStamp, gDataString

on mouseUp
   put empty into varTemp
   put line 1 of field "datastream" into varCurrentItems
   put the number of items in varCurrentItems into varItems
   put item 1 of varCurrentItems into varTemp
   repeat with i = 2 to varItems -1
      put varTemp&","&item i of varCurrentItems into varTemp
   end repeat
   put varTemp into gDataString
   put gDataString into line 1 of field "datastream"
end mouseUp


I'm not going to dissect this line by line, but basically it simply looks at how many items are in the current data stream, separates out each items, then reconstructs the data stream by stopping one short. The result is the same data stream, but minus the last item. The last line of code (just before the "end mouseUp" puts the reconstructed gDataString into line 1 of the field "datastream," thus replacing the line of data previously located there.

The button "Reset" simply empties out the current contents of "gDataString," the main variable holding the data, and removes anything that is in the field "datastream":

global gDataString

on mouseUp
   put empty into field "datastream"
   put empty into gDataString
end mouseUp


This of this as "Do Over" button.

Recording the Data

When the classroom event has ended, the researcher can click the "Record" button to record the data on the card "data." It also then resets everything for the next classroom event.

Let's take a look at the script in the "Record" button:

on mouseUp
   put line 1 of field "datastream" into line (the number of lines of field "data" on card "data" +1) of field "data" on card "data"
   opencard
end mouseUp


There are only two lines of code here. The first puts the data from the field "datastream" into the next open line in the field "data" on the card "data." It does this by first counting the number of lines currently being used on the field "data", then adds one to this number. (Another way to do this, which is simpler, is to use the command "after.")

Here's a partial screen shot of the data resting comfortably in the field "data" on the card "data":

 

The simple one word script "opencard" does just that, it reopens the current card which you will recall initializes everything.

It's important to note that the current strategy for collecting the data only works while running the app directly in LiveCode. Before you publish this app as a stand-alone application, you have to save the data from this field to a text file that would accompany the app. (I show how to do this on my LiveCode workshop site.)

Analyzing the Data

One thing that this app does not do is analyze the data. But, why bother scripting that out when we have such fantastic tools such as Excel and SPSS? All of the data recorded in the field "data" on the card "data" can be copied and pasted into either application. For example, here is an example of some data copied and pasted into Excel:



After copying and pasting the data into the first cell of a new Excel file, I used Excel's "Data > Text to Columns" feature to separate each item of data into its own column (using the comma between items as the delimiter).

Now that we have the data in Excel, we can add a column that figures the duration of each event. I added a new column - Column D - and inserted this formula in the first row:

=C2-C1



This subtracts the time the second classroom event started from the time the first classroom event started and puts the answer as the total time that the first event lasted. By using the "fill down" technique, I can quickly add this relative formula to all of the other events (one event per row).

I could do other quantitative analyses very easily, such as counting the number of event occurrences with the COUNTIF or COUNTA functions. Lindy is also interested in adding other tags besides "teacher" and "student" -- besides the "activity" and "tools" tags, her app has about six more. (Her specific items under each tag go far beyond what my version shows here.)

What About Qualitative Data?

Good question. Despite the apparent heavy quantitative elements to this app, recall that the app's main function is to code and record classroom events as they occur, a predominately qualitative activity. If the researcher is also recording the audio or video of the class (and simply synced the time that the class recording and app started, it would be very easy to then revisit the app's data stream to add the student and teacher voices to the narrative. It would also be easy to add a notepad to the app to let the researcher make short narrative notes while collecting the other data.

Using the Computer as a Partner in Research

Again, the whole purpose of this was to be creative in using the computer's processing abilities as a powerful tool to implement and collect data in an educational research project. I think this app shows an intriguing example of how to do this.

Saturday, September 28, 2013

Creating a Simple Game Spinner with LiveCode

Despite my lack of posts, I've been very active with LiveCode these past few months. I'm currently teaching two courses at the University of Georgia in which I've introduced LiveCode to our very talented graduated students. I'm always amazed and impressed with the interesting ideas that they come up with. So, I thought I would use some of their ideas in at least some of my next posts.

The first one is a simple example of a game spinner, based on an idea by Lucy Daigle, one of our Masters students in our Instructional Design and Development graduate program at UGA. I built this simple prototype for her.

Before I proceed, a reminder to everyone that the LiveCode Community version is now free. So you really have no excuse for not downloading your own copy of LiveCode. For that reason I am not providing executable versions for Macintosh and Windows. Instead, you can simply download and run the LiveCode file. Here's what the final version looks like:

This prototype took about 30 minutes to build, so yes, it is very simple. I started by building six triangles using LiveCode's polygon tool. When you add a polygon to your card, it defaults to a 12-sided polygon, but you have the option to change the number of sides to three in the Basic Properties window of the Property Inspector. It's also a good idea while you are in Basic Properties to make the triangle "opaque," if only because it will be much easier to grab hold of the object and move it around later. Plus, you will need all of the six triangles to eventually be opaque anyway in order to show colors.

Next, with the triangle object selected, you can quickly choose "Edit > Duplicate" twice.

You can then rearrange these three triangles so that they touch at a center point. (A good tip to remember is that when an object is selected, you can use the arrow keys to move an object pixel by pixel.)

Duplicate one of the triangles to make a fourth. Now, you will need to rotate this triangle 60 degrees. The rotation option is also found in Basic Properties of the Property Inspector for the object. After you rotate it, duplicate it twice. Then, move these three triangles into their positions around the center point formed by the first three triangles. This makes the a six-sided set of pie shapes reminiscent of the Trivial Pursuit game token.

Next, give each triangle a distinctive color. I chose yellow, green, red, orange, blue, and purple. You do this by selecting a triangle and going to the Colors & Patterns window in the Property Inspector. Choose the BackgroundColor block for Fill (the square block on the right). I always like choosing from the "Crayons" option. Now, you would think that you would find color names like yellow, green, etc., but you'll have to choose between options like banana and lemon.

Next, name each triangle object accordingly by clicking on each and entering "yellow," "green," etc. in the name field Basic Properties of the Property Inspector. These names will be important because they will be referenced in the upcoming scripts. Oh, I also created a seventh polygon - a six-sided polygon with a gray background - to act as a background (doing so is optional, but I liked having it for aesthetic reasons).

To create the illusion of spinning, all I do in the script is to show and hide the six triangles in a "strategic" order. So, I created a button named "Spin" on the card and added the following script:

global varSpinTimes, varNumber, varDelay

on mouseUp
  
   put 4 into varDelay
   put random (7)+3 into varSpinTimes
   put random (6) into varNumber
  
   hide graphic "yellow"
   hide graphic "green"
   hide graphic "red"
   hide graphic "orange"
   hide graphic "blue"
   hide graphic "purple"
  
   repeat varSpinTimes times
      show graphic "yellow"
      wait varDelay ticks
      hide graphic "yellow"
     
      show graphic "green"
      wait varDelay ticks
      hide graphic "green"
     
      show graphic "red"
      wait varDelay ticks
      hide graphic "red"
     
      show graphic "orange"
      wait varDelay ticks
      hide graphic "orange"
     
      show graphic "blue"
      wait varDelay ticks
      hide graphic "blue"
     
      show graphic "purple"
      wait varDelay ticks
      hide graphic "purple"
     
   end repeat
    
   if varNumber = 1 then show graphic "yellow"
   if varNumber = 2 then show graphic "green"
   if varNumber = 3 then show graphic "red"
   if varNumber = 4 then show graphic "orange"
   if varNumber = 5 then show graphic "blue"
   if varNumber = 6 then show graphic "purple"
  
end mouseUp

So, let's dissect this code a little bit to see how it works. The repeat loop is the heart of the code. As you can see, starting with yellow, it shows and then hides each triangle with a short delay in-between. Now, I wasn't sure how long the delay should be and so I knew I would need a lot of trial and error to get it just right. I didn't want to have to change the delay 6 times (once for each triangle), so instead I just created a variable called varDelay. That way, I could change the delay once in the first line after the mouseUp. (Recall that a tick is 1/60 of a second. I settled on 4 ticks.)

Notice that before the spinning starts, I hide all the triangles.

Now the question was how many times should the spinner spin? It seemed boring to me to always have it spin the same number of times. In all of the board games I ever played that involved spinners, it was part of the fun to spin it fast and slow. I thought it should spin at least 3 times, but no more than 10, hence this line:

   put random (7)+3 into varSpinTimes

Since "random(7)" returns a number between 1 and 7, by adding three to the result changes the range to 4 to 11 (hmm, perhaps I should change this line to only add 2?)

Anyhow, I put this random number into the variable "varSpinTimes," which, as you can see, I use to determine the number of times the repeat loop repeats.

Note also that near the top of the script is this line:

   put random (6) into varNumber

This actually chooses the final number that the spinner will reveal. It obviously will be a number somewhere between 1 and 6. 

After the spinning has concluded, all of the pieces will be hidden.

The final six lines of code determine which triangle will be revealed as the spinner's final result. So, if varNumber turns out to be 1, then the yellow triangle will be shown. If 2, then the green triangle, and so on.

Now, the interesting thing is that, unlike a real spinner that physically ends at the final number (or color), this spinner just spins a certain number of times and then jumps to the final triangle. So, that's a sneaky shortcut, but one that I don't think players will ever notice. Of course, you may disagree. But, I just don't think it's worth revising the code to make the spinner spin "pure."

I made another button on the card that simply shows all of the triangles. Finally, I put similar code on the card script so that when the card is initially opened, all of the triangles show.

I don't know if Lucy will use this actual spinner prototype, but it's now hers to use, adapt, or ignore. But, let's see how Lucy eventually uses the spinner idea in her project.


Monday, June 10, 2013

Statistics Teacher Helper Tool: Generating Random Numbers and Normal Distributions

If all goes well, I'll be teaching my first Massive Open Online Course (MOOC) at the end of the summer. My course title is "Statistics in Education for Mere Mortals." It will be managed and distributed by Instructure using Canvas, their learning management system (LMS).

I chose this topic based on the advice to choose a topic that I love to teach, but rarely have the opportunity to teach it as fully as I wish. I teach a little statistics in my research methods course for masters students. I've used an instructional strategy of having students learn the fundamentals of statistics by creating Excel spreadsheets from scratch. In my online course, I created some short video tutorials that the students follow. Then, I send them each a new data set and they have to email me the resulting statistic. I'm going to follow the same approach in my MOOC, but with a few twists to allow this to all be graded by the LMS.

Anyhow, the purpose of this blog posting is not to talk about my MOOC, but rather to share a little helper tool I created for it using LiveCode. I wanted to be able to create unique data sets. I started by creating a simple tool that generated random numbers. That was good, but what I really needed was a tool that would create a normally distributed data set. Now, that's a little trickier. In other words, when the frequencies of the numbers in the data set are graphed, it will take on the shape of the classic bell curve. How does program a set of normally distributed numbers?

Well, Sir Francis Galton invented a device called the Galton Machine (or the Galton Box, or the Bean Machine, or a list of many other names) that I'll bet many of you either know about or have actually seen. A common name for a game based on the Galton Machine is Pachinko. The idea is that you drop marbles, one by one, through a sieve of pins so that as the ball drops, it strikes a pin and either goes left or right with a probability of 50%. It then drops down into a column at the bottom. The more marbles you drop, the closer the resulting collection of marbles resembles a normal curve.

Click here to see a computer simulation (programmed with Java) of this device. Here's a screen snapshot of the simulation as it chugs along:


As the columns continue to grow, they will nicely match the shape of the bell curve.

So, I used this very concrete idea to program my own Galton Machine. Now, I didn't have time (or interest) in building an animated interface to show everyone what is happening. Instead, I just have the program build a column of numbers (left column) that result from having a number begin at a certain starting position, then increase or decrease by a certain amount. The chance of increasing or decreasing is 50/50. I have 25 such random events, corresponding to the number of rows of pins that the marbles fall through. But I easily could have had as many as I wanted. Here is the code:

      repeat 25 times
         //A Pachinko machine
         put random(2) into varNail
         if varNail=1 then put varNumber-random(varBounce) into varNumber
         if varNail=2 then put varNumber+random(varBounce) into varNumber
      end repeat
      put varNumber into line (the number of lines in field "fldRandomList" +1) of field
        "fldRandomList"

varNumber is the starting point on the x-axis where the marbled is dropped.
varBounce is a variable I created to determine the "bounciness" of the marble. A value of 1 is like a regular marble, and 5 would be like hard rubber.

The variable varNail is just a random number from 1 to 2. When 1 comes up, the marble bounces left (i.e. the bounce number is subtracted from varNumber). Conversely, when 2 comes up, the marble bounces right (i.e. the bounce number is added to varNumber).

The result is put into the next open line (i.e. the number of lines in field "fldRandomList" +1) in the field "fldRandomList."

(The code for all of this is in the button "Generate Distribution.")

I give the user control over how many marbles are dropped, the starting point of the drop, and the bounciness of the marbles.

Now, this would have been good enough, but I wanted to have a little more functionality. I partner with Excel again here to take advantage of Excel's graphing capabilities. I found a cool technique to graph a set of numbers in Excel, created by folks at Santa Clara University. However, it's a little clunky to have to first have Excel create the frequencies of the numbers. So, I added another column (right column) to do just that. (I had a devil of a time figuring this out -- it seemed so simple when I first conceived of doing it, but it took me over an hour to get it right.)

OK, here is a screen shot:

I also added some simply functionality, like automatically copying the frequencies in the right column to the clipboard for easy pasting into Excel. Here's the code for that:

   copy char 1 to -1 of field "fldFrequencies"

(I really don't understand the "1 to -1" part. But hey, it works!)

I also programmed it to compute the mean and the range. (I almost programmed it to compute the standard deviation, but my heart wasn't in it. Plus, it is so easy to compute this with Excel.)

I thought it might also be useful to also sort the list of raw numbers (left column). It turns out this is a snap to do in LiveCode -- just one line (in the button "Sort"):

      sort lines of field "fldRandomList" ascending numeric

I even added a little explanation -- just click on the question "What's the Galton Machine?" The program is not as fast as I would like, so be careful if you want very large data sets. The largest I tried were 500 numbers (i.e. marbles), which took well over a minute to crunch. But, I think most data set would be in the 30-50 range of total numbers.

If you teach statistics, you'll "probably" find this to be a useful tool.



Wednesday, May 29, 2013

Lloyd's (Hypothetical) Math Fair Contest Entry

You can tell from the title that I didn't actually submit this idea to any math fair. But, I think imagining a math and science fair is the right mindset for this blog posting. I really like the idea of giving the power of computer programming to students to explore topics that would be most suitable as entries into a math and science fair. Remember that there is a free edition of LiveCode available, so I hope many teachers will choose to begin offering this powerful computer programming environment to their students. Consider this blog posting as an example of what a student might do with such a powerful analytic and computational tool.

Like most people, I often dream of prime numbers. Actually, no, I'm not that geeky. However, I have been reading and viewing lots of stuff related to calculus lately. I do this from time to time. I get back into the most challenging mathematics I have ever tried to see if I can understand again some of the stuff I supposedly once learned, plus make some new personal advances. (Despite my love of mathematics, I'm honestly not that good at it.)

Anyhow, a couple of weeks ago, on a Saturday morning, I somehow got to thinking about prime numbers and how easy it would be to write a little computer program to identify a list of prime numbers. After all, all one would have to do is start with the number 2, then begin listing all of the natural (i.e. plain 'ol counting) numbers one by one, dividing each of them into the given number and see if there is any remainder. If any of the numbers divide into it evenly (without any remainder), then voila, it's a prime number! I'll just let the computer do all of the nasty, tedious, number crunching. I wondered just how big a prime number I could find (or in my personal case, "discover"). In doing a quick Internet search, it was cool to find that there is a name for this time-honored approach to divining prime numbers, the trial division approach (though it fell out of favor centuries ago - hey, better late than never). I also was quite thrilled to learn a neat trick to greatly reducing the numbers needed to run the program in order to determine if a number if prime or composite -- stop when you get to the number that is or larger than the square root of the number in question. Of course, this makes total sense if you think about it. There can obviously be no factors to consider that are greater than the square root of the number because you would have found already the other factor that multiplies with it to obtain the number. So, for a number like 100, the largest number you would have to check is 10. For a much bigger number, say one million, 1000 is the largest number that needs to be checked as a factor. No need to try out the remaining 999,000 numbers!

So, I quickly wrote such a program and the fruits of my labor are contained in this blog posting. As promised, I also integrated the graphing techniques I've been describing in recent posts by including the option of drawing a running graph of the total number of prime and composite numbers found. I'm not going to provide the level of explanation to my code as I have previously, but I'm very happy to answer questions about it if you look at the code and can't figure it out.


Here is a snapshot of the program:


How It Works


First, you enter a number as the upper limit of the range that will be searched for prime numbers. Then, you click the "Start Search" button to have the program begin its number crunching to search for prime numbers beginning with 2 and ending with the number you entered. You can also choose to graph totals of prime and composite numbers found so far, though the program runs a lot more slowly if you choose this option.

In the "Status Window" you will see the current number being analyzed with the factors being considered on the next line. Of course, the numbers are buzzing by very fast at the beginning, though things do slow down after awhile.

As prime numbers are found, they are listed in the first column. That column is actually the heart of the program, because all factors of other numbers can be reduced to two or more prime numbers. So, when a number is being analyzed (let's call it the target), each of the prime numbers found so far are divided into the target. If there is no remainder, then it is not a prime number and it is added to the middle column as a composite number, and the next natural number becomes the target.

As the program runs, you are given a running total of prime and composite numbers found so far.

The script that makes all this work can be found in the "Start Search" button. Basically, there are two Repeat loops, one nested in the other.

The first Repeat loop increments the variable "varNumber" which serves as the target number. Within that is another Repeat loop that is dividing all of the prime numbers found so far into it. Let's dissect this second Repeat loop. Here's the code:

      put true into varPrimeFlag
      put the number of lines in field "prime numbers" into varL
      repeat with i = 1 to varL-1
         put line i of field "prime numbers" into line 3 of field "feedback"
         put varNumber/(line i of field "prime numbers") into varAnswer
         put varNumber&", "&(line i of field "prime numbers")&", "&varAnswer&", r="&varAnswer-
            round(varAnswer) into line (number of lines in field "answer")+1 of field "answer"
         if varAnswer-round(varAnswer) = 0 then
            put varNumber&", "&(line i of field "prime numbers") into line (number of lines in field
              "composite numbers")+1 of field "composite numbers"
            put false into varPrimeFlag
            exit repeat
         else
         end if
         if line i of field "prime numbers" > sqrt(varNumber) then exit repeat
      end repeat
      wait 1 tick
      if varPrimeFlag is true then
         put varNumber into line (number of lines in field "prime numbers")+1 of field "prime numbers"
      end if

I really like the scripting technique of using LiveCode's list processing capabilities in tandem with text fields. It's a snap to continually add to any text field just by a script that counts up how many lines are being used, then saying to put the next data item to the +1 line. Similarly, it's a snap to take and use each line of data just by building a repeat loop that starts at line 1 and repeats for the number of lines in the field.

I use the variable "varPrimeFlag" to flag whether or not the target number is prime. As you can see, before the loop begins, I assume the number will be prime by setting it to true; if the loop finds it to be a composite number, I set varPrimeFlag to false. So, if nothing triggers this, the repeat ends with varPrimeFlag being true, and the number is added to the list of primes.

Lines 5 and 8 are key to determining if a number is composite:

put varNumber/(line i of field "prime numbers") into varAnswer
...
if varAnswer-round(varAnswer) = 0 then 

Line 5 divides the target number by each and every prime number found so far. Line 8 takes the answer and subtracts from it the answer rounded off to the nearest whole number. If the answer is 0 this means that there was no remainder to the original division problem. Any number that divides into a second number, with no remainder, is a factor of the second number.

This line checks to see if the next prime number to be used to check out the target is larger than the square of the target number:

if line i of field "prime numbers" > sqrt(varNumber) then exit repeat

If it is, the repeat loop ends.

Also, you will find a procedure within the script for this button titled "graphPrime" that handles the graphing tasks.

Lloyd's Findings


Next, here are some of my "findings."

First, I programmed the time it took the program to compute all of the primes up to a given range. I think this is an interesting data point in and of itself. Here are three examples:
 
  • Analyzing 1000 numbers takes 1:01 minutes
  • Analyzing 2000 numbers takes 3:01 minutes
  • Analyzing 5000 numbers takes 16:56 minutes 
So, if I were to devise alternate ways of crunching the numbers, I could use these data to compare the efficiency of the strategies. But, that wasn't a question I was interested in at the moment.

Next, here are two other interesting facts:
  • 4999 is the largest prime number I've found so far.
  • In the set of numbers 2-5000, there are 669 prime numbers (and 4330 composite numbers)
Don't worry, at some point I'll run the program for much longer periods of time. I'm curious as to how long it could run before crashing -- I don't know of any limit to the number of lines in a text field, but I'm sure at some point I'll exceed some memory cache. (It will be rather cool to keep posted somewhere the "largest prime I found so far.")

Next, I became interested in the factors of composite numbers. The program is not looking for all the factors, but merely asking the question of whether the number being analyzed is a prime or composite number. So, as soon as a factor is found, the number is deemed composite and the program moves on to the next number. So, obviously, every even number is quickly determined not to be a prime number because the factor 2 reveals this fact. So, I thought it be at least interesting to include in the output the first (i.e. smallest) factor found. So, the number 2 is obviously common with it being the first factor of half of the numbers. But, I was intrigued to find what would be the largest first factor.

My program revealed that in the data set 2-5000, 67 is largest first factor, and it is found in the following composite numbers: 4489, 4757, 4891.

I found this answer by copying and pasting all of the data in column two into Excel. I decided it would be good to partner LiveCode with Excel for this project. After all, why take the time to program functionality into my computer program when it already exists in Excel. There is a wonderful option in Excel called "Text to Columns..." under the "Data" file menu choice that lets you parse a list of numbers in each cell of a column into separate columns. So, the number pairs...

4,2
6,2
8,2

...can easily be parsed into two separate columns in excel using the comma as the delimiter.

Next, I found the results of the graph quite amazing. For example, consider your own hypotheses about the distribution of prime and composite numbers. Are they evenly spread out or they do they appear randomly, or in clusters? Do they occur is equal numbers, or in some proportion. Are the patterns consistent, or do the patterns stop and start up again? To be honest, I didn't know what to expect and I forced myself not to do any google searches because I wanted to make the discovery myself!

The running graph shows that the rate of which composite and prime numbers are distributed looks to be a linear function for each. The fact that the slopes are so straight and smooth is fascinating. I wasn't sure if I would find some "jaggy" lines showing gaps in the number of primes. However, there does seem to be a slight bend in the prime number graph, where the rate seems to be falling off slightly. So, I programmed the file to keep track of the ratio of the totals of prime and composite number found at any given point, and these are found in the third data column (each line contains a pair of numbers: the ratio and the point in the list). When I copy and paste these into Excel and plot them in a graph, here is what you find:


This graph clearly shows that the ratio gets smaller and smaller, meaning that as the more numbers you analyze, the fewer prime numbers there are, relative to the total. It seems to resemble an exponential-logarithmic distribution. (Rather impressive of me to notice this, don't you think?) Actually, in doing some research (aka I looked it up on Google), the distribution of prime numbers is known as the Prime Number Theorem. According to the Wikipedia entry, "Informally speaking, the prime number theorem states that if a random integer is selected in the range of zero to some large integer N, the probability that the selected integer is prime is about 1 / ln(N), where ln(N) is the natural logarithm of N." I guess that's about as informally as most mathematicians speak.

This also means that it is an asymptotic distribution, which is actually something I do understand. An asymptote is a number representing a limit. So, the ratio of prime to composite numbers will approach, but never reach, this number. (Interestingly, I did a google search for this number, but did not find it. Hmm, this makes me "curiouser."

After analyzing 5000 numbers, the ratio is .15, in other words, there are about 6.7 times as many composite numbers as prime numbers between the range of 2-5000, the number continues to decline. Amazing!

My little program, of course, is not of interest to a mathematician as it reveals nothing not previously know. Indeed, although the largest prime I have found is 4999, a number with 4 digits, the largest prime number yet discovered has 17,425,170 digits! (Read this article to learn about the largest prime number yet found.) However, contributing to the mathematics literature was not the point of this exercise. In was intended to be my own wonderful journey of personal discovery. But, you never know from where the next major mathematical discover will arise. I just read an article on Slate.com  titled "The Beauty of Bounded Gaps" of an "ordinary" mathematician -- Yitang “Tom” Zhang, a  math professor at the University of New Hampshire -- making a monumental discovery about prime numbers.

Here's a quote from the Slate article I found  interesting:

"The primes are the atoms of number theory, the basic indivisible entities of which all numbers are made."

Boy, this really makes me feel that my humble investigation into prime numbers is important stuff. But, I had a truly emotional reaction to next quote from the article:

"This means, in particular, that prime numbers get less and less common as the numbers get bigger, though the decrease is very slow; a random number with 20 digits is half as likely to be prime as a random number with 10 digits."

I got emotional because I feel like I'm the one who discovered this! That's what math and science projects do to people.

There are so many other questions that I want to explore if I had time. The Slate.com article actually gives me some ideas for how to look for gaps in prime numbers. An obvious next question -- obvious just by thinking about it, and especially by watching the program run -- is that it takes more and more time to analyze a number as the number increases. But, does the time increase in a linear or exponential fashion? Perhaps I'll analyze these data and report on what I find in a future blog posting.

Perhaps you have a question you'd like me to consider exploring. If so, send it on! Better yet, use my program to explore your own prime number questions. And better still, build your own program to explore your questions, feeling free to use any of my code as a starting point.

So, those of you who teach mathematics, especially with middle and high school students, please consider integrating LiveCode into your teaching and challenge your students to think like a mathematician to discover mathematical principles and ideas on their own. Who cares if the answers are already found in some book or some Google search. A personal discovery based on questions you find meaningful makes it a real discovery.


Saturday, May 18, 2013

Graphing with LiveCode, Part 3: Graphing Parabolas and the Sine & Cosine Functions

It's time to turn our attention to graphing functions that will result in curves. I think this is where mathematics truly becomes beautiful for me. We will graph three of the most common mathematical functions that result in classic curved shapes: parabolas, the sine curve, and the cosine curve. (The sine and cosine curves look exactly the same, except that they cross the x-axis at different places.)

As promised, here is a copy of the LiveCode stack with all of the scripts from this and the previous two blog postings, along with Mac and Windows executable versions of each (for those of you who have not yet downloaded LiveCode, which would be strange given that it is now free):


Note: I just learned that when saving a stack that uses the brush tool as a stand-alone application, you have to go to the standalone settings, and under "General" choose to "select inclusions for the standalone application," then check "brushes."

Graphing Parabolas


Let's start with the parabola, which is derived from the function: y=x2
(This is read as "y equals x squared.")

Here are x, y pairs of numbers for this function:
-3,9
-2,4
-1,1
0,0
1,1
2,4
3,9

Here is a graph of a parabola linked from purplemath.com:


I just love how the curve mirrors itself around the y-axis due to the fact that a negative times a negative gives a positive.

Graphing a Parabola in LiveCode

Let's turn now to doing this in LiveCode. In the previous blog post, we ended with the script for a button that graphed a line of the following function:

y=3x+50

Here's the entire script of that button:

on mouseUp
   choose brush tool
   set the brush to 32
   set the brushColor to orange -- could use an RGB triplet here
   set the dragSpeed to 0
   //Set the starting point for the x and y variables:
   put 0 into var_x
   put 0 into var_y
   put -1000 into var_xprev
   put -1000 into var_yprev
  
   repeat 250 times
      //add one to var_x:
      put var_x+1 into var_x
      //This is the function "y=3x+50" scripted in LiveCode:
      put -(3*var_x+50) into var_y
      drag from var_xprev, var_yprev to var_x+250, var_y+250
      put var_x+250 into var_xprev
      put var_y+250 into var_yprev     
   end repeat
   choose browse tool
end mouseUp

Line 5 of the repeat script is the LiveCode version of that function:

      put -(3*var_x+50) into var_y

Recall that in order to "flip" the y-axis of LiveCode to match that of a traditional Cartesian coordinate system, we took the negative of the function, hence the negative sign near the beginning of the line. Also recall that we wanted the origin of the graph -- 0, 0 in a Cartesian system -- to be at the center of our 500 by 500 pixel large card. So, lines 6-8 add 250 to varx and vary to accomplish this. We repeated the loop 250 times because that seemed about right. (Don't you just love my precise way of describing things.)

Now we need to rewrite line 5 to graph not the function of the line "y=3x+50," but instead the function of the parabola "y=x2." A digital piece of cake.

      put -(var_x^2) into var_y

Here is what the resulting graph looks like:

OK, it kinda looks like the right half of the parabola shown above at purplemath.com. But it's rather tall and skinny. Let's analyze this a bit. Keep in mind that squaring a positive number makes it much bigger. (Yes, a profound statement, I know.) As x gets bigger by 1, y grows exponentially. So, it doesn't take long for the value of y to be so big that it is "off the charts," so to speak. Indeed, in the above example, since 250 is the upper limit of y, we can calculate the biggest value of x that will actually appear on our graph just by taking the square root of 250, which is approximately 15.8. So, obviously, it does not good to repeat the loop 250 times; 15 or so will get us the same results. So, change the script to repeat 15 or 16 times to see what I mean.

To flatten the curve out, we just need to divide y by some amount. In playing around, 50 worked out pretty well. There are a couple of ways to do this, but I think the simplest is just to change line 5 in the repeat loop to the following:

      put -(var_x^2)/50 into var_y

When you run the script now, you only get a small arc graphed because we need to repeat the loop many more times. So, since we are dividing y by 50, we can calculate the largest x value by taking the square root of 250*50, which is not quite 112. So, let's repeat this loop 112 times and see what we now get:

Very nice parabolic shape, if I say so myself.

Anyhow, play around with dividing y by 60 or 40 or 106, whatever, to see how to make the curve flatter or skinnier.

To get the full parabola (left and right halves), we just need to set the first value of x to the negative of the number of loops we needed to attain the perfect right half, then of course we need to double the number of times we repeat the loop.

Line 7 of the mouseUp script becomes:

   put -112 into var_x

We will repeat the loop 224 times to get the following:
Here is the final script of my button "Graph y=x^2":

on mouseUp
   choose brush tool
   set the brush to 32
   set the brushColor to brown -- could use an RGB triplet here
   set the dragSpeed to 0
   //Set the starting point for the x and y variables:
   put -112 into var_x
   put 0 into var_y
   put -1000 into var_xprev
   put -1000 into var_yprev
  
   repeat 224 times
      //This is the function "y=x squared" scripted in LiveCode:
      put -(var_x^2)/50 into var_y
      //put var_x&", "&var_y into line 1 of field "data"
      //put var_x+250&", "&var_y+250 into line 1 of field "data"
      drag from var_xprev, var_yprev to var_x+250, var_y+250
      //wait until the mouseclick
      put var_x+250 into var_xprev
      put var_y+250 into var_yprev     
      //add one to var_x:
      put var_x+1 into var_x
   end repeat
   choose browse tool
end mouseUp

You'll notice some variations to what we used above. First, notice that I moved the code to increase x by 1 to the bottom of the repeat loop. That just seemed a better place.

Second, notice lines 4-5 and line 7 in the repeat loop. They are set as comment lines now, so they do not get executed. But, they will if you delete the "//" in front of each.  What would these do? Well, I added them to help me understand and troubleshoot the code. I do this all the time. After I perfect a script, I usually delete them, but I thought you might like to see them so as to learn a good programming trick to help you figure out what's going on in your script as you work on it. Line 7 - "wait until the mouseclick" - is a simple way to have the repeat loop pause at each loop and wait for you to click the mouse button. This lets you look at what is happening at each loop. Lines 4 and 5 put var_x and var_y into a (now hidden) card field titled "data." (This field can easily be made visible in the Application Browser tool.)

Let's move on graphing two more interesting curves -- the sine and cosine functions.

Graphing the Curves Derived from the Sine and Cosine Functions


OK, let's explore the sine and cosine curves. First, here is a graph of the sine curve linked from purplemath.com:



It's important to understand how this curved shape is generated. For most people, the shapes, though familiar, are quite mysterious, even though everyone certainly graphed them at some point in high school. The explanation of these curves is rather simple and is derived by considering a circle.

Here is a figure of the circle linked from the purplemath.com web site that will help explain things for us:



As you can see, the radius of this circle is conveniently set to 1. Any radius I draw will obviously intersect the circle at one and only one point. Now, take a moment and look at the figure above and estimate the angle that is formed at left point of the triangle formed by the line segments r and x. It looks to be about 30 degrees to me. Well, the sine of that angle is y and the cosine of that angle is x. That's it. It's no more complicated than that. So, the sine of an angle of 0 degrees would 0 and the cosine would be 1. The sine of an angle of 90 degrees would be 1 and cosine would be 0. What about 45 degrees? Although one might be first tempted to say both sine and cosine are .5 (half of 1), the answer is actually about .707. One way to reason this out is with the all-powerful Pythagorean Theorem, which for right triangles is a2 = b2 + c2 and is read as "a squared equals b squared plus c squared." b squared and c squared both equal .5, and the square root of .5 is .707 (when rounded to 3 decimal places).

Click here to try out a very nice interactive animation of the sine curve at intmath.com -- it nicely shows how the sine curve is formed just by tracing out the value of y.

Graphing the Sine and Cosine Functions in LiveCode

An important fact to remember throughout the next few minutes is that the maximum value of sine or cosine is 1 and the minimum value is 0. So, this is not a very large range for graphing purposes. So, obviously, we are going to need to multiply some value in our code to make the curve more readily apparent. So, tuck that thought away as we proceed to program these curves in LiveCode. We'll start with the sine curve.

Fortunately, all need to do is revise a single line of code in the script for the parabola. And I'll bet you've already figured out which line of code I mean - line 3 of the final script shown above:

      put -(var_x^2)/50 into var_y

LiveCode already includes the sine and cosine functions. These are "sin" and "cos," respectively. (Refer to the LiveCode dictionary to learn more about them.) To change from graphing a parabola to graphing the sine curve, it seems it should be as easy as change it first to the following:

      put -(sin(var_x)) into var_y

But, not so fast. By default, LiveCode using radians instead of degrees to express an angle. (Rather than take the time to explain it here, I suggest you do a google search on the difference between radians and degrees. In short, radians are based on pi, not degrees, where 2*pi equals 360 degrees. Remember the formula for the circumference of a circle? "A=2*pi*r") We could stick with radians, but this would take some more programming gymnastics to get everything to work. So, we'll just convert radians to degrees by multiplying the value by (pi/180):

      put -(sin(var_x * pi/180)) into var_y

Finally, we need to multiply everything by some number so the result will be bigger than 1. I think multiplying by 100 will do the trick. So, here is the final script:

      put -(sin(var_x * pi/180))*100 into var_y

Let's start the graph as far left of the card as we can. Since the card is 500 pixels wide and high, let's set var_x to about -240. We'll repeat everything twice this amount, or 480 times. Here's what we get:

Let's duplicate this button and title it "Graph cosine function." We change the line of code above to the following:

      put -(cos(var_x * pi/180))*100 into var_y

I changed the brush color to blue. Here is the resulting graph:

Poetry in motion.

So what?


I admit that the past three blog posts have been rather theoretical in nature. Being able to graph simple lines, parabolas, and the sine and cosine functions may impress our high school algebra teachers, but what good is all this for LiveCode development? In my next posting, I'll show an example that puts this graphing script to use in a blog posting that I'm thinking of calling "Lloyd's Math Fair Contest Entry."

A Mystery Remains


Although I'm tempted not to tell you this, there is one last mystery that, frankly, I've not been able to solve. Remember way back when (in my first blog posting in this graphing series) that we had to choose our first point "way out to the left" in order to avoid that awkward first line segment -- we chose -1000, -1000. Well, I'm actually wondering why we are not seeing the first line segment connecting -1000,-1000 with the first point of the parabola, sine curve, or cosine curve. I've played around a little bit with this. If you change this first point where the coordinates are both positive numbers - say 5, 5 - then that pesky line segment appears. So, it has something to do with the fact that the initial point is not shown on the visible area of the card. I hope anyone who knows more about this will help explain this for us.