Learning 13: Coding strategies

This page describes strategies for coding with Perlenspiel, using a simple game as an example.

 

Getting started with Goldgrabber

Let’s build a collecting game, Goldgrabber, in which the player moves around a maze of walls collecting gold pieces.

Initial design

We’ll begin by setting up the grid and perimeter walls, and placing the player on the grid. Run the demo to see the initial result.

[Run Demo]

// The global G variable creates a namespace
// for game-specific code and variables

// It is initialized with an immediately-invoked
// function call (described below)

var G = ( function () {
 // By convention, constants are all upper-case

 var WIDTH = 15; // width of grid
 var HEIGHT = 15; // height of grid

 var COLOR_GRAB = PS.COLOR_GREEN; // grabber color
 var COLOR_FLOOR = PS.COLOR_WHITE; // floor color
 var COLOR_WALL = PS.COLOR_BLACK; // wall color

 // The following variables are grab-related,
 // so they start with 'grab'

 var grab_x = 2; // current x-pos of grabber
 var grab_y = 2; // current y-pos of grabber

 // The 'exports' object is used to define
 // variables and/or functions that need to be
 // accessible outside this function.
 // So far, it contains only one property,
 // an 'init' function with no parameters.

 var exports = {

 // G.init()
 // Initializes the game

 init : function () {
 PS.gridSize( WIDTH, HEIGHT ); // init grid
 PS.color( PS.ALL, PS.ALL, COLOR_FLOOR );
 PS.border( PS.ALL, PS.ALL, 0 ); // no borders

 // Enclose edges of grid with black walls.
 // PS.ALL lets us draw the top, bottom,
 // left and right edges with a single
 // PS.color() call for each edge.

 PS.color( PS.ALL, 0, COLOR_WALL );
 PS.color( PS.ALL, HEIGHT - 1, COLOR_WALL );
 PS.color( 0, PS.ALL, COLOR_WALL );
 PS.color( WIDTH - 1, PS.ALL, COLOR_WALL );

 // Place grabber at initial position

 PS.color( grab_x, grab_y, COLOR_GRAB );
 }
 };

 // Return the 'exports' object as the value
 // of this function, thereby assigning it
 // to the global G variable. This makes
 // its properties visible to Perlenspiel.

 return exports;
} () );

// Tell Perlenspiel to use our G.init() function
// to initialize the game

PS.init = G.init;

The code above illustrates a clean, professional way to organize your Perlenspiel projects. Take a few moments to understand how it works, and why you ought to emulate its structure.

It begins by declaring a global namespace variable, here called G. This single variable is used to expose any game code that Perlenspiel needs to reference. You could call this variable GOLDGRABBER if you wanted to, but since you’ll be typing it hundreds of times while developing your code, it’s best to keep it as short as possible.

The value of G is assigned using a curious but extremely powerful JavaScript idiom known as an immediately-invoked function (IIF). It’s an ordinary function definition, wrapped in parentheses, and appended with an empty parameter list (the pair of closed parentheses at the bottom). That parameter list causes the associated function to be executed immediately as it is loaded. In this case, the value returned by the function is assigned to the global G variable.

Why do this? Because global variables are evil. They make your code brittle, and vulnerable to insidious bugs caused by variable name clashes. You want to define as few global variables as possible. Immmediately-invoked functions provide an excellent way to accomplish this.

A JavaScript function behaves like a firewall. Anything defined inside a function can only be accessed inside that function unless it is deliberately exposed by returning a value or calling another function. By encapsulating your game code within a function, you’re free to use any variable or function names you want, without having to worry about clashing with code outside the function. This is especially helpful in web applications, where JavaScript code often needs to coexist with many different libraries.

Inside the immediately-invoking function, an exports variable is declared as an object with various properties. This exports object is returned as the value of the function and assigned to the G variable. Because G is declared globally, exports acts like a “window” into the immediately-invoked function, exposing only those variables and functions that Perlenspiel needs to “see.”

We could use initialize our game by using Perlenspiel’s PS.init() event to call the G.init() function, like this:

PS.init = function( system, options ) {
 G.init();
};

But because the functionality of G.init() is completely self-contained, we can just assign it to PS.init() so it is called directly by Perlenspiel.

PS.init = G.init;

Simple and clean. And only one global variable. Minimized evil.

 

Moving the grabber around

Our grabber is in position on the grid. We want it to move around in response to the arrow/WASD keys. To accomplish this, we’ll use Perlenspiel’s PS.keyDown() event handler, which is called whenever a key is pressed on the keyboard.

Below is a basic code template for responding to the arrow/WASD keys. Feel free to cut and paste this code into your Perlenspiel projects.

// Generic template for sensing arrow/WASD keys.
// NOTE: Handles both lower- and upper-case letters.

PS.keyDown = function( key, shift, ctrl, options ) {
 switch ( key ) {
 case PS.KEY_ARROW_UP:
 case 119:
 case 87: {
 // Code to move things UP
 break;
 }
 case PS.KEY_ARROW_DOWN:
 case 115:
 case 83: {
 // Code to move things DOWN
 break;
 }
 case PS.KEY_ARROW_LEFT:
 case 97:
 case 65: {
 // Code to move things LEFT
 break;
 }
 case PS.KEY_ARROW_RIGHT:
 case 100:
 case 68: {
 // Code to move things RIGHT
 break;
 }
 }
};

To keep things clean and non-evil, let’s encapsulate the actual movement code into our private G namespace by adding a new function called move() to the exports object. The move() function expects two numeric arguments, h (horizontal) and v (vertical), which specify if/how the grabber should move in either dimension:

// G.move( h, v )
// Moves the grabber around the map
// h : integer = horizontal movement
// v : integer = vertical movement
// h = 0: No horizontal movement
// h = 1: Move one bead to the right
// h = -1: Move one bead to the left
// v = 0: No vertical movement
// v = 1: Move one bead down
// v = -1: Move one bead up

To make the grabber follow the keyboard, all we have to do is look at which direction key the player has pressed, update the grabber’s location, and change the corresponding bead to the grabber’s color. Right?

Run the code below, touching the arrow keys to see what happens.

NOTE: Only the relevant sections of changed/new code are shown below and in most subsequent examples.

[Run Demo]

// Naive code for moving the grabber

var exports = {
 move : function ( h, v ) {
 grab_x += h; // update grabber's x-pos
 grab_y += v; // update grabber's y-pos
 PS.color( grab_x, grab_y, COLOR_GRAB );
 }
};

PS.keyDown = function( key, shift, ctrl, options ) {
 switch ( key ) {
 case PS.KEY_ARROW_UP:
 case 119:
 case 87: {
 G.move( 0, -1 ); // move UP (v = -1)
 break;
 }
 case PS.KEY_ARROW_DOWN:
 case 115:
 case 83: {
 G.move( 0, 1 ); // move DOWN (v = 1)
 break;
 }
 case PS.KEY_ARROW_LEFT:
 case 97:
 case 65: {
 G.move( -1, 0 ); // move LEFT (h = -1)
 break;
 }
 case PS.KEY_ARROW_RIGHT:
 case 100:
 case 68: {
 G.move( 1, 0 ); // move RIGHT (h = 1)
 break;
 }
 }
};

Oops. The grabber is leaving a green trail everywhere it goes. It’s also ignoring the walls, and causing error messages when it tries to move off the grid.

Looks like we need to:

The following additions to the move() function handle this.

[Run Demo]

var exports = {
 move : function ( h, v ) {
 var nx, ny;

 // Calculate proposed new location.

 nx = grab_x + h;
 ny = grab_y + v;

 // Is there a wall in the proposed location?
 // If the bead there is COLOR_WALL (black),
 // exit without moving.

 if ( PS.color( nx, ny ) === COLOR_WALL ) {
 return;
 }

 // Is new location off the grid?
 // If so, exit without moving.
 // NOTE: Current map design would never
 // allow grabber to get past the edge walls.
 // This code will prevent errors if
 // the map layout is changed.

 if ( ( nx < 0 ) || ( nx >= WIDTH ) ||
 ( ny < 0 ) || ( ny >= HEIGHT ) ) {
 return;
 }

 // Legal move, so change current grabber
 // location to floor color.

 PS.color( grab_x, grab_y, COLOR_FLOOR );

 // Assign grabber's color to the
 // new location.

 PS.color ( nx, ny, COLOR_GRAB );

 // Finally, update grabber's position

 grab_x = nx;
 grab_y = ny;
 }
};

PS.keyDown = function( key, shift, ctrl, options ) {
 switch ( key ) {
 case PS.KEY_ARROW_UP:
 case 119:
 case 87: {
 G.move( 0, -1 ); // move UP (v = -1)
 break;
 }
 case PS.KEY_ARROW_DOWN:
 case 115:
 case 83: {
 G.move( 0, 1 ); // move DOWN (v = 1)
 break;
 }
 case PS.KEY_ARROW_LEFT:
 case 97:
 case 65: {
 G.move( -1, 0 ); // move LEFT (h = -1)
 break;
 }
 case PS.KEY_ARROW_RIGHT:
 case 100:
 case 68: {
 G.move( 1, 0 ); // move RIGHT (h = 1)
 break;
 }
 }
};

Notice how simple our PS.keyDown() handler is. Only one line of code — a call to G.move() — is required to handle each of the four movement directions. (We could simplify the code even more with some sneaky programming tricks, but this version is clearer.)

Also notice that G.init() and G.move() are the only functions exposed to Perlenspiel by the G variable. All other game state and functionality are safely encapsulated inside the immediately-invoked function.

 

Adding more walls

The grabber’s playing field is boring. More walls would make navigation more interesting.

There are many possible ways to add walls. The most obvious is to call PS.color() once for every wall location.

// Draw a wall from 3, 3 to 3, 11

PS.color( 3, 3, COLOR_WALL );
PS.color( 3, 4, COLOR_WALL );
PS.color( 3, 5, COLOR_WALL );
PS.color( 3, 6, COLOR_WALL );
PS.color( 3, 7, COLOR_WALL );
PS.color( 3, 8, COLOR_WALL );
PS.color( 3, 9, COLOR_WALL );
PS.color( 3, 10, COLOR_WALL );
PS.color( 3, 11, COLOR_WALL );

If you add this code to the end of G.init(), it works just fine. G.move() interprets the new black beads as walls, and prevents the grabber from moving into them.

You could continue this way, adding PS.color() calls for every wall bead, until you had a satisfying layout. But this brute-force method is verbose and awkward, making it hard to visualize and change your layout.

Instead, consider another approach:

// 15 x 15 map array
// 0 = wall, 1 = floor

var map = [
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];

// Draw layout based on map array

var draw_map = function () {
 var x, y, data;

 for ( y = 0; y < HEIGHT; y += 1 ) {
 for ( x = 0; x < WIDTH; x += 1 ) {
 data = map [ ( y * WIDTH ) + x ];
 if ( data === 0 ) {
 PS.color( x, y, COLOR_WALL );
 }
 }
 }
};

This 15x15 array provides a simple, easy-to-read way to define the game geography. Wall locations are indicated by 0s, and floor locations by 1s.

The draw_map() function uses the array data as a guide for placing walls on the grid. It is called by G.init(), replacing the hard-coded edge walls in the original code. You can easily alter the map’s layout by rearranging the pattern of 0s and 1s.

[Run Demo]

// The global G variable creates a namespace
// for game-specific code and variables

// It is initialized with an immediately-invoked
// function call (described below)

var G = ( function () {
 // By convention, constants are all upper-case

 var WIDTH = 15; // width of grid
 var HEIGHT = 15; // height of grid

 var COLOR_GRAB = PS.COLOR_GREEN; // grabber color
 var COLOR_FLOOR = PS.COLOR_WHITE; // floor color
 var COLOR_WALL = PS.COLOR_BLACK; // wall color

 // The following variables are grab-related,
 // so they start with 'grab'

 var grab_x = 2; // current x-pos of grabber
 var grab_y = 2; // current y-pos of grabber

 // 15 x 15 map array
 // 0 = wall, 1 = floor

 var map = [
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 ];

 // Draw layout based on map array

 var draw_map = function () {
 var x, y, data;

 for ( y = 0; y < HEIGHT; y += 1 ) {
 for ( x = 0; x < WIDTH; x += 1 ) {
 data = map [ ( y * WIDTH ) + x ];
 if ( data === 0 ) {
 PS.color( x, y, COLOR_WALL );
 }
 }
 }
 };

 var exports = {

 // G.init()
 // Initializes the game

 init : function () {
 PS.gridSize( WIDTH, HEIGHT ); // init grid
 PS.color( PS.ALL, PS.ALL, COLOR_FLOOR );
 PS.border( PS.ALL, PS.ALL, 0 ); // no borders

 // Call the map-drawing function

 draw_map();

 // Place grabber at initial position

 PS.color( grab_x, grab_y, COLOR_GRAB );
 },

 // G.move( h, v )
 // Moves the grabber around the map
 // h : integer = horizontal movement
 // v : integer = vertical movement
 // h = 0: No horizontal movement
 // h = 1: Move one bead to the right
 // h = -1: Move one bead to the left
 // v = 0: No vertical movement
 // v = 1: Move one bead down
 // v = -1: Move one bead up

 move : function ( h, v ) {
 var nx, ny;

 // Calculate proposed new location.

 nx = grab_x + h;
 ny = grab_y + v;

 // Is there a wall in the proposed location?
 // If the bead there is COLOR_WALL (black),
 // exit without moving.

 if ( PS.color( nx, ny ) === COLOR_WALL ) {
 return;
 }

 // Is new location off the grid?
 // If so, exit without moving.
 // NOTE: Current map design would never
 // allow grabber to get past the edge walls.
 // This code will prevent errors if
 // the map layout is changed.

 if ( ( nx < 0 ) || ( nx >= WIDTH ) ||
 ( ny < 0 ) || ( ny >= HEIGHT ) ) {
 return;
 }

 // Legal move, so change current grabber
 // location to floor color.

 PS.color( grab_x, grab_y, COLOR_FLOOR );

 // Assign grabber's color to the
 // new location.

 PS.color ( nx, ny, COLOR_GRAB );

 // Finally, update grabber's position

 grab_x = nx;
 grab_y = ny;
 }
 };

 return exports;
} () );

 

Adding the gold

Just as the wall positions can be represented in an array map, so can the positions of the ten gold pieces, as well as the initial position of the grabber.

In the revised map below, the value 2 represents the location of a gold piece, and the value 3 the initial position of the grabber. Because of the way movement is implemented, the grabber appears to consume gold pieces as it moves over them.

[Run Demo]

var WIDTH = 15; // width of grid
var HEIGHT = 15; // height of grid

var COLOR_GRAB = PS.COLOR_GREEN; // grabber color
var COLOR_FLOOR = PS.COLOR_GRAY; // floor color
var COLOR_WALL = PS.COLOR_BLACK; // wall color
var COLOR_GOLD = PS.COLOR_YELLOW; // gold color

// Color array to associate map values
// with above colors

var colors = [
 COLOR_WALL, COLOR_FLOOR, COLOR_GOLD, COLOR_GRAB
];

// 15 x 15 map array
// 0 = wall, 1 = floor, 2 = gold, 3 = grabber

var map = [
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0,
 0, 1, 3, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 2, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 1, 1, 0,
 0, 1, 2, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 0,
 0, 2, 1, 0, 1, 2, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];

// draw_map()
// Scans the map data and draws walls, floors,
// gold and grabber

var draw_map = function () {
 var x, y, data, color;

 for ( y = 0; y < HEIGHT; y += 1 ) {
 for ( x = 0; x < WIDTH; x += 1 ) {
 data = map [ ( y * WIDTH ) + x ];
 PS.color( x, y, colors[ data ] );
 }
 }
};

The above architecture makes it easy to change not only the layout of the walls and location of the gold pieces, but also the colors of every game element, including the floor. Even the dimensions of the playing area can be quickly changed by creating a new map array and modifying the values of the WIDTH and HEIGHT constants to match.

 

Making it random

Goldgrabber works now, but it’s boringly predictable. The grabber always starts at the same place, and the gold is always distributed the same way.

Our map array makes this fairly easy to fix. A few extra lines of code in draw_map() can place the grabber and gold pieces in random locations every time the game is played. While we’re at it, we can put the total number and value of the gold pieces into constants to make them easy to tweak.

By adding code to handle scoring and winning, a few sound effects, and instructions for the player, Goldgrabber becomes a complete game.

Here’s the full game source.

[Run Demo]

// The global G variable creates a namespace
// for game-specific code and variables

// It is initialized with an immediately-invoked
// function call (described below)

var G = ( function () {
 // By convention, constants are all upper-case

 var WIDTH = 15; // width of grid
 var HEIGHT = 15; // height of grid

 var COLOR_GRAB = PS.COLOR_GREEN; // grabber color
 var COLOR_FLOOR = PS.COLOR_WHITE; // floor color
 var COLOR_WALL = PS.COLOR_BLACK; // wall color
 var COLOR_GOLD = PS.COLOR_YELLOW; // gold color

 var GOLD_MAX = 10; // max number of gold pieces
 var GOLD_VALUE = 10; // value of each gold piece

 // The following variables are grab-related,
 // so they start with 'grab'

 var grab_x; // current x-pos of grabber
 var grab_y; // current y-pos of grabber

 var score = 0; // current score
 var gold_count = 0; // number of pieces grabbed

 // 15 x 15 map array
 // 0 = wall, 1 = floor

 var map = [
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 ];

 // This function finds a random floor
 // location on the map (the value 1)
 // and places an item there, returning the
 // x/y position of each placed object.
 // It's used to place the gold pieces
 // and the grabber.

 var find_floor = function ( item, color ) {
 var xpos, ypos, loc, data;

 do {
 xpos = PS.random( WIDTH ) - 1;
 ypos = PS.random( HEIGHT ) - 1;
 loc = ( ypos * WIDTH ) + xpos;
 data = map[ loc ]; // get map data
 } while ( data !== 1 ); // try again

 map[ loc ] = item; // place item
 PS.color( xpos, ypos, color ); // set color
 return { x : xpos, y : ypos }; // return x/y
 };

 // Draw layout based on map array

 var draw_map = function () {
 var x, y, data, i, pos;

 for ( y = 0; y < HEIGHT; y += 1 ) {
 for ( x = 0; x < WIDTH; x += 1 ) {
 data = map [ ( y * WIDTH ) + x ];
 if ( data === 0 ) {
 PS.color( x, y, COLOR_WALL );
 }
 }
 }

 // Randomly place gold pieces on map.
 // No need to record their x/y positions.

 for ( i = 0; i < GOLD_MAX; i += 1 ) {
 find_floor( 2, COLOR_GOLD );
 }

 // Randomly place grabber on floor
 // and save its x/y position

 pos = find_floor( 3, COLOR_GRAB );
 grab_x = pos.x;
 grab_y = pos.y;
 };

 // The 'exports' object is used to define
 // variables and/or functions that need to be
 // accessible outside this function.

 var exports = {

 // G.init()
 // Initializes the game

 init : function () {
 PS.gridSize( WIDTH, HEIGHT ); // init grid
 PS.color( PS.ALL, PS.ALL, COLOR_FLOOR );
 PS.border( PS.ALL, PS.ALL, 0 ); // no borders

 draw_map();

 // Preload sound effects

 PS.audioLoad( "fx_click" );
 PS.audioLoad( "fx_coin7" );
 PS.audioLoad( "fx_tada" );

 PS.statusText( "Use arrows/WASD to grab gold" );
 },

 // G.move( h, v )
 // Moves the grabber around the map
 // h : integer = horizontal movement
 // v : integer = vertical movement
 // h = 0: No horizontal movement
 // h = 1: Move one bead to the right
 // h = -1: Move one bead to the left
 // v = 0: No vertical movement
 // v = 1: Move one bead down
 // v = -1: Move one bead up

 move : function ( h, v ) {
 var nx, ny;

 // Calculate proposed new location.

 nx = grab_x + h;
 ny = grab_y + v;

 // Is new location off the grid?
 // If so, exit without moving.

 if ( ( nx < 0 ) || ( nx >= WIDTH ) ||
 ( ny < 0 ) || ( ny >= HEIGHT ) ) {
 return;
 }

 // Is there a wall in the proposed location?
 // If the array data there = 0,
 // exit without moving.
 // If data = 2, it's gold, so update score.

 loc = ( ny * WIDTH ) + nx;
 data = map[ loc ];
 if ( data === 0 ) {
 return;
 }

 if ( data === 2 ) {
 map[ loc ] = 1; // Change gold to floor
 score += GOLD_VALUE;
 gold_count += 1;
 if ( gold_count >= GOLD_MAX ) {
 PS.statusText( "You win with $" +
 score + "!" );
 PS.audioPlay( "fx_tada" );
 }
 else {
 PS.statusText( "Score = $" + score );
 PS.audioPlay( "fx_coin7" );
 }
 }

 // Legal move, so change current grabber
 // location to floor color.

 PS.color( grab_x, grab_y, COLOR_FLOOR );

 // Assign grabber's color to the
 // new location.

 PS.color ( nx, ny, COLOR_GRAB );

 // Finally, update grabber's position

 grab_x = nx;
 grab_y = ny;
 }
 };

 return exports;
} () );

// Tell Perlenspiel to use our G.init() function
// to initialize the game

PS.init = G.init;

PS.keyDown = function( key, shift, ctrl, options ) {
 switch ( key ) {
 case PS.KEY_ARROW_UP:
 case 119:
 case 87: {
 G.move( 0, -1 ); // move UP (v = -1)
 break;
 }
 case PS.KEY_ARROW_DOWN:
 case 115:
 case 83: {
 G.move( 0, 1 ); // move DOWN (v = 1)
 break;
 }
 case PS.KEY_ARROW_LEFT:
 case 97:
 case 65: {
 G.move( -1, 0 ); // move LEFT (h = -1)
 break;
 }
 case PS.KEY_ARROW_RIGHT:
 case 100:
 case 68: {
 G.move( 1, 0 ); // move RIGHT (h = 1)
 break;
 }
 }
};

Once you understand how this code works, you can make all kinds of improvements. Some suggestions:

 

Terms to know

Next: Bead data, functions and visibility