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.
© Copyright 2026 Paperzz