Showing posts with label example. Show all posts
Showing posts with label example. Show all posts

Friday, July 13, 2012

Interlude 8 - A Simpler Starfield (Updated 23/01/16)

Webcomic Courtesy of Ethanol & Entropy

We have been using the Twinkle class written by Ipad41001 to produce our star-field effect in MineSweeper, and a very fine class it is. However it is a touch computationally expensive if there is a bit else going on at the same time.

On the "hard" difficulty using our first generation iPad, the frame rate per second is around 14-15 using the Twinkle class. This doesn't effect gameplay but the twinkles are a bit slow.

If your game is more frame rate critical or you just want a different look then you could use the following function instead of this class. Using this star field effect gives us an extra 4-5 frames per second.

In your Main class, you will need:

-- Main

-- A variable to hold the number of stars to display on the screen,
-- the smaller the number of stars the quicker this function will run.
-- Note that this is a local variable and only available in the Main Class

local NUMSTARS = 50

-- In the setup function you need to initialise the table 
-- which will hold your stars

function setup()
    
    -- define black colour to make our code more readable

    blackColour = color(0, 0, 0)

    -- Initialise the stars table which contains the x and y screen co-ordinates
    -- for each star. These are set to a random position between 1 and the screen
    -- height and width.

    stars = {}
    for i = 1, NUMSTARS do
        stars[i] = {x = math.random(WIDTH), y = math.random(HEIGHT)}
    end

end

Add the following function to draw the star field.

function drawStarField()

    -- Star Field function courtesy of Javier Moral
    -- from his Fireworks example

    for i = 1, NUMSTARS do

        -- Set the fill colour to white and a random transparency, 
        -- this is half of the twinkle effect.

        fill(255, 255, 255, math.random(255))

        -- Each star is represented by a small rectangle. 
        -- The random x and y co-ordinate of
        -- the star was initialised in the setup() function, 
        -- so the stars don't move. The width 
        -- and height of each rectangle is set to a random integer 
        -- between 1 and 3 each frame.
        -- Codea will try and call draw() 60 times per second.

        rect(stars[i].x, stars[i].y, math.random(3), math.random(3))

    end

end

and then call it in the draw() function:

function draw()

    -- Set the background colour to black

    background(blackColour)

    -- Call the star field function

    drawStarField()

end

Thursday, July 12, 2012

Interlude 7 - Recursion & Closures in Lua (Updated 23/01/16)

Webcomic Courtesy of Ethanol & Entropy

Interlude 7.1 Preamble


Before part 2 of the MineSweeper tutorial, we need to go over two concepts which you may not have come across before, namely recursion and closures. Writing recursive code is a fairly common technique but Lua is the first language that we have come across that uses closures.


Interlude 7.2 Recursion


Recursion in programming is using a function to call itself to solve a problem. The example given in every lecture on computer science is calculating the factorial of a number. We can't think of a better example so let's go with that.

What is a factorial? Well we are glad you asked. The factorial of an integer n greater than 0 (designated by n!) is the product of all positive integers less than or equal to n. So factorial five is:

5! = 5 x 4 x 3 x 2 x 1 = 120

Note that factorial zero is defined as 1. The iterative solution to a factorial function, could look something like this:


-- Given a number 'n' calculate its factorial the iterative way

function factorial(n)

    if n == 0
        return 1

    local temp = 1

    for i = 1, n do
        temp = temp * i
    end

    return temp

end


And the recursive version:


-- Given a number 'n' calculate its factorial the recursive way

function factorial(n)

  if n == 0 then
      return 1
  else
      return n * factorial(n - 1)
  end

end


The recursive version will be marginally slower, it is a bit shorter to code, and it is a bit simpler and closer to the mathematical definition of recursion. For very large values of n, the recursive function will crash due to a stack overflow. Every time a recursive call is made, the function clones itself and pushes the previous function onto the stack. You can only do this so many times before running out of memory.

There are two rules for using recursion:
  1. If you use recursion then you must always have a base case which stops your code from calling itself forever. This won't happen of course, instead your program will crash with a stack overflow when it runs out of memory. The base case for the factorial function is n == 0.
  2. Your function must also make progress towards your base case or you will be caught in infinite recursion and crash. In the factorial example, each recursive call decrements n, so eventually it will get to the base case of zero.
The advantage of a recursive solution is its simplicity and elegance, the disadvantage can be the speed and the amount of memory used if the depth of recursion is large.

The MineSweeper game uses recursion to reveal the neighbouring cells if you tap a cell with no neighbouring mines. Have a look at the revealCell() function in the Main class.


Interlude 7.3 Tail Recursion



@gunnar_z had the following to add on the topic of recursion: 

"Lua supports something called tail recursion, or tail calls. The idea is that if the calling function does not actually do anything else but return to its caller after the called function has returned, the call is basically replaced by a jump and the current stack frame is reused. That is, the call needs to have the form "return fun(args)". With a bit of thinking, this can be made to work with a lot of recursive problems. For your example (factorial), it might look like this:

function fact(n, s)

    s = s or 1
    if n == 0 then
        return s
    else
        return fact(n-1, s*n)
    end

end

Recursion is a bit slower than iteration for trivial problems, but that is hardly noticable. For non-trivial problems (for example a recursive vs. iterative implementation of a quicksort or a tree traversal algorithm), this difference in speed shrinks rapidly, as you (may) need a stack for them anyway, and it may even be faster to implicitly use the stack provided by the language runtime through recursion than to simulate your own."


Interlude 7.4 Closures


When a function is enclosed in another function, then it has access to all the local variables of that function. This is called lexical scoping and the external local variable is called an "upvalue". 

To understand how this is useful, consider if we wanted to create a calculator application. This has a number of digit buttons that we display on the screen.


function digitButton (digit) 
    return Button{ label = digit, action = function () add_to_display(digit) end } 
end


In this example, we assume that Button is a class that creates new buttons; label is the button label; and action is the callback function to be called when the button is pressed. (It is actually a closure, because it accesses the upvalue digit.)

In our MineSweeper game, the inNeighbourCells() function uses closures to access the cell index upvalues and count the number of mines in neighbouring cells.

Another use for closures is to produce an iterator.


function makeIterator()

    local n = 0

    function iterator()
        n = n + 1
        return n
    end

    return iterator

end

-- Make two different iterator's

iterator_a = makeIterator()
iterator_b = makeIterator() 

print(iterator_a())         -- Will print 1
print(iterator_a())         -- Will print 2
print(iterator_b())         -- Will print 1
print(iterator_a())         -- Will print 3
print(iterator_b())         -- Will print 2


Once again we have a function within a function. makeIterator() is a constructor of iterator()'s and every time iterator() gets called it can see the upValue n and increments it.

@gunnar_z contributed the following additional information on closures:

"Closures are created whenever a function is created in a lexical block, not only within functions. When a function is created in a do .. end block, or even within a loop, it creates a closure. Also, all functions created within a file (or a tab in the codea world) are closures existing in the lexical context of that file (or tab). Btw. some time ago the scoping of the 

for i=1,n do ... end

construct was changed to create a new scope for every iteration. Thus, the following:

t={}

for i=1,10 do
    t[i] = function() print(i) end

end

will create a table with 10 functions, each one printing the value of i during its iteration."

Tuesday, July 10, 2012

Tutorial 6 - MineSweeper Part 1 (Updated 23/01/16)


6.1 Game Design


To further illustrate the many uses of Finite State Machines (FSM) we will deconstruct a simple game called MineSweeper. This game will illustrate a bunch of new concepts including recursive coding and closures as well as using a lot of the code that we developed in early tutorials.

Our MineSweeper code uses no less than three state machines to keep track of what is happening. The main state machine is illustrated above. This is a pretty standard pattern for most games. The goal was to produce the entire game using just an iPad. 

The aim of MineSweeper is to clear the minefield represented by a square grid without tapping a mine. When the splash screen fades away you will be presented with a menu of three buttons: Easy, Medium and Hard which you can use to select the game difficulty and launch the game. 



Game difficulty is tracked using a state machine and is used when the New button is tapped on the game screen to ensure we launch another game at the same difficulty level as the current game. The variables effected by difficulty are shown in the following table.


Game Difficulty
Easy Medium Hard
Number of Mines 10 15 40
Grid Width 8 12 16
Grid Height 8 12 16


Once you select the game difficulty by tapping on the appropriate button, you will be presented by the main game screen. The blue grid represents the minefield. The number on the left above the minefield is the number of cells left to clear. The number on the right is the elapsed time in seconds (approximately). The game timer will start as soon as you tap a minefield cell. Winning, losing or tapping the New or Menu buttons will stop the game timer.

The button in the top right will initially have "Show" as its button text. This indicates that you are in Show mode when you tap the grid. Show mode will reveal the cell contents. This will either be blank, a number or a mine. If it is a mine then the game is over. If the cell is blank it means that there are no mines in the neighbouring cells (each cell can have up to eight neighbours depending on its grid position). If the cell contains a number, this represents the number of mines in adjacent cells. You can use these numbers to deduce where the mines are located. If you determine that a cell contains a mine you can flag it by using Flag mode. You enter Flag mode by tapping the Show Button. When in Flag mode this button text will change to "Flag". Tapping it again will return it to Show mode. Tapping any unrevealed cell while in Flag mode will place a flag icon on that cell. This icon can be toggled on and off by tapping the same cell in Flag mode.

If you tap a blank cell in Show mode, the game will also reveal any other adjacent blank cells. This can reveal quite large areas depending on the mine disposition.

You don't have to use flags to indicate suspected mine locations if you don't want to. The game is harder if you don't use flags.

The game ends if you tap a mine (lose) or if you reveal all of the cells without mines (win).


6.2 Download the Code


If you just want to run MineSweeper and play the game then the easiest approach is to download the entire code in one file and paste it into the Main tab of a new project. If you are more interested in reusing the classes and understanding how it works then you are better off downloading the individual classes and pasting them into separate tabs. The order of the tabs can be important, have a look at this article on inherited classes if you want to know why.

We suggest that you use the same tab order as our project. The classes you need can be downloaded using the following links. Use these names for your tabs/class.

1. Main
2. Cell
3. Button
4. RoundBorder
5. SplashScreen
6. Fader
7. Twinkle
8. IconImages

We have already discussed a number of these classes (RoundBorder, SplashScreen, Fader and Twinkle) in previous tutorials so we won't be covering them here. We had previously been using the Button class provided with the Codea sample project "Sounds Plus", but we found this class took too much tweaking to make the various buttons look right. Consequently, we are now using the mesh button class developed by Vega. We tweaked this a little bit, adding callback functionality and push/popStyle to preserve the various graphic variables.

The following tutorial will delve into the MineSweeper Main and Cell classes, which is where most of the action is, but we did want to touch on developing the images.

6.3 Pixel Images in Spritely



The sprite images used to represent the table cells were done in Spritely, the pixel image editor provided with Codea. This was fun but you probably wouldn't want to do a lot of images with it. Drawing every sprite, pixel by pixel using your finger is an exercise in patience. A side effect of this approach is that the MineSweeper code is over 5,000 lines long. Most of this is Spritely images. Each 32x32 pixel image takes over 1,000 lines of code to define it. Now Spritely automatically generates this for you but you need to draw the images first. To make life easy we have included all the code (including images) which you can download from dropbox.


For future games we probably won't use Spritely images. The largest sprite that you can produce is 32x32 pixels. This is just big enough for my little fingers to tap reliably but I imagine some folks could have difficulty. We would have preferred to make the sprites a touch bigger for MineSweeper. The advantage of this method is that all the game assets are in one file which makes it easy to distribute, however this is offset by the 32 pixel size constraint and the long files required to represent each image. On my first generation iPad, editing the IconImages class (which contains the 4 sprites) was very slow and crashed frequently. If you are going to use Spritely, a separate tab for each image is probably the way to go and don't forget to back up your code. The easiest way to backup is to tap and hold on your project icon in the project explorer screen of Codea and then tap copy. We then paste and email the resulting file, clean it up in TextWrangler and finally save it in DropBox.

But this does demonstrate that the iPad is moving from a content consumption device to a content generation device. How amazing is it to be able to code and produce graphics on this fantastic tablet?

Stay tuned for part 2 of the MineSweeper Tutorial...


Friday, June 29, 2012

Interlude 4 - A Rounded Border Class (Updated 17/01/16)


Interlude 4.1 Yes it is a bit of a Hack...


For our splash screen Tutorial we wanted to include a nice rounded border. To that end the following extends the roundRect() function that we have used in previous tutorials (to create a button). There are much more elegant ways to achieve this (e.g. using a mesh) but this has the advantage of simplicity. Once we have covered meshes we will rewrite this class. Another alternative would be to extend the roundRect() function to include a variable which determines if the roundRect is filled or not.


Interlude 4.2 The Rounded Border Class


This class relies on the roundRect() function, so you must include that to use RoundBorder(). The concept is very simple. We just draw two rounded rectangles, one inside the other. The inner rectangle is smaller by a factor of borderWidth. The larger rectangle is drawn using the borderColour and the inner using the fillColour.



RoundBorder = class()

function RoundBorder:init(x, y, w, h, borderWidth, borderColour, fillColour)

    -- you can accept and set parameters here
    -- Set dimensions of the outer "border" round rectangle

    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.r = 30

    self.borderWidth = borderWidth
    self.borderColour = borderColour
    self.fillColour = fillColour
    
    -- Set dimensions of the inner "background" round rectangle

    inX = self.x + self.borderWidth
    inY = self.y + self.borderWidth
    inW = self.w - 2 * self.borderWidth
    inH = self.h - 2 * self.borderWidth
end

function RoundBorder:draw()
    -- Codea does not automatically call this method
    -- Do the right thing and save the graphic context
    
    pushStyle()
    
    -- Start by drawing the outer Rounded Rectangle, this will become the border.
    
    fill(self.borderColour)
    roundRect(self.x, self.y, self.w, self.h, self.r)
    
    -- Then draw the inner rectangle using the fill colour
    
    fill(self.fillColour)
    roundRect(inX, inY, inW, inH, self.r)
    
    -- Return the graphic context to the way it was when entering this function
    
    popStyle()
end