Programming a Memory Game in Delphi - Part 2 - Starting to Code

'

Recap

In part 1 of Programming a Memory Game we explained how to create the form and drawgrid at design time. Now we will move onto writing the actual code.

Keeping the Images to be Drawn

Since we have 20 cells on our grid, we need 10 distinct images.

As an application developer, it's up to you to consider this trade off and to make the best choice. But for simplicity, I will use the second one. That is, I will automatically load the images at the beginning and keep all of the images in memory till the end of the application. This is reasonable when your application uses a small number of images with reasonable sizes.

For this purpose, we will need two global arrays:

var
	ImagePaths : array [0..9] of string;
	Images : array [0..9] of TImage;

The following procedure will help us load the images:

procedure LoadImages;
var
  i : integer;
begin
  for i := 0 to 9 do
  begin
    Images[i] := TImage.Create(nil);
    Images[i].Picture.LoadFromFile(ImagePaths[i])
  end
end;

We will call this procedure only once, just on the creation of the main form.

NOTE: You have to assign the image paths before calling this procedure. And you'd better handle exceptions such as non-existent files, etc. Furthermore, your images should have one of the supported image extensions, such as .bmp. But for the sake of simplicity, I skip such kind of details and assume that all of the images will exist in the same directory with the application and all of them will be bitmap images.

Thus, I will change my ImagePaths array variable declaration as follows:

ImagePaths : array [0..9] of string
  = ('img0.bmp', 'img1.bmp', 'img2.bmp', 'img3.bmp', 'img4.bmp', 'img5.bmp',
        'img6.bmp', 'img7.bmp', 'img8.bmp', 'img9.bmp');

Make sure that these images exist under your application directory.

Once we ensure that the images exist and have appropriate extensions, we can call the LoadImages procedure on creation:

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  LoadImages
end;

Coupling the Cells Randomly

The easiest way to randomize the couples is to randomize a permutation array.

Since we have 20 cells, we will have an array of integers, where each integer value keeps the index of a cell. But wait a minute... Our cells are indexed by 2 integers, namely one row and one column value. How can we represent them with a single index? It is easy. We can think of our double-indexed cell array as a single-indexed linear array. For this purpose, we will use the following function:

function LinearIndexOf (Row, Column : integer) : integer;
begin
  Result := 5 * Row + Column // Returns the index of the given cell
end;

Here, we multiply by 5 because there are 5 cells on each row.

Now, we will declare our random permutation array (globally):

var
  RandomPermutationArray : array [0..19] of integer;

We have to initialize this array once, in the creation of the application. Thus, OnCreate event of our main form will be as follows:

procedure TfrmMain.FormCreate(Sender: TObject);
var
  i : integer;
begin
  LoadImages;

  for i := 0 to 19 do
    RandomPermutationArray[i] := i
end;

And the following procedure will randomize this array at each call to this procedure:

procedure RandomizeThePermutationArray;
var
  i, RandomPosition, Temp : integer;
begin
  for i := 0 to 18 do
  begin
    RandomPosition := i + Random(19 - i) + 1;
    Temp := RandomPermutationArray[i];
    RandomPermutationArray[i] := RandomPermutationArray[RandomPosition];
    RandomPermutationArray[RandomPosition] := Temp
  end;
end;

This procedure is an easy-to-understand and a good way of randomizing a permutation array.

It just does the following:

For each element in the array except the last one, it selects randomly one of the elements to the right of this element and swaps them.

NOTE: You can use this nice method in your other applications whenever you need randomization.

How will we make use of this array in our application?

Suppose that after a call for randomization, our array became as follows:

19 - 6 - 7 -10 - 0 - 12 - 5 - 1 - 11 - 8 - 2 - 3 - 16 - 15 - 17 - 18 - 4 - 9 - 14 - 13

This will mean in our application that the cells with linear indexes 19 & 6 are couples and they share the first image, 7 & 10 are couples and they share the second image, 0 & 12 are couples and they share the third image, etc.

Now, we also need to keep the information of which cell constitutes a couple with which cell. For this purpose, we will use the following array:

var
  PartnerOf : array [0..19] of integer;

And the following procedure will assign the partnerships:

procedure AssignPartnerships;
var
  i : integer;
begin
  for i := 0 to 19 do
    if i mod 2 = 0 then
      PartnerOf[RandomPermutationArray[i]] := RandomPermutationArray[i + 1]
    else
      PartnerOf[RandomPermutationArray[i]] := RandomPermutationArray[i - 1]
end;

This procedure couples two elements next to each other at each time.

We also need to keep the information of which cell will display which image. Therefore, we need to declare the following variable:

var
  ImageOfCell : array [0..19] of integer;

And the following procedure assigns the images to the cells, using the random permutation array:

procedure AssignImagesToCells;
var
  i : integer;
begin
  for i := 0 to 19 do
    ImageOfCell[RandomPermutationArray[i]] := i div 2
end;

This procedure assigns the 0th image to the first couple, 1st image to the second couple, and so on.

We have to call the followings at start of each new game:

NOTE: We used the built-in function Random() in the RandomizeThePermutationArray procedure. But before using it, we have to call the built-in Randomize procedure once, in order to assign the randomization seed. We can call it in the OnCreate event of the main form:

procedure TfrmMain.FormCreate(Sender: TObject);
var
  i : integer;
begin
  LoadImages;

  for i := 0 to 19 do
    RandomPermutationArray[i] := i;

  Randomize
end;

Starting a New Game

Let's add a main menu to our application and add an item with a caption "New Game" to it. I will name this item as itemNewGame. When clicked, this item will start a new game.

Delphi Game with menu

We should call the three procedures mentioned above in the OnClick event of this item in order to make the necessary initializations

procedure TfrmMain.itemNewGameClick(Sender: TObject);
begin
  RandomizeThePermutationArray;
  AssignPartnerships;
  AssignImagesToCells
end;

In the third part of this tutorial we will move onto drawing the playing board using the DrawGrid component, if you wish to be informed when part 3 is available please send us your email address via the update section on the left of this page.

Delphi Tutorial - Programming a Memory Game - Part 1
Programming a Memory Game in Delphi - Part 2 - Starting to Code
Programming a Memory Game in Delphi - Part 3 - Drawing the Grid
Programming a Memory Game in Delphi - Part 4 - Coding the Algorithm


Google
Web www.Delphi-Central.com
Delphi Central - Delphi Programming Tutorials, Hints and Tips