All Corona programs start in the file main.lua

Introduction to Programming with Lua and the Corona Game Lab
v 1.0 © Robert P. Cook
June 1, 2015
dedicated to Kristina, Kimberley:Alex:Molly:Luke, Thomas:Joseph, Amanda:Elliott, Nicholas, Jana, Peter
Disclaimer of Liability: The author makes no warranty, express or implied, including the warranties of merchantability and fitness for a particular purpose, or assumes any legal
liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe
privately owned rights.
Foreword: The chapters herein have been used as class notes for many years. I may add a web site with on-line tests and copies of the programs if there is interest. Please send
any corrections or suggestions to [email protected]. You can also request a free copy of the zip file of the example programs. There are no registration fees or spam; I
just send you the code.
Thank you for your purchase. The code in the book and the sample code that I will send you can be reused under the M.I.T. license.
QUICK ACCESS
CHAPTER ONE --BASIC COMPUTER CONCEPTS
CHAPTER TWO -- CONSTANTS AND EXPRESSIONS
CHAPTER THREE -- STATEMENTS
CHAPTER FOUR -- PROCEDURES
CHAPTER FIVE -- TABLES
CHAPTER SIX -- STRINGS
CHAPTER SEVEN -- DEBUGGING
ASCII TABLES
BINARY, OCTAL, HEXADECIMAL
PROGRAMS
TABLE OF CONTENTS
CHAPTER ONE --BASIC COMPUTER CONCEPTS
Introduction
CHAPTER TWO -- CONSTANTS AND EXPRESSIONS
Introduction
Constants
Line Syntax
Expressions
Math Functions
CHAPTER THREE -- STATEMENTS
Introduction
Variables
Declarations
Changing a variable
Conditional Statement
Boolean expressions
Short-circuit evaluation
IF statement
While, Repeat and Do Statements
Break Statement
For Statements
CHAPTER FOUR -- PROCEDURES
Introduction
Parameters and Arguments
Simple Image Animation
Procedure Parameters
CHAPTER FIVE -- TABLES
Introduction
Particles
Array Parameters
Sprites
Tables
CHAPTER SIX -- STRINGS
Introduction
String Input and Output
CHAPTER SEVEN -- DEBUGGING
Introduction
ASCII TABLES
BINARY, OCTAL, HEXADECIMAL
PROGRAMS
aaFirstExample
abFillStroke
acShapes
adConstants
aeExpressions
ahInput
alfruit
anParticles
aoCat
apCatBackground
aqAlert
arInput
asParticle
bbBouncy
ztest
CHAPTER ONE
BASIC COMPUTER CONCEPTS
Introduction
The author has written over half a million lines of code in forty-five years of programming. Hopefully, some of that experience can be communicated to ease the reader's path to
productive programming. The text assumes that you are familiar enough with a computer to edit a file and to install the Corona Game SDK. Other than that, concepts are
explained from the ground up.
This book teaches how to understand Lua (from the Portuguese for "moon") programs and places the reader on the path to becoming a Lua programmer. Lua was created in
1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group (Tecgraf) at the Pontifical Catholic
University of Rio de Janeiro, in Brazil.
Why learn Lua? Lua is a cross-platform, lightweight, extensible scripting language. The term "cross-platform" means that a program written for Windows or Mac OS/X or
Android or Apple iOS can be run on the other platforms without rewriting. The term "scripting" indicates that programs are not converted from text to binary before execution.
A text representation facilitates "cross-platform" execution because binary programs, such as Windows .exe, cannot be run on different platforms without a re-translation from
text. An additional, nice property of a scripting language is "immediate execution", like a calculator app. For example, if you enter the text "3+4" in Lua, the system outputs
"7". The extensible aspect of Lua allows the language to add new language features to itself. Lua is also designed so that it is trivial to "wrap" different APIs (Application
Programming Interfaces) to expose graphics, math, astronomy, database, game console, LEGO NXT interfaces to the programmer.
The second reason to learn Lua is that it is widely used as a scripting language by game programmers, perhaps owing to its extensibility, its fast execution, and its short learning
curve. A poll conducted by GameDev.net showed Lua as the most popular scripting language for game programming.
The Corona Labs Game development environment (coronalabs.com) uses Lua as the programming language. The examples in the book run in the Corona IDE (Integrated
Development Environment) to make learning programming as fun and interesting as possible. Whether the reader is building games, e-books, utilities or business applications,
Corona is the leading mobile development framework. More than 200,000 developers world-wide use Corona to develop mobile applications for Android, iOS, Kindle, Nook,
GameStck, Ouya devices and others.
Click on the OPEN button, then navigate to LuaExamples on the Desktop, and select the directory aaFirstExample. The directory names are prefixed with aa to zz so that the
alphabetical order matches the appearance of examples in the book. The next Figure shows the Corona application menu and an Android mobile device window with a white
rectangle. One of the cool features of Corona is that your Lua programs can also run on mobile devices. Completing that last step to run the examples on a mobile device is not
covered in this book; however, once you finish this book, you should be able to follow online directions to target a mobile device.
Hooray !! YOUR FIRST PROGRAM (aaFirstExample)
1. Select the menu option File/OpenProjectInEditor. The program consists of exactly one line. It is not an accident that a 100x100 rectangle was drawn on the Android
screen. Click on the Android window. Select the menu option File/Relaunch. The Lua program will be re-executed. All Corona programs start in the file main.lua; if
that file is missing, an error message is displayed.
display.newRect(140,140, 100, 100)
2. Click on the editor window and change 100, 100 to 200, 200, then select File/Save, then click on the Android window, then select File/Relaunch. A larger 200x200
rectangle is drawn.
display.newRect(140,140, 200, 200)
3. You may not remember from school but languages are defined by syntax rules. For example, the sentence "bone the a dog ate" has valid English words but bad syntax.
Change the original example to omit the , between the two 100s. Click "Edit Code" to dismiss the error dialog.
CODING: Language translators are notorious for giving poor error messages for syntax errors. Keep a Lua Quick Reference Sheet (easily googled) handy when
programming.
CODING: Copy code that already works (giving credit where appropriate) and then modify it for your purpose.
CODING: Make only small changes. If a syntax error occurs, the last change was the culprit.
4. Programming is very detail oriented, just like playing a piano. You would not expect to master the piano in a short time. Further, your teacher (and maybe parents too)
would say "practice, practice, practice!". Programming requires the same mental discipline.
5. The syntax of the sample program includes the names "display" and "newRect", the period ( . ) between "display and newRect", and the comma-separated list (140,140,
100, 100).
SYNTAX: A name (or identifier) can be any string of letters, digits and underscores not starting with a digit and not a reserved word. Identifiers starting with underscore
and an uppercase letter are reserved for use by the system.
RESERVED WORDS: and break do else elseif end false for function if in local nil not or repeat return then true until while
SYNTAX: The period denotes that "newRect" is an action verb that affects "display".
SYNTAX: Lua is case-sensitive. "newRect" is not the same as "newrect"; "display" is not the same as "DISPLAY".
SYNTAX: A comma-separated list must start with ( and end with ) and the items must be comma-separated.
6. Attention to syntax details is important, but is not the most difficult task. Syntax errors frustrate beginning programmers, but the good news is that the frustration
eventually wanes to mere annoyance. The continuing problem for all programmers is semantics, which is the meaning of code. Documentation and sample code are the
keys to understanding the result of a method, such as newRect. The documentation for the Corona APIs can be found at http://docs.coronalabs.com/api/index.html. Click
on "display.*" then "newRect".
7. The semantics of graphic systems varies widely from one vendor to another. Corona has one API for all platforms. You probably already guessed that "new" Rect" is an
action that creates a new rectangle object at an xy coordinate and with a given pixel width and height. However, Corona and some others do not use the familiar Cartesian
coordinate system. Instead, the display's xy origin (0,0) is at the upper-lefthand corner of the screen. Further, the positive Y axis is pointed down the screen, not upwards.
Change the program to (0,0). The rectangle is only partially displayed. The reason is that the operating system's window control uses part of the available space. As a
rule, applications should only use an inner rectangle that is referred to as the client area.
The single line in the Lua program can be replicated many times to create different size rectangles at different locations. Try it. Draw two overlapping rectangles. Which
one is drawn first?
CODING: When presented with a new API or even a new method, write a simple Lua program to make sure that you understand each method before ever using one of
the methods in a larger program.
8. In Corona, new graphic objects, such as rectangles, are created then their properties can be modified. Objects are drawn when the program stops executing. Rectangles,
and other shapes, have properties such as the interior fill color, the boundary color (referred to as stroke), and a stroke width (in pixels). The first two properties can be set
with the setFillColor and setStrokeColor methods. In order to use these new features, every rectangle must be given a unique name using an assignment statement
(described in Chapter Two). The stroke-width property can also be set directly using an assignment statement. Open project abFillStroke.
x = display.newRect(140, 140, 100, 100)
x:setFillColor(1, 1, 1) --white
x:setStrokeColor(0, 1, 0) --green
x.strokeWidth = 4
SYNTAX: Corona requires RGB values to be between 0.0 and 1.0, not 0 to 255. The reason is that real numbers are infinitely expandable. The 0-to-1 convention can
adapt to colors represented by more than 32 bits. Decimal fractions can be expressed as 0.0123456789, such as 0.1, 0.74, 0.023.
9. The documentation for the methods is listed next. Experiment with all the color options, including gray scale. Try drawing overlapping rectangles in which the top
rectangles have different transparency values.
10. How well did you learn? The fill and stroke properties apply to all shapes, and rectangles are not the only shape. Study the Corona documentation and use what you have
learned to experiment with the following methods. Start with the acShapes code.
display.newCircle( xCenter, yCenter, radius )
myCircle = display.newCircle( 100, 100, 30 )
myCircle:setFillColor( 0.5 )
myCircle.strokeWidth = 5
myCircle:setStrokeColor( 1, 0, 0 )
display.newLine( x1, y1, x2, y2 )
star = display.newLine( 200, 90, 227, 165 )
star:setStrokeColor( 1, 0, 0, 1 )
star.strokeWidth = 8
display.newPolygon( xCenter, yCenter, vertices )
o = display.newPolygon( 250, 250,
{ 0,-110, 27,-35, 105,-35, 43,16, 65,90, 0,45, -65,90, -43,15, -105,-35, -27,-35, } )
o.strokeWidth = 10
o:setStrokeColor( 0, 1, 1 )
display.newRoundedRect( xCenter, yCenter, width, height, cornerRadius )
myRoundedRect = display.newRoundedRect( 200, 200, 150, 50, 12 )
myRoundedRect.strokeWidth = 3
myRoundedRect:setFillColor( 0.9 )
myRoundedRect:setStrokeColor( 0, 1, 0 )
display.newText( text, xCenter, yCenter, font, fontSize )
myText = display.newText( "Hello World!", 200, 200, native.systemFont, 16 )
myText:setFillColor( 0, 0, 1 )
CHAPTER TWO
CONSTANTS AND EXPRESSIONS
Introduction
You might think that you understand constants, like 3 or 4, but wait until you see the syntax and semantics! Once you can program with constants correctly, we move on to
expressions, such as 3+4, then to formulas, and then to tables. Yes, there is a lot of syntax and semantics in those concepts too.
Constants
A constant is a value that never changes. Lua supports integer (whole number) constants, real numbers (decimal fractions), Boolean (true or false) values, and strings (zero or
more characters). Lua also defines a special constant nil, which denotes the "has no value" value. The world of computing is built (mostly) on these data types. These different
data types have evolved over the decades to meet human computing needs.
A property of ALUs is that the number of bits in operands is limited in size. In the real world, you can repetitively add one to a number until exhaustion sets in. On a computer
that does not work. Adding one to the largest integer or real number is an error. Subtracting one from the most negative integer or real number is an error.
Lua numbers have three special error values: -inf, inf and nan (not a number). The -inf value is set when a negative magnitude or exponent exceeds the fixed-precision limit.
The inf value is set when a positive number exceeds the fixed precision limit. The nan value is set for illegal results, such as squareRoot(-1). Three common cases are (4/0 inf)
(-4/0 -inf) and (0/0 nan).
The following table lists the different Lua constants, their range of values, and examples of each.
Lua Constants
Constant Type
Range of Values
Examples
nil
nil
x = nil
Boolean
true, false
today = true
whynot = false
integer
-253 to +253-1
degrees = -32
hot = 100
real
1.7E +/- 308 (15 digits)
The 15 digits is calculated based on the range of magnitudes.
10.0e30 + 10.0e-30 will lose the fractional part.
pi = 3.14
half = .5
pi = 314.0e-2
string
zero characters to many thousands
noChars = ""
hello = 'hello'
twoLines = "hi\ngirls"
apple = 'ap\112le'
twoLines = [[
hi
girls]]
table
zero elements to many thousands
vertices = { 0,0, 5,5, -12,34 }
mixture = { 5, true, 'hi', 6.8 }
empty = { }
Rules to Remember
1.
2.
3.
4.
5.
6.
7.
8.
All values are bits in the computer. The data type does not become relevant until the bits become operands in an ALU.
Pick the data type that matches the task.
Be aware of the fixed-precision limits of numeric constants.
Real numbers are stored in bits, not decimal digits. As a result, there are more repeating fractions (hence inaccuracies) in binary reals than in decimal e.g. 0.610=0.10012.
The fixed-precision restriction means that the repeating fraction is truncated to a value slightly less than 0.6. To illustrate further, 0.1+0.2 yields a slightly different answer
from 0.06+0.24.
Never compare real numbers for equality. See Point 4.
Hexadecimal (base 16) constants are used when programs require specific bit patterns. Hexadecimal is a shorthand notation for bit values, four at a time. The hexadecimal
digits (see Appendix) are 0 1 2 3 4 5 6 7 8 9 A B C D E F. A hexadecimal constant is denoted by a leading 0x followed by the hex digits.
Real numbers have a decimal point. Real numbers have four parts: sign, exponent, sign of the mantissa, mantissa. The E suffix for the exponent is optional, is used for
scientific notation, and represents powers of ten e.g. 2.0E+2 equals 2000.0E-1 equals 200.0.
String constants are enclosed by matching single or double-quote characters. Strings have the problem of how to enter non-printing characters or a ' or ". The following
escape convention is used. A backslash followed by a real newline results in a newline in the string.
\n Write a <new-line> character.
\b
Write a <backspace> character.
\r
Write a <carriage return> character.
\t Write a <tab> character.
\'
Write a <quote> character.
\" Write a <double-quote> character.
\[ Write a <open bracket> character.
\] Write a <close bracket> character.
\\ Write a backslash character.
\0
Write a <0x00> character.
\ddd Write an 8-bit character whose ASCII value is the 3-digit decimal number.
One of the challenges in programming language design is to invent a syntax for multi-line string constants. Lua defines a "long bracket" convention. A double bracket [[
signifies the beginning of a multi-line string, which is terminated by the ]] symbols. For convenience, when the opening long brackets are immediately followed by a
newline, the newline is not included in the string.
A big theme of this book is to learn by doing. No one ever learned a language by reading about it. The first rule in learning to program is called the "hello world" step. It is
considered a significant accomplishment for a novice if they can create a program that prints the string "hello world". Basically, that means they know a lot of what has been
discussed so far. Other than that knowledge, there is a secondary advantage.
<Knowing how to print means that printing can be used to "explore" the rest of a programming language. If you do not understand something, write a very tiny test
program that prints or displays the result. When using a new API for the first time, always write a small program to make sure that you understand its functions before
using the API in a larger program. The next example prints one of each type of constant.
Earlier we listed the code (newText) to display a line of text on the screen. Displaying text on the screen gets more complicated when there are multiple lines or, even worse, the
text scrolls off the bottom of the display. Displaying text also has the disadvantage of being transient. One output can overwrite another.
Luckily, Lua defines a print method, which takes a variable-length list as an argument. The method prints its arguments and then outputs a newline. One defect, however, is that
table contents are not printed automatically. The next issue is "where is the output produced?". The Corona SDK on OS/X includes a Terminal option that links the Simulator
window to a command window, which is where "print" output is listed. Click on the CoronaTerminal icon instead of the CoronaSimulator icon to display both the Simulator and
the Terminal windows.
For Microsoft Windows, the terminal window starts automatically together with the emulator window.
RUN THE PROGRAM LISTED BELOW
1. Copy the listed program or execute adConstants. (Remember all the examples can be obtained by e-mailing [email protected]) The program also illustrates the use
of the Lua string format method.The arguments to string.format are format codes, which are denoted by a %, and an optional list of values (in the examples: one).
Arguments must be separated from each other with a comma ( , ) just like a list in English. The argument list as a whole must be enclosed in ( ). Part of the semantics
require that there be one format code for every value listed i.e. three values means three formats. The format string can have other content besides the % formats. There
must be one value for each format code. Each data type has its own format code. More detail on the format code options is listed in an appendix (click).
print(4, 0x14, 5.6, nil, true, 'hi', "ho",[[
happy
day]])
print(string.format('data= %d %5d', 62, 17))
print(string.format('da%%ta= %x %5o', 62, 17))
print(string.format('%f %10.3f', 6.2, 76.03456))
print(string.format('%e %10.3E', 6.2, 76.03456))
print(string.format('%g %g', 6.2, 76.03456))
print(string.format('%q ', "hi\nyy\tyf"))
print(string.format('%20s ', "hi\nyy\tyf"))
EXPECTED OUTPUT
4 20 5.6 nil true hi ho
day
data= 62 17
da%ta= 3e 21
6.200000
76.035
6.200000e+00 7.603E+01
6.2 76.0346
"hi\
yy yf"
hi
yy yf
happy
2. Compile and execute the program.
3. Modify each line to try different constants. Using step-wise refinement, only modify one line at a time. If you get a compilation error, revert to the last program that
worked and try again. Remember "copy code that works"!
Lua provides the "type" method to determine the data type of any Lua value. When a language can ask questions about itself, it supports reflection. The following code
illustrates the use of the "type" function, which returns the string name for the type of an object.
print(type("Type test")) --> string
print(type(7+8*6.1))
--> number
print(type(print))
--> function
print(type(type))
--> function
print(type(true))
--> boolean
print(type(nil))
--> nil
print(type(anyName))
--> nil
print(type(type(anyName))) --> string
anyName=5 print(type(anyName)) --> number
Line Syntax
In Lua, a sequence of blanks or tabs is not significant except in strings. Thus "print ( 35 )" is as valid as "print(35)"; however, "print(3 5)" is illegal. If you should make a typo
in your program, the Lua compiler prints out a list of error messages, usually with line numbers. Most editors will update a display of the line number as you move the caret to
different lines.
Strings obviously require some kind of marker (such as ") so that the parser can tell the difference between Lua reserved words (if return do while etc.) and a user's strings ("if"
"return" etc.). In a similar fashion, there exist markers to allow programmers to add explanatory comments to their code for the benefit of others. Some Lua code has been in
use for years! Its value is enhanced by embedding documentation comments in the code.
There are two comment syntax definitions -- and --[[ --]]. In the first option --, all characters from the -- to the next end-of-line character are ignored. The second convention -[[ --]] encloses the text that comprises the comment; however, multiple lines can be enclosed.
Comments are also useful when an error is encountered. Just comment out --[[ --]] the last code entered and verify that the program in its previous state still works correctly.
Consult documentation to try to identify the error in the commented out code.
Sometimes reading documentation just does not help to overcome a mental block. One of the traditional aids in programming is to ask someone else to take a look at the code,
either face-to-face or through an online forum. A second debugging option is based on a principle termed "determinism", which means that given a Lua statement, a
programmer can properly describe the result. Programming with statement or methods that are not understood is a recipe for disaster. Sometimes mental brain freeze causes a
programmer to think that code is correct when it is not. In this case, intersperse "print" statements throughout the problem code then compare the mental model to the printed
reality.
Expressions
The Lua language has a rich set of operators. Lua operators are polymorphic, which means that one operator (like + or -) works with several data types. There are three
operators (* / %) that are different symbols than the traditional operators used in math books. Asterisk (*) is the Lua multiplication operator. Slash (/) is the division operator.
The percentage (%) operator is used for modulo calculations; that is, the remainder on division (e.g. 13.1%4.2 = 0.5). The traditional unary - is provided to negate numbers
(-4.56). Exponentiation is denoted by the ^ operator (3^4 == 81). The operators are polymorphic because they can be applied to different types of operands, specifically integer,
real and string operands. Finally, there is a string concatenation operator .. that is also polymorphic.
In programming, the laws of algebra are only true sometimes. For example in algebra, (a+b)+c == a+(b+c). This law can fail on a CPU because of fixed precision limitations.
Either of the sums can overflow if a+b or b+c is too large.
The following program (aeExpressions) uses the print method to test each of the arithmetic and concatenation operators. In the last example, note that the product of two valid
floats is the illegal inf value (infinity meaning too big), which is a result of the limited precision in the FPU.
RUN PROGRAM LISTED BELOW
1. Copy the following code.
print('5+6.3=', 5 + 6.3)
print('5-6.3=', 5 - 6.3)
print('5*6.3=', 5 * 6.3)
print('5/6.3=', 5 / 6.3)
print('15%6.3=', 15 % 6.3)
print('15^6.3=', 15 ^ 6.3)
print('5 + -6.3=', 5 + -6.3)
print("'78' + 9=", '78' + 9)
print("'78' + 9=", '78' + 9)
print("'have a ' .. 'nice day='", 'have a ' .. 'nice day')
print("'have a ' .. (8*4+6)", 'have a ' .. (8*4+6))
print('1e+155*1e+155=', 1e+155*1e+155)
EXPECTED OUTPUT
5+6.3= 11.3
5-6.3= -1.3
5*6.3= 31.5
5/6.3= 0.79365079365079
15%6.3= 2.4
15^6.3= 25666989.447411
5 + -6.3= -1.3
'78' + 9= 87
'78' + 9= 87
'have a ' .. 'nice day=' have a nice day
'have a ' .. (8*4+6) have a 38
1e+155*1e+155= inf
2. Compile and execute the program.
3. Modify each line to try different constants in the expressions. Try using hexadecimal constants. Using step-wise refinement, only modify one line at a time. If you get a
compilation error, revert to the last program that worked and try again. Remember "copy code that works"! Try changing different characters in the program to become
familiar with some of the error messages that the compiler generates.
In formatting a program, it is recommended to place a space before and after each operator to make the program clearer to you, and to others.
What does 3+4*6 evaluate to? Is it 3+4 = 7 then 7*6 or is it 4*6 = 24 then 3+24? The answer is the latter and the reason is termed operator precedence, which basically means
that in parsing an expression from left to right, higher precedence operators are executed first. The precedence of an operator is defined by the grammar rules of a language,
which can be tricky to remember. The left-to-right part also fools people. We recommend that all expressions with multiple operators be fully parenthesized ( ). Thus, with
(3+4)*6 or 3+(4*6) there are no mistakes.
OPERATOR PRECEDENCE and ASSOCIATIVITY
Expressions are evaluated from left to right. Of two adjacent operators, the one with higher precedence is evaluated first.
If the operators have equal precedence, associativity determines left-to-right or right-to-left order.
Binary addition + (a+b+c) is left associative; b is added to a first.
Unary negation - is right associative (- - -3); the rightmost - is evaluated first.
3+5+6*7 == (3+5)+(6*7)
3+5*6-7 == (3+(5*6))-7
6*8*9 == (6*8)*9
7+3*6/2-1 == (7+((3*6)/2))-1
Math Functions
The Lua language has a rich set of libraries. One of the most useful mathematics libraries is the "math" (duh) library. For convenience, the definitions are listed next.
Math Library
math.abs ( x )
Returns the absolute value of x.
math.fmod ( x, y )
Returns the remainder of x / y as a rounded-down integer, for y not equal 0.
math.floor ( x )
Returns x rounded down to integer.
math.ceil ( x )
Returns x rounded up to the nearest integer.
math.min( list )
Returns minimum value from list.
math.max( list )
Returns maximum value from list.
math.huge
Returns largest represented number
math.modf ( x )
Returns integer AND fractional parts of x
math.sqrt ( x )
Returns square root of x, for x >= 0.
math.pow ( x, y )
Returns x raised to the power of y, i.e. x^y
math.exp ( x )
Returns e to the power of x, i.e. e^x.
math.log ( x )
Returns natural logarithm of x, for x >= 0.
math.log10 ( x )
Returns base-10 log of x, for x >= 0.
math.frexp ( x )
If x = m2e, returns m (0, 0.5-1) and integer e
math.ldexp ( x, y )
Returns x2y with y an integer.
math.deg ( a )
Converts angle a from radians to degrees.
math.rad ( a )
Converts angle a from degrees to radians.
math.pi
Constant Pi.
math.sin ( a )
Sine of angle a in radians.
math.cos ( a )
Cosine of angle a in radians.
math.tan ( a )
Tangent of angle a in radians.
math.asin ( x )
Arc sine of x in radians, for x in [-1, 1].
math.acos ( x )
Arc cosine of x in radians, for x in [-1, 1].
math.atan ( x )
Arc tangent of x in radians.
math.random ( [n [, m] )
Pseudo-random number in range [0, 1], [1, n] or [n, m].
math.randomseed ( n )
Sets a seed n for random sequence.
CHAPTER THREE
STATEMENTS
Introduction
The reader has probably observed that there is a lot more to Lua syntax than what we have discussed so far. In this Chapter, we cover variables (not constants), variable input,
and statements, such as assignment (copying a value into a variable), conditional testing and looping. With this additional information, more should become clear about the
programming examples discussed so far.
Variables
A variable (also termed an identifier) in all programming languages represents a name-value(s) pair. Referencing a name is the same as referencing its value(s). Like any other
component of a computer language, names have syntax rules. For example, ben-gurion might be a valid human name, but in Lua, the minus sign (-) indicates subtraction.
Therefore, minus signs and other special symbols cannot be used in names. A Lua name must begin with an underline (_) or a letter (A-Z or a-z). A Lua name can contain
any letters or digits and underlines. Remember that a name beginning with an underline character, followed by a upper case letter, is reserved for Lua's use.
Name Examples
_hello good_bye Y567 boy _
Names can stand for a value, or values, of any data type or mixture of data types. In Lua, the binding of a name to a data type occurs when the name is assigned a value.
Actions, such as data type binding, that occur at runtime are defined as dynamic. Referring to a name that has not been assigned a value returns nil.
Changing a variable
We would not call them "variables" if there were not some way to change them. The two choices are to set a new value either from input or from assignment (copying from a
constant, expression or another variable). Changing a variable by input can occur by filling-in-the-blank in a GUI environment text box, by typing from a keyboard, or by
reading from a file.
One of the problems with reading from the keyboard instead of a GUI environment is "how does the user know when or what to type?". Remember that the shell interpreter
begins each line of user input with a prompt. For many mobile devices, there is no physical keyboard. The keyboard is drawn as a graphic image and then the user "taps" keys to
enter text.
Assignment Statement
The assignment statement copies the value of a constant, expression, or another variable into a target variable. The target is referred to as the left-hand side (LHS) of the
assignment; the value as the right-hand side (RHS). The data type of the RHS need not be the same as the LHS. None of the operands on the RHS are affected by the
assignment; however, any old values for the LHS are totally replaced. If multiple statements are written on the same line, a semicolon can optionally be used as a separator.
Lua also supports multiple assignment; that is, several assignment statements can be written as a single operation. There is no limit on the number of comma-separated
elements. Multiple assignments involving the same variables on the LHS and RHS can be used to permute the values. If two variables occur on the LHS and RHS in opposite
order, the result is a swap of their values.
Assignment Statement Syntax
variable = variable
variable = constant
variable = expression
variable , variable = var/const/expr , var/const/expr
x = 23 y = x + 6 z = hello print(x, y, z)
23 29 nil
x = 23; y = x + 6; z = hello; print(x, y, z);
23 29 nil
x, y, z = 28, x + 6, hello print(x, y, z)
generates an error, all three RHS values are calculated before any assignment
x = 23 y = x + 6 x,y = y,x print(x, y)
29 23
a = 5 b = 6 c = 7 a,b,c = c,a,b print(a, b, c)
7 5 6
Conditional Statement
A condition is another name for a Boolean expression and an action. If you don't pick up your room this instant, well I don't know what! The condition is either that the room
will be picked up or it won't. The action to not picking up your room is probably all too familiar.
Before talking about the action part of the conditional statement, we need to discuss Boolean expressions.
Boolean expressions
Boolean expressions come in two forms: comparisons and connectors. A comparison can be for equality (== has to be different from assignment =), non-equality (~=), greater
than (>), less than (<), greater than or equal (>=) or less than or equal (<=). Do not compare real numbers for equality! The result of a comparison is either false or true.
Expressions can contain a complex combination of comparison operators and any other Boolean operator.
The Boolean connectors are logical and, logical or, logical not. Connectors require a bit of explanation. The Boolean connectors treat each entire operand as either false or
true. The next table defines the result of Boolean operators on hardware bits (typically zero is false and 1 is true). Boolean data is widely used in programming languages, such
as C and C++, because multiple variables can be stored in a byte of memory, which can hold eight Boolean bits.
In Lua, the operand nil is interpreted specially (nil and value is nil), (nil or value is value), (not nil is true). Numbers are interpreted as follows ( 5.6 and true is true, true and
5.6 is 5.6, not 7 is false, number or anything is number). In other words, false and nil are interpreted as false, anything else is true.
Short-circuit evaluation
In programming, it is often the case that two comparisons are joined by an AND (requires both true) or OR (requires either true). Computer gurus realized that if the first AND
operand is false, the expression cannot be true (false and anything equals false). Similarly, if the first OR operand is true, the expression must be true (true or anything equals
true). The resulting "Aha" is termed short-circuit evaluation. The optimization is to skip the evaluation of the second operand if the first operand is false (for and) or true (for
or). Lua implements short-circuit evaluation.
As the examples below illustrate, short-circuit AND evaluation is often required in situations in which the failure of the first test would generate an error if the second expression
were evaluated. The example checks a variable for zero, then skips a division by zero if the left-hand expression is false.
Short-Circuit Examples
x = ((a~=0) and ((x/a)>9))
--if "a" equals 0, the test is false, the 2nd expression is not evaluated
x = ((a==0) or ((x/a)>9))
--if a==0, the 2nd expression is not evaluated
true or 'hi' --result is true
false or 'hi' --result is 'hi'
IF statement
The conditional, or "if" statement is interpreted very much like its English usage, except for the syntax. There are three forms: simple, alternative and multiple. The simple form
just checks a Boolean expression; if it is true, a collection of statements is executed; if false, the statements are skipped. The alternative form includes an "else" and another list
of statements. The "else" clause is executed if the initial Boolean expression is false. Finally, the multiple form supports any number of test conditions, for example, an IRS
income tax table. The "elseif" component can be repeated as many times as necessary in order to add test conditions and actions. The Boolean expressions in the multiple form
should be complete; that is, if (x>5) is in one part, (x<=5) should be in another. The Boolean expressions in the multiple form are evaluated in order from top to bottom until one
evaluates to true. One of the conditions should always be true.
Be careful with the ordering in the multiple form. In what order should the following tests be performed: (x<=1) (x>7) (x>=22) (x>12)? Presumably, the intent is to partition
the test space into numbers less than or equal to one, between two and seven, between eight and twelve, and between thirteen and twenty-one, and greater than or equal twentytwo. Greater-than tests must be ordered from high to low (22 12 7 1) to be correct. Less-than tests must be ordered from low to high.
Just as parentheses ( ) group expressions, indentation should be used to highlight the statements in an action group.
IF Statement Examples
Simple
if Boolean Expression then
-- any list of statements
end --if
Alternative
if Boolean Expression then
-- any list of statements
else
-- any list of statements
end --if
Multiple
if Boolean Expression then
-- any list of statements
elseif (Boolean expression then
-- any list of statements
elseif true then
-- any list of statements
end --if
PROBLEM STATEMENT
1. Most programming assignments start with a problem statement, then the programmer uses step-wise refinement to develop a solution. The next problem is to read three
sets of X-Y values, verify that the points describe a right triangle, then draw the triangle.
STEP-WISE REFINEMENT
1. The program, as almost all programs do, consists of multiple (3) separable parts. A program could be written to read the input independent of the other parts. A program
could be written to verify the right triangle property. A program could be written to draw a triangle. ALWAYS MAKE UP TEST DATA BEFORE WRITING ANY
CODE! If you want to try coding it by yourself first, stop reading at this point then take a look at ahInput when you finish your code or get frustrated.
1 1 4 1 1 1 --2 points the same
1 1 2 1 4 1 --all points horizontal
1 1 1 4 1 0 --all points vertical
1 1 1 4 2 3 --no horizontal
1 20 50 20 1 40 --good one
100 100 200 100 200 50 -- good one
2. Write the code to draw a triangle first. Often visualizing the expected output can guide the correct coding of the remainder of the problem. The drawing code was only 9
lines but I made three mistakes and had to consult the documentation once before getting it correct and I am writing the book!
point1x = 1
point1y = 20
point2x = 50
point2y = 20
point3x = 1
point3y = 40
display.newLine(point1x, point1y, point2x, point2y)
display.newLine(point1x, point1y, point3x, point3y)
display.newLine(point3x, point3y, point2x, point2y)
3. Do you know how to test points for the right triangle property? No. Well I did not know either, but I do know Google. Never develop new non-trivial code in the
middle of a larger program! The first rule of coding is to look for code that does the job; the second rule is to look for a solution that can be coded. I Googled "are three
points a right triangle" and discovered that for three disjoint points, the right triangle property holds if the Pythagorean Theorem holds for any two of the sides.
It is often the case in step-wise refinement that problem decomposition exposes new problems that must be solved. Now we add the two new problems "Are three points
disjoint?" and "Does the sum of the squares of two line lengths equal the square of a third connecting line (the hypotenuse)?". In investigating the first problem, the
easiest way to test for three disjoint points is to test if a triangle's area is non-zero. The second property requires three Pythagorean Theorem line-length-squared tests
L122+L132==L232, L122+L232==L132, L132+L232==L122. We leave this portion of the problem to the reader. Write a program that calculates the area of a triangle and
calculates the three Pythagorean tests. Compare your result to ajTriangle2. Did you remember the real-number comparison rule?
4. The final step is to input the three points. At this point, the planned example took somewhat of a U-turn as the DragMe program was discovered in the Corona sample
code. The code displays three rounded rectangles that can be individually selected and dragged around the screen. Aha! If the rectangles were changed to small circles
and then those circles were connected by lines, we would have an app in which the user could dynamically create triangles to be tested.
As a further enhancement, the app will change a circle's fill color from red to green when the selected triangle has the "right" property. The example (aktriangle3) took
much longer than expected to develop. Just substitute your test for mine to try it out. At this point, only a brief explanation is presented. All aspects of the code will be
covered by the end of the book.
5. GUI programs are typically event driven. Events are generated for key presses, mouse motions, changes in screen orientation, clock ticks, enter-frame, and many other
user and system actions. Programs deal with events by connecting (via addEventListener) a block of code (termed an "on unit" or "listener") to the event type.
The example activates the "enterFrame" and "touch" events. Corona supports touch events on some shape objects, such as circles but not lines. Further, Corona
implements the dynamic movement of circles whenever their x,y coordinates are changed. However, lines cannot be moved dynamically. Discovering this anomaly and
then overcoming it took some time. Debugging was complicated by several typos and a couple of mental errors. The liberal use of "print" statements was a great aid to
isolating the bugs.
function onTouch( event )
return true
end
-- Iterate through arguments array and create circles for each item
for _,item in ipairs({dot1, dot2, dot3}) do
--code omitted
-- Make the circle instance respond to touch events
item.dot:addEventListener( "touch", onTouch )
end
circle = display.newCircle(100, 400, 100)
circle:setFillColor(1, 0, 0)
function update()
--insert your code here to test for the "right triangle" property
if right then circle:setFillColor(0, 1, 0) --green
else
circle:setFillColor(1, 0, 0) --red
end
end
Runtime:addEventListener( "enterFrame", update
While, Repeat and Do Statements
Program fragments are often enclosed by repetition constructs with the intent of performing a calculation over and over until a termination condition is met. The first two Lua
statements presented in this Section are while and repeat. The former tests the termination condition at the beginning of the loop; the latter tests the condition at the end.
While loops repeat the enclosed code fragment continuously as long as the Boolean termination condition is true. If the condition is initially false, the loop is skipped entirely.
The statements that are repeated are referred to as a compound statement or statement block. Repeat loops are used when the code block should always be executed at least
once. The do statement is used to group a block of code that has some unified purpose. do blocks are executed only once.
Statement Syntax
while BooleanExpression do
Statement List
end --while
repeat
Statement List
until BooleanExpression
do
Statement List
end --do
1. The while statement repeats the statement list only as long as the Boolean expression is true. If the expression is initially false, the entire statement list is skipped and
execution continues after the closing end.
2. The repeat statement always executes the statement list at least once. Further, execution of the list continues only as long as the Boolean expression is true.
CENTIGRADE TO FAHRENHEIT USING WHILE/REPEAT
--change from 51.0 to the stop value
stop = 51.0
--change 10.0 to the table increment
increment = 10.0
--output table heading
print("C\tF")
--change from 11.0 to the desired start value
x = 11.0
while x <= stop do
-- table values
print(string.format("%g\t%g", x, (1.8*x+32.0)))
x = x + increment
end --while
--change from 51.0 to the stop value
stop = 51.0
--change 10.0 to the table increment
increment = 10.0
--output table heading
print("C\tF")
--change from 11.0 to the desired start value
x = 11.0
repeat
-- table values
print(string.format("%g\t%g", x, (1.8*x+32.0)))
x = x + increment
until x > stop
Break Statement
This Lua statement is a no-brainer with respect to syntax: break. That is about as simple as it gets. The break statement can only be used inside while or do or for loops.
The break statement is frequently used with an if statement to check for a termination condition in the middle of a loop. If the break statement is executed, the loop
immediately terminates and execution continues after the loop's closing end.
WORD PROBLEM
Given an integer "start" value and an integer "stop" value and an integer "decrement" value, print the sequence. However, if one of the elements is zero, stop printing and exit the
loop.
start = 27
stop = -22
decrement = 9
while start >= stop do
if start == 0 then break end
print(start)
start = start - decrement
end --while
OUTPUT
27
18
9
For Statements
In the previous BigTables example, the program had an assignment statement, a Boolean loop termination test, and then the loop closed with an arithmetic assignment. This
pattern is very common in programming as it represents calculating a range of values over an independent variable (iNumber) then calculating one or more functions (iNumber3)
of that variable. All three parts (initialization, loop termination test, increment) are encapsulated in a single for statement in Lua. Remember that we used a for loop earlier to
calculate a "big" table. Just like the while loop, if the termination test is initially false, the statement list is never executed.
FOR LOOP SYNTAX AND SEMANTICS
for Assignment, StopValue, optional Increment or Decrement do
Statement List
end --for
1.
2.
3.
4.
5.
6.
7.
The statement list is continually executed (by repeating) the statements until the StopValue is exceeded (for incrementing) or the StopValue is passed (for decrementing).
If a break statement is executed within the loop, the loop is terminated and execution continues after the end.
For loops can be nested to step through multiple variables simultaneously.
If the Increment is omitted, the default value is one. To execute a loop from a high initial value to a lower value, a negative Decrement must be specified.
The Increment/Decrement values can be constants, variables or expressions.
Only numbers can be stepped with this form of the for loop.
Never change the value of the assignment variable within the loop. The effect of such changes is unpredictable.
--COUNT DOWN FROM 10 to -3 by 1
for x=10, -3, -1 do
print( string.format("%d ", x) )
end --for
OUTPUT
10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3
--COUNT UP FROM -3 to 10 by 2
for x = -3, 10, 2 do
print( string.format("%d ", x) )
end --for
OUTPUT
-3 -1 1 3 5 7 9
--ADDITION TABLE
for x=1, 3, 1 do
for y=1, 3 do
print( string.format("%d+%d == %d\n", x, y, x+y) )
end --for y
end --for x
OUTPUT
1+1 == 2
1+2 == 3
1+3 == 4
2+1 == 3
2+2 == 4
2+3 == 5
3+1 == 4
3+2 == 5
3+3 == 6
CHAPTER FOUR
PROCEDURES
Introduction
In programming, it is frequently the case that you find yourself typing the same thing over and over, sometimes with only a little variation from one copy to the next. The
solution, procedure definitions, is designed to help humans write programs. All of the string.format() uses in the previous Chapters are procedure references that 1) save you a
lot of typing and 2) hide a lot of complex detail that you really do not want to know. Procedures require only one definition part and can then have many references, like
math.sqrt.
Procedures (also called methods) come in two flavors: subroutines and functions. A subroutine does not compute a value that is returned to the caller. A function, such as square
root math.sqrt(25.0), calculates a value that can be a component of any expression: Boolean or arithmetic. A function can return one value or many. Lua uses the keyword
"function" to define both options. Functions must be defined before use.
Subroutine -- saves typing
x=5
x = x+1
x = x+1
x = x+1 --and so on; see a pattern?
--if so, define a subroutine!
-----------------------------------------function xPlusOne()
x = x+1 --x is global
return
end
x=5
xPlusOne()
xPlusOne()
xPlusOne()
print( string.format("x = %d", x) )
OUTPUT
8
1.
2.
3.
4.
5.
Type the subroutine sample program and execute it.
Procedure names must be unique.
Procedures must be defined before they are used.
A return statement, when executed, immediately exits a subroutine and returns execution control to the point of call.
A return statement that is just prior to a subroutine's closing end can be omitted as "executing" the closing end is the same as a return.
Function -- saves typing
x=5
x=x+1
a=x+1
x=x+1
b=x+1
x=x+1
c = x + 1 --and so on; see a pattern?
--if so, define a function!
-----------------------------------------function xPlusOne()
return x+1
end
x=5
a = xPlusOne()
b = xPlusOne()
c = xPlusOne()
print( string.format("a,b,c = %d,%d,%d", a,b,c) )
OUTPUT
a,b,c = 6,6,6
1. Type the function sample program and execute it.
2. The last statement executed in a function must be a return statement that calculates a value.
3. A return statement can be nested anywhere within a function. Any number of return statements can be used, as appropriate.
function r() return 3,4,5 end print( r() )
OUTPUT
3 4 5
function r() return 3,4,5 end a, b, c = r() print(a, b, c)
OUTPUT
3 4 5
Parameters and Arguments
Of course, the previous two examples are trivial, but they do make the point to be on the lookout for repeated patterns of code, either within expressions or across multiple
statements. Sometimes the use of a subroutine just "makes sense". For example, an equilateral triangle can be specified by a point and a side length. Why should the user who
wants to draw one have to figure out where the three points are? The question then is "how are the point and length parameters specified?". The answer is that the parameters for
a procedure are represented as a comma-separated list enclosed in parentheses ( ). With modern GUI text editors, parameters should be named according to their purpose.
Just copy-paste a name to add parameter references.
On the procedure call side, the arguments are also listed as a comma-separated, parenthesized list. The number of arguments on the call side must typically match the number of
parameters on the definition side. If there are too many arguments, the extra are ignored. If there are too few, the parameters corresponding to the missing arguments are
assigned nil values.
Max(a,b) Function
1. What is the return type?
2. What are the parameters' purposes and names?
3. How is the function accomplished?
4. Execute the following function.
function max(a, b)
--returns maximum of a or b
if a >= b then return a end
return b;
end
print( string.format("%d %d\n", max(5,6), max(3,-12)) )
OUTPUT
6 3
1. Procedure names must be unique.
2. The values in the parenthesized list in a procedure reference are referred to as arguments or actual parameters. The names in the parenthesized list in the procedure
definition are termed parameters or formal parameters. Parameters are local in scope.
3. Parameter names in the same procedure must be unique.
4. Parameter names in a procedure cannot duplicate variable names declared in the same procedure.
5. Procedures with the same purpose, such as max, but with different types for the arguments (number or string) must be given different names. The use of Hungarian
naming is one possibility i.e. add a one-letter prefix to denote the data types manipulated (nMax number sMax string).
6. Procedures with the same purpose, such as max, but with a different number of required arguments are usually given different names i.e. iMax2 iMax3.
7. The parameter names and all local variables declared within a procedure are accessible only within that procedure. The names are said to have a local scope of
reference.
8. Procedure names and all variables declared outside of a procedure can be accessed from their point of definition forward unless a name is reused in a local scope in which
case the local definition takes precedence, but only for that procedure. All procedure names and non-local variables are said to have global scope.
9. Passing an argument to its corresponding parameter is the same as executing an assignment statement on the parameter name. If there are fewer arguments than
parameters, the missing right-most arguments are set to nil. If there are more arguments than parameters, the right-most excess arguments are ignored.
10. The storage for parameters and local-scope variables is allocated automatically and disappears when the procedure returns.
Simple Image Animation
Congratulations on learning some of the key concepts of not only Lua procedures, but procedures in any language. We take a fun break to learn more about Corona using
example alfruit, which bounces a fruit around the screen. As mentioned in the shape programs, even though a screen might be 480x640, the client area for applications can be
inset based on operating system imposed limits. The following code uses display properties to calculate the proper dimensions. Further, the properties contentCenterX and
contentCenterY can be referenced to identify the middle of the client area. Remember that shapes are centered at their xy coordinate.
local screenTop = display.screenOriginY
local screenBottom = display.viewableContentHeight + display.screenOriginY
local screenLeft = display.screenOriginX
local screenRight = display.viewableContentWidth + display.screenOriginX
The example sets a background image to a field of grass and the fruit image to a peach. Google "jpeg versus png gif" to learn more about image formats. Corona images are
loaded using the display.newImage method. The arguments are a path name to the image file and an xy coordinate at which to center the image.
External files that are used by a program are referred to as resources or assets. Different operating systems have different conventions for accessing resources. For now, place
files in the same directory as main.lua. Put these two chunks of code together. Try substituting different image files. Experiment with the three image transforms listed next.
Also, use background::setFillColor(0,0,1) to learn about tinting images.
local xpos = display.contentCenterX
local ypos = display.contentCenterY
local background = display.newImage( "grass.png", xpos, ypos)
local fruit = display.newImage( "fruit.png", xpos, ypos )
Three of the common methods to transform images are translate, scale and rotate. In fact, these methods can also be applied to any shape.
fruit:translate( deltaX, deltaY )
--Amount to add to the object's x and y properties, respectively.
fruit:scale( xScale, yScale )
--Factors by which to multiply the scale in the x and y directions, respectively. You can flip a display object horizontally or vertically by passing negative arguments.
fruit:rotate( deltaAngle )
--Degrees to rotate. A positive number adds rotation to the object clockwise and a negative number rotates the object counter-clockwise.
In computer animation, motion is based on physics. The velocity of an object is the rate of change of the position of an object, which includes its speed and its direction of
motion, e.g. 50 miles/h to the north. In the example, the speed is in pixels and the direction is specified by a unit vector (x from -1 to +1, y from -1 to +1). We could define the
velocity in terms of seconds by using a timer, but since several other examples are based on the frame-drawing rate, the velocity of the peach will be expressed in pixels/frame in
the unit vector direction. Velocity then requires four variables (xspeed/yspeed, xdirection, ydirection). The change in position per frame can be calculated by a simple
multiplication. Be sure you understand the calculations that bounce the peach off the sides and even corner shots.
local radius = 40
local xdirection = 1
local ydirection = 1
local xspeed = 7.5
local yspeed = 6.4
local function animate()
xpos = xpos + ( xspeed * xdirection )
ypos = ypos + ( yspeed * ydirection )
--remember, xpos/ypos are the center of the peach
if ( xpos > screenRight - radius or xpos < screenLeft + radius ) then
xdirection = -xdirection
end
if ( ypos > screenBottom - radius or ypos < screenTop + radius ) then
ydirection = -ydirection
end
fruit:translate( xpos - fruit.x, ypos - fruit.y)
end
Runtime:addEventListener( "enterFrame", animate )
There are two final points. Note that the peach "floats" over the background and does not look like a rectangle even though we know that an image file always represents a
rectangle of pixels. Use Google or Yahoo to find an image editor for PNG files. The background has an alpha value of zero, which indicates complete transparency (typically
shown as diagonal bands). Even though the program moves the whole rectangle, only the solid-color pixels are shown. The second point explains how radius was set to 40
(diameter 80). Notice that there is a gap of about 10 pixels on each side of the peach and that the dimensions of the image are 100x100. Therefore, radius = (100-2*10)/2 or 40
pixels. The result is that the circumference of the peach, not the image edges, bounces off the window sides.
Procedure Parameters
Lua actually has a number of features that are missing in more widespread languages, such as Java. One such feature is procedure parameters. The purpose is dynamic, or
runtime, binding. The benefit is that part of a procedure's implementation can be chosen at runtime by the user of a routine.
function x(proc) return proc(0.7) end
print(x(math.sqrt), x(math.cos))
0.83666002653408 0.76484218728449
The addEventListener has two arguments: an event name and a procedure name. The example programs used the onTouch and enterFrame events. These events are
implemented as upcalls or callbacks (from runtime to user) using procedure parameters. The enterFrame callback is typically used for animation.
Timer Event
local function called(event)
print( event.time )
end
timer.performWithDelay( 1000, called, 0 ) --millisecond interval, name, continuously
CHAPTER FIVE
TABLES
Introduction
The term array is used generally to refer to vectors, and to matrices of two dimensions or higher. The element data type of an array need not be the same for all
components. Further, the number of elements in an array can increase or decrease as a program executes. Unlike mathematics, an element i of vector x is not referenced,
or indexed, as x(i) but as x[i]. The reason for square brackets is that parentheses () were already in use to denote procedure calls.
The first element of an array is always number one e.g. x[1]. If a vector has N elements, the last element is referenced as x[N]. The limits of an array are sometimes
referred to as bounds. The lower bound of a Lua array is one; the upper bound is N.
In Lua, arrays can be indexed by values other than integers. As a result, the data structure used to store arrays (indexed by consecutive integers) and to store associative data
(key, value) is identical and is referred to as a table. Since this book is for beginners, we draw the distinction between arrays and tables because they are different data structures
with different syntax in most other languages.
The first step in creating a new data type is to write procedures to output its values. Unfortunately, print does not have a format for arrays so we have to write our own routine.
Array Examples
a = {1, 2, 3, 4, 5}
b = {22, 'hi', 5.6, {98, 44,37} }
1. An array reference consists of the array Name[IntegerExpression].
2. The IntegerExpression is referred to as the array's subscript.
3. The subscript expression must evaluate to an integer between one and TotalNoOfElements; however Lua neither checks for subscript errors nor issues runtime errors for
violations of the rule. Accessing an out-of-bounds element yields a nil value. However, storing into an out-of-bounds element is always legal. Lua inserts the element at
the specified index but the insertion "breaks" the array property; that is, the array is no longer indexed by consecutive integers.
4. The "standard" method of referring to an array's TotalNoOfElements is #Name. In the example above, array #a has five elements, #b has four elements, #b[4] has
three elements.
5. Copy the code below as needed when you create additional data types.
function aformat(format, array)
--format is applied to each element
local s = ''
for i=1, #array do
s = s .. string.format(format, array[i])
end --for
return s
end --aformat
x={1,2,3,4,99};
print( aformat("%d ", x))
OUTPUT
1 2 3 4 99
Particles
Particle effects, such as explosions and laser beams, are a key component of game programming as well as other multimedia projects, such as television commercials and
movies. In its simplest form, a particle has a velocity, a size and a shape. To construct a particle effect, such as a fountain, we need hundreds or thousands of particles. An array
is the perfect data structure. For the example, the code defines circle shapes. New particles are created by adding elements to the four arrays. The outline for the program is
listed next.
local xVel = {} --particle data structures
local yVel = {}
local partSize = {}
local circle = {}
local xpos = 100 --create particles here
local ypos = 100
display.setDefault('background', 0,0,0)
display.setDefault('fillColor',
1,0,0)
local function animate()
--create one new particle per frame
for i = 1, #circle do
--update the position and size of every particle
end
if #circle > 200 then
--if too many particles, delete one
end
end
Runtime:addEventListener( "enterFrame", animate )
There are three ways to insert new array elements. The first is table.insert(array, value); the second option is table.insert(array, positionIndex, value). The first "insert" option
adds the element at the end of the array; the second option adds the element at a given index position and shifts up all succeeding elements. The third insertion option is to just
store at index position #array+1.
There is also a corresponding table.remove(array, index). The program illustrates the second insertion technique, which is to just store a new element in the last array index plus
one.
--create one new particle per frame
xVel[#xVel+1] = math.random(-1, 1) -- left - or right +
yVel[#yVel+1] = math.random(-12, -6) --only upward
partSize[#partSize+1] = 1
circle[#circle+1] = display.newCircle(xpos,ypos,math.random(6, 12))
--update the position and size of every particle
-- add the velocity to the positions
circle[i]:translate(xVel[i], yVel[i])
circle[i]:scale(partSize[i], partSize[i])
-- add some gravity
yVel[i] = yVel[i] + 0.1
-- make the particles shrink
partSize[i] = partSize[i] * 0.99
Forget about deleting particles for the moment and try out the code with different shapes, and even images. Modify the particle size-reduction from 0.99 to 0.99999.
A program that generates particles forever is said to have a "memory leak". Memory is a finite, limited resource; that is even more true on mobile devices. Lua implements
automatic garbage collection. For example, a circle that should never be drawn again is garbage. Lua attempts to reclaim garbage (unused space) at fixed intervals.
How does Lua tell the difference between a garbage circle and a good one? Lua keeps track of object references. For example, the two statements "x=newCircle(..) y=x"
generate two references (x and y) to the circle. Objects are trashed by calling their removeSelf method and setting any references to nil. To delete the last element of an array,
set its value to nil.
--to trash a circle
circle:removeSelf()
circle = nil
-- delete the last element of an array
x[#array] = nil
--delete the oldest particle
table.remove(xVel, 1)
table.remove(yVel, 1)
table.remove(partSize, 1)
circle[1]:removeSelf() --trash first
table.remove(circle, 1)
An alternative to deleting the oldest particle would be to delete a random particle. The table.remove particle action could be used to perform the deletion; however, its
implementation moves every higher array element down one. For thousands of particles, the copying could get expensive.
One branch of the computer science discipline is the study of algorithms. An algorithm can be time-efficient or space-efficient, or both.
An algorithm for deleting an element in an unordered array is to copy the last array element into the deleted position and then to delete the last array element, which is efficient.
The sample code anParticles illustrates both deletion choices. Modify the code to delete a particle when its x coordinate exceeds the screen width or is less than zero, or if the y
coordinate exceeds the screen height.
The strategy of deleting the oldest particle first can also be implemented with an algorithm that implements a queue or First-In-First-Out data structure. An array can be used to
implement a 200-element circular FIFO queue, which is very efficient for insertion and deletion. Look up the algorithm on the Internet and try it out on the sample code.
Array Parameters
As discussed earlier, argument values are assigned to parameter variables. This implementation choice would be quite inefficient for large arrays. As a result, Lua implements
two forms of parameter passing: 1) call by value and 2) call by reference. The former choice we already covered.
Array parameters are assigned arguments using call by reference. For array arguments, there is no special notation. It "works" by default. The primary difference is semantic.
On assignment, changes to array parameters immediately affect the argument array's value. Any change to one array changes the other. The reason is that the parameter
and the argument both "reference" the same array. However, assigning a new array to an array parameter "breaks" the connection to the argument array.
x={1, 2}
function y(z) table.insert(z,22) print(#z, #x) end
y(x)
OUTPUT
3 3
Some functions in Lua can be called with a variable number of arguments. For instance, we have already used print with one, two, and more arguments. Lua defines a syntax for
a method with a variable number of parameters ... (3 dots), which can only be used once and must occur last in the parameter list. When such a function is called, all its variablelist arguments are collected into a single list, which the function can access with the name ... . The multiple values are treated just like a list of values in a multiple assignment.
In other contexts, referring to ... will just access the first value if it exists.
function test (a, ...)
local x = ...
print(x)
print( ... )
print( {...} ) --convert to an array with an extra n field
print( select(2, ...) ) --select a sub-list
print( select('#', ...) ) --length of the list
end
test(3)
OUTPUT
nil
table: 0x7fc962d01580
0
test(4,5,6,7,8)
5
5 6 7 8
table: 0x7fc962c0f480
6 7 8
4
Variable argument lists are widely used for functions like max or min. Other uses occur in vector operations or in applying a function to a list. Both examples follow. In the
vop example, the function argument is termed an anonymous function. It is a procedure constant.
function vsum( ... )
local sum = 0
local x = { ... }
for i=1, #x do sum=sum+x[i] end
return sum
end
print(vsum(1,2,3))
6
print(vsum(4))
4
print(vsum())
0
print(vsum(unpack{7,8,9}))
24
function vop(f, ... )
local sum = 0
local x = { ... }
for i=1, #x do sum=sum+f(x[i]) end
return sum
end
print( vop(function(x) return x*2 end, 8,9,5,3) )
50
Sprites
Analyzing algorithms gives some people brain freeze so we take a mental break to discuss one of the cool areas of gaming: animated sprites. Back in the old days at Disney,
animations were drawn one frame at a time on acetate cels, which went out of use in 1990. Disney Stores sold production cels from The Little Mermaid (their last film to use
cels) at prices from $2,500 to $3,500, without the original backgrounds. Today, cels have been replaced by sprite sheets, such as the following image file, which is 256x256.
Corona defines a graphics.newImageSheet method to map sub-images to frames. In the cat example, each frame is 256/2 wide and 256/4 tall. The frames are designed so that
frame 8 (bottom right) flows naturally into frame 1 (top left). As a result, the eight frames can be played circularly from 1 to 8 to animate a running cat. The frame rate is set to 8
frames/second. The rate can be set higher or lower, or modified dynamically, to vary the speed of the cat. Do you remember the earlier discussion of xScale/yScale? What is
the effect of setting xScale to minus one? Try it.
-- an image sheet with a cat
local catSheet = graphics.newImageSheet( "runningcat.png", { width=128, height=64, numFrames=8 } )
-- play 8 frames every 1000 ms
local catSprite = display.newSprite( catSheet, { name="cat", start=1, count=8, time=1000 } )
catSprite.x = display.contentWidth / 4 + 40
catSprite.y = 275
catSprite:play()
Corona's sprite API is very flexible. The frames can be played in any order, can be different sizes, and can be drawn from different sprite sheets. Run the aoCat example. Try
replacing the image with your own sprite sheets. Just Yahoo "sprite sheet" to download examples from the Internet. Use an image editor to get the frame dimensions. If you are
artistically inclined, draw your own frames. Google "animated gif". An animated gif is basically a sprite sheet in a special format.
A running cat is interesting, but notice that it runs in place. If we translate the x coordinate, the cat will run across the screen then disappear! This is not desirable. From the
earliest days of cartoons, and even movies, the standard has been for the characters to stay or move in place and to have the background scroll from right to left. The
apCatBackground example implements background scrolling.
The image grass.png is scrolled from right to left to convey the impression that the cat is moving forward. The effect would be more dramatic if the background varied. We
leave that as an exercise for the reader. Examine the Jungle sprite example in the Corona SDK.
The problem with scrolling an image is that one of the edges is going to move into view which would "break" the effect. The code implements a double-image algorithm. By
creating the same image side-by-side and scrolling them in tandem, there is never a bare edge visible. The technique does require the alignment of the right edge of image1 with
the left edge of image2 and the left edge of image1 with the right edge of image 2. Otherwise, the discontinuities would be visible to the viewer.
local grass = display.newImage( "grass.png" )
-- ...
local grass2 = display.newImage( "grass.png" )
-- ...
--slide an object right by factor pixels per 1 ms
local function slide(object, factor, delta)
local xOffset = ( factor * delta )
object.x = object.x - xOffset
if (object.x + object.contentWidth) < 0 then
object:translate( object.contentWidth * 2, 0)
end
end
-- A per-frame event to move the objects
local tPrevious = system.getTimer() --milliseconds
local function move(event)
local tDelta = event.time - tPrevious
tPrevious = event.time
slide(grass, 0.1, tDelta)
slide(grass2, 0.1, tDelta)
end
-- Start everything moving
Runtime:addEventListener( "enterFrame", move );
Tables
Conceptually, an array is a table whose elements are indexed by the consecutive integers 1 to #array. But what if the indices are not consecutive; what if the indices are not
integers? The only array data structure in Lua is the table, which is an associative list of (key, value) pairs. If the indices conform to the "array" property, the data
structure behaves like an array in other languages.
Did you wonder why the floor function was used in the quicksort code? The answer is that translating the code from Java introduced an error because Lua and Java differ in
semantics. In Java, 7/3 is 2. In Lua, 7/3 is 2.3333. Since "a" is an array represented as a table, trying to access the element with key 2.3333 always yields nil. When coding
expressions that can result in fractions, always apply the floor function prior to using the expression as an array index.
local k = math.floor((left+right) / 2)
pivot = a[ k ]
Lua tables can have two parts, both of which are optional. The first part is the array part, which must have a first key of 1 and a last key of #n. Initializing an array with nonconsecutive integer indices will "break" the array property. #array always returns the index of the last non-nil element after index position one. The integer indices do
not have to be initialized in order; 35421, 54321, 12345 are all legal sequences that maintain the array property.
The second part of a Lua table is an associative list. Either the array part or the list part may be present, or both, or neither. The associative list part is indexed by strings or real
numbers. As a shorthand notation, x['foo'] is synonymous with x.foo. Telephone books and contact directories are associative lists. If the user creates an array part and an
associative part, the associative part can only be accessed associatively; integer indices will not work on it. Do not confuse strings as table values with strings as table keys. The
associative part can only be created by an x.foo or x['foo'] assignment.
a={'hi', 'by', 'fli'} print(unpack(a)) --array only
hi by fli
a[1]='hi' a[3]='by' a[2]='fli' print(unpack(a)) --array only
hi fli by
a={}
table.insert(a, 'hi') table.insert(a, 'by') table.insert(a, 'fli')
print(unpack(a)) --array only
hi by fli
a={} a[57]=9 print(a[57], #a) --associative only
9 0
a={} a['rt']=9 print(a['rt'], #a) --associative only
9 0
a={'hi', 'by', 'fli'} a[57]=9 print(unpack(a)) print(#a, a[57]) --array and associative
hi by fli
3 9
Lua defines two convenience functions for tables, ipairs and pairs, that are typically used with a special for-loop syntax. The ipairs function only iterates over the array part.
The pairs function iterates over all the keys. The term "iterate" means to enumerate, or step through, the elements of a list. An "iterator" is a function that iterates.
Remember that Lua functions can return multiple values. Both functions return the key for each element accessed and its associated value. It is common to use the name '_' to
indicate that one of the two values is being ignored. Study the following examples. You can also go back and examine the triangle example, which used the ipairs function.
a={'hi', 'by', 'fli'} a[57]=9
for key,value in ipairs(a) do print(key,value) end
1 hi
2 by
3 fli
for key,value in pairs(a) do print(key,value) end
1 hi
2 by
3 fli
57 9
CHAPTER SIX
STRINGS
Introduction
People understand people-talk. Luckily, some of the first interfaces added to the Lua library were to support character and string operators. The string interface string.* defines
methods to reverse, search, match and convert strings. Further, there are methods that deal with substrings; for example, extracting a substring from a longer string. The prefix #
operator returns the length of a string.
The rules are somewhat unusual because ASCII and Unicode characters can be intermingled. As discussed earlier, ASCII characters are 8-bits; Unicode characters are
normally16-bits. The Unicode and ISO-10646 standards define a Universal Character Set (UCS), a 31-bit character set, which has various encodings of subsets: UTF-8, UTF-16
and UTF-32.
string.* API
Function
Description
Example
string.byte(string)
string.byte(string, start, end)
string:byte(string)
string:byte(string, start, end)
Return the ASCII or Unicode integer value(s) for the first character of a string or any substring thereof
x='abcdä34'
print(#x)
8
print(x:byte(5))
195
print(string.byte(x,2,3))
98 99
string.char(list of integers)
Convert a sequence of only ASCII values into a string.
print(string.char(97,98,99,51,52))
abc34
string.len(string)
string:len()
Return the length of a string
x="ä¸çä½ å¥½ï¼"
print(string.len(x))
22
string.lower(string)
string:lower()
Return a string that has been converted to lowercase
x='aBCDe45' print(x:lower())
abcde45
string.upper(string)
string:upper()
Return a string that has been converted to uppercase
x='aBCDe45' print(x:upper())
ABCDE45
string.reverse(string)
string:reverse()
Return the string reversed
x='aBCDe45' print(x:reverse())
54eDCBa
string.rep(string, n)
string:rep(n)
Return n concatenated copies of the string
x='aBCDe45' print(x:rep(2))
aBCDe45aBCDe45
string.sub(string, start)
string.sub(string, start, end)
string:sub(start)
string:byte(start, end)
Return a substring of the given string. If only the start index is listed, the remainder of the string is
returned. A negative start index indicates to start at the end of the string.
x='aBCDe45' print(x:sub(2))
BCDe45
x='aBCDe45' print(x:sub(2,4))
BCD
x='aBCDe45' print(x:sub(-2))
45
x='aBCDe45' print(x:sub(-5,-2))
CDe4
string.find(string,pattern,init)
string:find(pattern, init)
Looks for the first match of a pattern in a string. If found, it returns the indices where the pattern starts and
ends; otherwise, returns nil. The init value is optional and indicates where to start the search.
x='aBCDe45' print(x:find('De'))
4 5
string.gsub(s,pattern,replace,n) Returns a string in which all (or n if specified) occurrences of the pattern are replaced with the replace
x='abacadaeaf'
s:gsub( pattern,replace,n)
string. The number of replacements is also returned. There are advanced options. Refer to a Lua reference print(x:gsub('a', 'XX'))
manual for more information.
XXbXXcXXdXXeXXf 5
x='abacadaeaf'
print(x:gsub('a', 'XX', 3))
XXbXXcXXdaeaf 3
String Input and Output
It is very common on computer games to display pop-up windows, termed "alert dialogs", that display information to the player and that possibly ask the user to choose among
several options. One must be careful when localizing text layout as simple words in one language may have lengthy equivalents in another. Try different options with the
aqAlert sample code.
local function onComplete( event )
print( "index => ".. event.index .. " action => " .. event.action )
local action = event.action
if "clicked" == event.action then -- test choices here
if 2 == event.index then
end
elseif "cancelled" == event.action then
-- the cancelAlert timer function dismissed the alert so do nothing
end
end
-- Show alert
local alert = native.showAlert( "Alert Example", "Tell user stuff.", { "Choice One", "Choice Two" }, onComplete )
-- Dismisses alert after 10 seconds, optional
local function cancelAlert()
native.cancelAlert( alert )
end
timer.performWithDelay( 10000, cancelAlert )
User input is always an issue in GUI programs. The Alert program, for example, prints information upon completion. However, in a real program, frame or timer events would
be occurring on a regular basis. A program must have a way of testing for completion and then retrieving the result of a dialog. The next example arInput just creates a singleline text field. Corona also implements a scrollable text box that can handle multiple-line input.
The example defines two global variables to manage text field input. One variable "gotText" is set to true when the dialog completes and the second variable "text" is set to the
string that was entered. In order to use the text field, the program must wait until the gotText variable is set to true and then the input variable "text" can be accessed.
The issue is that waiting in GUI code cannot be accomplished by pausing the program. Putting delay code in a timer or enterFrame method is a recipe for disaster! If you have
ever had a windows program that stopped responding, more than likely it got stuck in the display routine.
The code to create a text field follows. The text field is 10 characters wide with a user-specified label on the right. The design is oriented towards simplicity of use. The
implementation is adapted from the sample code in the Corona SDK. Only one text field at a time is supported. If multiple text fields are required, typically a button would be
added to the dialog to signal a completion event.
local function update(event)
if not touched then --create 10-char field on first screen touch
newText("INPUT", 10, 40,math.random(20,400)) --x 40 random y
touched = true
end
local done, txt = getText() --avoid lockup, return if not done
if done then print(text) end
end
-- Start everything moving
Runtime:addEventListener( "touch", update )
String Input
local gotText
local text
local function fieldHandler( textField )
return
function( event )
if "began" == event.phase then
-- This is the "keyboard has appeared" event
-- In some cases you may want to adjust the interface when the keyboard appears
elseif "ended" == event.phase then
-- Called when the user stops editing a field, e.g. when they touch another field
elseif "editing" == event.phase then
elseif "submitted" == event.phase then
-- The user presses the "return" key (if available) on the onscreen keyboard
field, label = textField()
gotText = true
text = field.text
-- Hide keyboard
native.setKeyboardFocus( nil )
field:removeSelf()
label:removeSelf()
end
end --function
end --fieldHandler
local function newText(label, nChars, x, y)
gotText=false
.........
local defaultLabel =
display.newText( label, x+nChars*inputFontSize+4, y, native.systemFont, 18 )
.........
local inputField =
native.newTextField( x, y, nChars*inputFontSize, tHeight )
.........
inputField:addEventListener( "userInput", fieldHandler( function() return inputField, defaultLabel end ) )
end
local function getText()
if gotText then gotText = false return true, text end
return false, nil
end --getText
CHAPTER SEVEN
DEBUGGING
Introduction
A program can fail for a number of reasons. The first reason is a misunderstanding of the proper usage of a method. The accepted prevention strategy is to write a small program
to test all aspects of a method before using it in a larger program. The second reason for program failure is to use or develop an algorithm that does not handle all possible
inputs. The third reason is to code a correct algorithm incorrectly. The fourth reason is a misunderstanding of Lua or Corona semantics. For example, comparing real numbers
for equality is error-prone. If that fact is forgotten, a program can fail. The fifth reason is Lua itself. Neither the compiler nor the runtime are very good at checking for user
errors. There are many other error possibilities; in fact, too many to cover them all. The conclusions are inescapable: proving that a program handles all inputs is difficult and
finding errors in an incorrect program is difficult.
The first step in program development (even before initiation of coding) is to develop a comprehensive set of test cases AND the expected output for each. It is imperative that
the solution model in your head match how the computer operates. If a programmer cannot calculate the proper output for an input, there is no hope of writing a correct program.
What is the proper response when a program's output is different from that expected? The goal is to identify the program state at which the program first varied from the result
expected. Program state is the representation of a running program. Do you remember the earlier discussion of determinism?
A running program is comprised of code, a procedure-call stack and dynamically allocated storage for variables. The only important aspect of code is what statement will be
executed next. This requires a good understanding of Lua and Corona semantics. A program begins with no variables and no procedure-call stack. As the program executes
statements, the initial state is transformed into new states by statement execution. The goal is to identify the earliest state at which the result differs from that expected.
Wolf-Trap Debugging
It is often the case that the only information available to the programmer is that the output is wrong. Wolf-trap debugging is a technique used to identify the earliest point of
error. The steps are best remembered by associating them with a short story.
Wolf-Trap Story
A farmer was losing sheep to a big bad wolf. So the farmer built a fence around the remaining sheep.
If the wolf was inside the fence, eventually another sheep would be eaten. If the wolf was outside the fence, the sheep would be safe. Problem solved!!
Unfortunately for the farmer, another sheep disappeared! What to do? The farmer built another fence inside the first and continued building fences until the wolf was
either caught or stopped eating sheep.
In programming, our fence equivalent is the print statement. Place print statements for the pertinent variables in the program to discover the point of error. Either the error
occurs before the prints, or after them. In any case, a large portion of the program is eliminated as an error source. If the error occurs before the prints, place more prints before
the first. If the error occurs after, place the second set of prints later in the code. Eventually, the point of error will be discovered.
Where to place the print statements? Place the first print halfway through the program. Place the second print either halfway through the first part of the program or the last part,
depending on where the error appears. Continue in this fashion to halve the remaining code at each step. How many steps are necessary to search a 1,000,000 line program?
log2(1000000) = 20. This strategy exactly mimics the binary search strategy that was discussed in the Algorithms Section.
Once the point of error is discovered, it may be necessary to insert conditional statements to isolate the error in time. For example, a for-loop may repeat many times before an
error occurs.
Examining Program State
There are command-line programs (debuggers) to control program execution and to display variables and other state information. Most systems also include a GUI debugger.
However, some systems and environments have neither, which is why we used print-statement debugging up to this point in the text. A GUI debugger is a luxury, not a
necessity.
The debugger that we recommend is ZeroBrane Studio, which is actually an Integrated Development Environment, or IDE, that supports program development and editing as
well as debugging. The software was developed by Paul Kulchenko. It can be downloaded at studio.zerobrane.com. For Windows, choose the ".exe installer" option.
Once downloaded, start the ZBS application. The first step in starting a new project is to create a project directory. The directory must be created using the OS' file manager. I
named the directory "ztest". The second step is to copy the mobdebug.lua file, which comes with the ZBS distribution, into the project directory. The mobdebug file can also be
copied from the ztest directory in the sample code that comes with the book. The file is only required to debug with the Corona emulator. You could also install a standard Lua
distribution from lua.org to develop non-GUI programs.
Next, click the ... button to select the "ztest" project.
The default file "untitled.lua" must be renamed to "main.lua" because Lua programs start execution in that file. Control-Click (OS/X) or right-click (Windows) on the name,
select the "Save As" menu item, update the edit box to main.lua, and then click "Save".
Enter the Lua code "print(333)". If you make a typing or syntax mistake, ZBS will display error messages with line numbers. Click the right-pointing green triangle to execute
the program. You may need to select the Lua interpreter first. ZBS supports debugging using Corona or the standard Lua distribution from lua.org. The first Figure shows the
standard distribution; the second Figure shows Corona in execution (the emulator window is shown in the background). The small green triangle located to the left of the "print"
statement indicates that a "breakpoint" has been reached.
A breakpoint is a language statement that is "marked" so that the debugger will "break" execution whenever that statement is encountered. When a program is stopped at a
breakpoint, its state variables can be examined in detail. To continue execution, click the icon bar triangle a second time or select the menu item Project/Continue".
ZBS starts the Corona emulator automatically. To terminate a Corona program, click the blue-square icon to the right of the green triangle. Notice that ZBS collects the console
output from "print" statements in its Output window.
Even with a GUI debugger, the wolf-trap debugging technique is still valid. The differences are that setting breakpoints is used to isolate errors (place fences) and that a
program's state can be viewed directly by hovering the mouse over a variable. Breakpoints can be toggled in any file and at any point by clicking to the left of a line or by
moving the edit caret to a line and then clicking the red circle.
Notice that starting the program at this point ignores breakpoints. This is where the "mobdebug.lua" file comes into play. The first line of the program must be set to a "require"
statement to connect ZBS to the chosen interpreter. The code only has to occur once, and then only in the main program. If you try to debug code and nothing works, MAKE
SURE THE MOBDEBUG FILE IS IN THE DIRECTORY AND THAT THE REQUIRE IS UNCOMMENTED. In Windows, you may get a firewall warning message. The
reason is that ZBS uses network ports to communicate with Corona. Ignore the message.
require('mobdebug').start()
A really cool debugging feature is the ability to single-step a program after it has reached a breakpoint. The single-step options are "into", "over" and "step out of". One of the
problems in single-stepping is that some lines contain calls to functions that you know are correct. It would be annoying and time consuming to have to step through a correct
function just to reach a further point of error. On the other hand, sometimes stepping requires entering a function to pursue an elusive bug. The "step over" option executes the
current statement and any function calls that it contains and then "breaks" at the next line.
The "step into" option enters every function definition as its name is encountered during execution. The "over" option only steps through lines in the current program file. The
"into" follows program flow through any module that defines a function that is used after a breakpoint. The "step out" choice is utilized to exit a function after a user has learned
all that is possible.
The following example contains a simple "sum" function. A breakpoint was set in the middle of the for-loop. The value of the variable "x" was displayed by hovering the
mouse over the "x".
Since tables can be very lengthy, ZBS provides an option to access table components by name. At a breakpoint, click on the "Remote console" tab then set the edit caret at the
">>>". Type "=x" or "=x[3]" or "x.name" to examine variables.
ZBS has a couple of other nice debugging capabilities. The first is referred to as a "watch" window. It can be time consuming to hover over different variables every time a
breakpoint is activated. By Ctrl-Clicking or right-clicking on a variable at a breakpoint, a menu is displayed that can be used to add a variable, or expression, to the "watch"
window as shown next. Now, when the associated breakpoint is reached, the "watch" window updates automatically. This option is a great timesaver.
You may have forgotten by this point, but earlier we discussed the three components of a running program: code, data heap, and the procedure call stack. The latter is referred to
as a stack because the most recent function has to exit in order to return to its caller. This behavior is defined as Last-In-First-Out, or LIFO, which is the definition of a stack
data structure in computer science.
Every programming language maintains the call stack data structure in a format that allows it to be reverse engineered to identify function arguments and local variables. The
"stack" window can be activated through the "View" menu or by clicking the stack icon to the right of the red circle.
ASCII TABLES
BINARY, OCTAL, HEXADECIMAL
string.format Codes
Each format specification is introduced by the percent character ("%''). The remainder of the format specification includes, in the following order:
Zero or more of the following flags:
#
A '#' character specifying that the value should be printed in an "alternative form''. For c, d, and s formats, this option has no effect. For the o formats the precision of the number
is increased to force the first character of the output string to a zero. For the x (X) format, a non-zero result has the string 0x (0X) prepended to it. For e, E, f, g, and G, formats,
the result will always contain a decimal point, even if no digits follow the point (normally, a decimal point only appears in the results of those formats if a digit follows the
decimal point). For g and G formats, trailing zeros are not removed from the result as they would otherwise be;
-
A minus sign '-' which specifies left adjustment of the output in the indicated field;
+
A '+' character specifying that there should always be a sign placed before the number when using signed formats.
' '
A space specifying that a blank should be left before a positive number for a signed format. A '+' overrides a space if both are used;
0
A zero '0' character indicating that zero-padding should be used rather than blank-padding. A '-' overrides a '0' if both are used;
Field Width:
An optional digit string specifying a field width; if the output string has fewer characters than the field width it will be blank-padded on the left (or right, if the left-adjustment
indicator has been given) to make up the field width (note that a leading zero is a flag, but an embedded zero is part of a field width);
Precision:
An optional period, '.', followed by an optional digit string giving a precision which specifies the number of digits to appear after the decimal point, for e and f formats, or the
maximum number of characters to be printed from a string; if the digit string is missing, the precision is treated as zero;
Format:
A character which indicates the type of format to use (one of diouxXfeEgGbcsq). A field width may optionally precede the format character. The format characters and their
meanings are as follows:
diouXx The argument is printed as a signed decimal (d or i), unsigned octal, unsigned decimal, or unsigned hexadecimal number (X or x), respectively.
f
The argument is printed in the style [-]ddd.ddd where the number of d's after the decimal point is equal to the precision specification for the argument. If the precision is
missing, 6 digits are given; if the precision is explicitly 0, no digits and no decimal point are printed.
eE
The argument is printed in the style [-]d.ddde+-dd where there is one digit before the decimal point and the number after is equal to the precision specification for the
argument; when the precision is missing, 6 digits are produced. An upper-case E is used for an 'E' format.
gG
The argument is printed in style f or in style e (E) whichever gives full precision in minimum space.
c
The first character of argument is printed.
s
Characters from the string argument are printed until the end is reached or until the number of characters indicated by the precision specification is reached; however if
the precision is 0 or missing, all characters in the string are printed. Strings with embedded \0 cannot be printed with this option.
q
The q option formats a string in a form suitable to be safely read back by the Lua interpreter: the string is written between double quotes, and all double quotes,
newlines, embedded zeros, and backslashes in the string are correctly escaped when written.
%
Print a `%'; no argument is used.
In no case does a non-existent or small field width cause truncation of a field; padding takes place only if the specified field width exceeds the actual width.
M.I.T. Software License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.