Jump to content

Recommended Posts

First of Iam a beginner to coding, I had some basic lessons about Delphi, but my knowledge is little.

So, the main topic I want to ask:  how can you program a working memory game in Delphi. 

As to the structure Iam going to work with 20 timage components and 20 tbuttons, with the caption: ?.

Also, the project should contain a ttimer and start and exit button. 

Furthermore, I want to build an array so that the code for flipping of every card isn't endless.

Where should I start?

 

Lary Rose,

:D

 

Share this post


Link to post
Posted (edited)

Well, there are many ways to approach such a project. Using 20 images and 20 buttons may look appropriate at first glance but you have already identified the problem of code duplication and it is not the only one. Even getting the layout riight may be more work than you think.

If you have a number of identical objects think array, and for visual stuff think grid. If your target platform is Windows and you intend to use the VCL instead of the FMX framework I would use a TDrawGrid as the main UI. For the start and exit buttons use TButtons hosted on a TPanel aligned alBottom on the form. The TDrawgrid is placed above the panel and aligned alClient. With 20 images you probably want 4 rows of 5 images each, so set the rowcount and colcount to 4 and 5, respectively and the FixedColCount and FixedRowCount to 0. Add a handler for the grid's OnDrawCell event, that is where you place the code to draw button (for an image not uncovered yet) or image. Then you need a way to store the state (covered/uncovered) for each cell and an identifier for the image a cell is holding. To keep the info for a cell together the natural storage is a record, so add a type definition like this above the form class:

type
  TGameCell = record
    ImageIndex: Integer;
    Uncovered: Boolean;
  end;

The game state is then a 4x5 array of TGameCell:

const
  CNumRows = 4;
  CNumCols = 5;
type
  TGameState = array [0..CNumRows-1, 0..CNumCols-1] of TGameCell;

Add a field to the protected section of the form class:

  FGameState: TGameState;

Each element of the game state array corresponds to a cell in the drawgrid. OK so far.

Now you need some storage for the images. The most appropriate is a TImageList, so drop one on the form, set its Height and Width to the size of the images you want to use and then load the images into the list. The index of a given image will be used to identify it in the TGameCell. I asume with 20 cells you will have 10 images. Set the defaultcolwidth and defaultrowheight properties of the grid to values a bit larger than the images, say 4 pixels more. That gives you spacing between the tiles you have to draw.

 

OK so far. Add a handler to the form's OnCreate event. There you place the code to initialize the FGameState array. The Uncovered members of the TGameCell records will start out as false, so you only need to set the ImageIndex to specify which image to show in each cell, using the index from the imagelist.

 

That is the basic setup. Now you have to figure out how to draw the cells to resemble the usual memory game card. The event handler for the grid's OnDrawCell event has the following parameter list:

procedure (Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState)

Sender is the grid, you will have to draw on its Canvas, so add a variable for that to the handler sceleton the IDE created for you:

var
  LCanvas: TCanvas;
begin
  LCanvas := (Sender as TDrawgrid).Canvas;

ACol and ARow tell you which cell to draw and, conveniently, these directly correspond to the matching TGameCell indices in FGameState. If you leave the DefaultDrawing property of the grid at the default True the VCL will have already drawn the cell background, so you can concentrate on drawing the image or button. Which it is you determine by looking at FGameState[aRow, aCol].Uncovered. If true you use the imagelists Draw method, passing it the LCanvas and the Left and Top members of the Rect parameter, adding 2 for the spacing to both. To draw the button the simplest way would in fact be to add a suitable image to the imagelist as well and draw it the same way. An alternative would be the DrawFrameControl Windows API method, but that is much more complicated to use.

 

OK, so the form will now show the grid when you run the project (at designtime the grid is empty). Now you need to detect clicks on the cells, so add a handler to the grid's OnMouseDown event. Its X and Y parameter tell you where the mouse went down inside the grid but not which cell that is. Fortunately the grid has a MouseToCell method, so call that to get the cell, look at the Uncovered member of the corresponding TGameCell. If true exit directly, if false set it to true and call the grid's Invalidate method to get it to redraw. This would also be the place from which you then evaluate the game state, i. e. see if this action has uncovered the mate to the last tile uncovered (if so stop the timer), has uncovered the first tile (if so remember the cell coordinates and start the timer), and so on. If the timer fires you would just set both cells to Unconvered:= false, stop the timer, and Invalidate the grid to redraw.

 

OK enough loafing around! Now get to work :classic_cool:

Edited by PeterBelow
  • Like 1
  • Thanks 1

Share this post


Link to post

Hello.

 

It's more a question of algorithm than language, in my opinion.

 

Once you know how to organize the game, the display, the clicks on the elements, you'll have a multitude of solutions in Delphi.

 

As for me, I have a FireMonkey version with a game whose board changes size depending on the game mode chosen. It's called Pairpix and here are the source codes (open today to answer your questions): https://github.com/DeveloppeurPascal/Pairpix You can have a look at it if you lack inspiration, but it won't be compilable (the images and other elements aren't public for copyright reasons).

 

Generally speaking, a grid (a two-dimensional array) storing the number of the image and the images placed on the screen by looping over the grid (in X and Y) remains the simplest solution.

 

To fill the grid have a 1D array or a List where you put your pairs (two elements with same image number). Use a random index to get them when you fill the grid and remove each element of the list when it has its place in the grid.

 

Share this post


Link to post

This is what i have managed to do until know, but a lot of connections are missing and wrong. What would be the next corrections and steps to take? 

And sry for butting it into a word file it just seemed more practical at the moment.

Unit1 (2).pas

Screenshot 2024-03-05 143436.png

Share this post


Link to post
Posted (edited)

To find what is not connected, use the debugger.   Switch from the form view ~design tab to the code view and insert a breakpoint on a line that has a blue dot on it in left gutter. Using F5 will cover the blue with red dot if the project is in debug mode selected in manager.  

 

Test the Button click it should halt program at the program line selected. 

 

Test the DrawGrid ondraw event next. If the program doesn't break at the breakpoint, inspect if the Drawgrid1 event ondraw is assigned using the object inspector Events tag.  

 

 

Edited by Pat Foley
Needs at

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×