In part 1 of this tutorial we got to the stage of being able to start a new game and the board being drawn, in this part we will continue where we left off.>
Then, the solution is to redraw each brick at start of each new game. The following procedure will help us:
procedure RedrawCells; var i, j : integer; begin for i := 0 to frmMain.DrawGrid.ColCount-1 do for j := 0 to frmMain.DrawGrid.RowCount-1 do frmMain.DrawGridDrawCell(frmMain, i, j, frmMain.DrawGrid.CellRect(i,j), ) end;
And the code for itemNewGameClick procedure should be changed as follows:
procedure TfrmMain.itemNewGameClick(Sender: TObject); begin AssignBrickColors; RedrawCells; end;
Now try it again. For this time, the appearance will change at each click on "New Game".
The algorithm is simple:
If the clicked brick has at least one neighbour of the same color (neigbour can be one of the four bricks around it: namely up, down, left and right bricks. Not the diagonals), Remove this brick; Apply this recursively to the neighbours of this cell those had the same colors with it
This means that a single cell that have no neigbours with the same color cannot be removed.
Before all, let's define the color of the wall, because when a brick is removed, the emptied cell will take the color of the wall:
const WALL_COLOR : TColor = clWhite;
Here I assumed that the wall will be white. You can select any other color you wish, but it should be different from all of the colors in our PossibleColors array.
Now, let's write our recursive procedure to remove the bricks properly:
procedure RemoveBricks (cl : TColor; ACol, ARow : integer); begin // If the given color does not match with the color of the current brick, // Exit without doing anything if ColorOfBrick[ACol, ARow] <> cl then exit; // Remove the brick, by making its color // with the same as the color of the wall ColorOfBrick[ACol, ARow] := WALL_COLOR; // Now, apply this to its neighbours recursively // But don't forget that this cell might be a border cell, // so that it may not have one of the left, right, up or down neighbours // left neighbour if ACol > 0 then RemoveBricks(cl, ACol-1, ARow); // right neighbour if ACol < frmMain.DrawGrid.ColCount-1 then RemoveBricks(cl, ACol+1, ARow); // upper neighbour if ARow > 0 then RemoveBricks(cl, ACol, ARow-1); // down neighbour if ARow < frmMain.DrawGrid.RowCount-1 then RemoveBricks(cl, ACol, ARow+1); end;
When should we call this recursive procedure? We should call it when a clicked brick has at least one neighbour with the same color, that is, when the brick is not alone. That is, it would be good to have a function that checks whether a brick is alone or not:
function IsBrickAlone(ACol, ARow : integer) : Boolean; begin // Initially assume that it is alone. Result := True; // If there is not a brick on the clicked cell, // exit without doing nothing. This means that // an empty cell on the wall will be interpreted to be alone. if ColorOfBrick[ACol, ARow] = WALL_COLOR then exit; // Now, check all of its neighbours whether at least one of them // has the same color. Don't forget to check whether the cell // is a border cell or not. If it is a border cell, we can get // a memory violation error when trying to check one of its non-existent // neighbours. // Check the left neighbour if ACol > 0 then if ColorOfBrick[ACol-1, ARow] = ColorOfBrick[ACol, ARow] then Result := False; // Check the right neighbour if ACol < frmMain.DrawGrid.ColCount-1 then if ColorOfBrick[ACol+1, ARow] = ColorOfBrick[ACol, ARow] then Result := False; // Check the upper neighbour if ARow > 0 then if ColorOfBrick[ACol, ARow-1] = ColorOfBrick[ACol, ARow] then Result := False; // Check the down neighbour if ARow < frmMain.DrawGrid.RowCount-1 then if ColorOfBrick[ACol, ARow+1] = ColorOfBrick[ACol, ARow] then Result := False end;
Now we can use this function when a brick is selected by the player. If it is not alone, the recursive procedure will be applied:
procedure TfrmMain.DrawGridSelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); begin // Check if the cell is alone or not. If it is, we will do nothing // according to the game rule, but it is not alone, we should remove // that area from the wall. We do this by calling our recursive procedure // RemoveBricks. // If it is not alone, remove the block if not IsBrickAlone(ACol, ARow) then begin RemoveBricks(ColorOfBrick[ACol, ARow], ACol, ARow); // Redraw the cells RedrawCells end end;
Now, run your application and then click on a region. You will see that the region disappears and it takes the color of the wall. Notice that an alone brick cannot be removed when clicked.
Will turn into
This concludes this part of the tutorial, in the third installment we will shift all the bricks along to fill the newly created gap.
If you enjoy addictive games you should probably check out Game Addicts, a blog on all the latest gaming news.