Spiral 4 – implementing the rules

But wait, our game can only work if the same color only appears in two cells: a cell and its twin cell. Moreover, a cell can be hidden or not, that is, the color can be seen or not? To take care of this, the Cell class gets two new attributes:

  Cell twin;
  bool hidden = true;

Note

For code files of this section, refer to chapter 7educ_memory_gamespiralss04 in the code bundle.

The method _colorBox in the Board class can now show the color of the cell when hidden is false (line (2)); when hidden = true (the default state) a neutral gray color will be used for that cell (line (1)):

  static const String COLOR_CODE = '#f0f0f0';

We also gave the gap variable a better name, boxSize:

void _colorBox(Cell cell) {
    var x = cell.column * boxSize;
    var y = cell.row * boxSize;
    context.beginPath();
    if (cell.hidden) {
      context.fillStyle = COLOR_CODE;                     (1)
    } else {
      context.fillStyle = colorMap[cell.color];           (2)
    }
// same code as in Spiral 3
}

The lines (1) and (2) can also be stated more succinctly with the ternary operator ?. Remember that the drawing changes because the method _colorBox is called via draw at 60 frames per second and the board can react to a mouse click. In this spiral, we will show a cell when it is clicked together with its twin cell and then they stay visible. Attaching an event handler for this is easy. We add the following line to the Board constructor:

querySelector('#canvas').onMouseDown.listen(onMouseDown);

The onMouseDown event handler has to know on which cell the click occurred. The mouse event e contains the coordinates of the click in its e.offset.x and e.offset.y (lines (3) and (4) beneath). We obtain the cell's row and column by using a truncating division ~/ operator dividing the x (which gives the column) and y (which gives the row) values by boxSize:

void onMouseDown(MouseEvent e) {
    int row = e.offset.y ~/ boxSize;                  (3)
    int column = e.offset.x ~/ boxSize;               (4)
    Cell cell = memory.getCell(row, column);          (5)
    cell.hidden = false;                              (6)
    cell.twin.hidden = false;                         (7)
} 

Memory has a collection of cells. To get the cell with a specified row and column value, we add a getCell method to memory and call it in line (5). When we have the cell, we set its hidden property and that of its twin cell to false (lines (6) to (7)). The getCell method must return the cell at the given row and column. It loops through all the cells in line (8) and checks each cell whether it is positioned at that row and column (line (9)). If yes, it returns that cell:

Cell getCell(int row, int column) {
    for (Cell cell in cells) {                 (8)
      if (cell.intersects(row, column)) {      (9)
        return cell;
      }
    }
}

For this purpose, we add an intersects method to the Cell class. This checks whether its row and column match the given row and column for the current cell (see line (10)):

bool intersects(int row, int column) {
    if (this.row == row && this.column == column) {    (10)
      return true;
    }
    return false;
}

Now, we have already added a lot of functionality, but the drawing of the board needs some more thinking:

  • How to give a cell (and its twin cell) a random color that is not yet used?
  • How to attach randomly a cell to a twin cell that is not yet used?

To this end, we will have to make the constructor of Memory a lot more intelligent:

Memory(this.length) {
    if (length.isOdd) {                               (1)
      throw new Exception(
          'Memory length must be an even integer: $length.'),
    }
    cells = new Cells();
    var cell, twinCell;
    for (var x = 0; x < length; x++) {
      for (var y = 0; y < length; y++) {
        cell = getCell(y, x);                         (2)
        if (cell == null) {                           (3)
          cell = new Cell(this, y, x);
          cell.color = _getFreeRandomColor();         (4)
          cells.add(cell);
          twinCell = _getFreeRandomCell();            (5)
          cell.twin = twinCell;                       (6)
          twinCell.twin = cell;
          twinCell.color = cell.color;
          cells.add(twinCell);
        }
      }
    }
}

The number of pairs given by ((length * length) / 2) must be even. This is only true if the length parameter of Memory itself is even, so we check that in line (1). Again, we code a nested loop and we get the cell at that row and column. But when the cell at that position has not yet been made (line (3)), we continue to construct it and assign its color and twin. In line (4), we call _getFreeRandomColor to get a color which is not yet used:

String _getFreeRandomColor() {
    var color;
    do {
      color = randomColor();
    } while (usedColors.any((c) => c == color));             (7)
    usedColors.add(color);                                   (8)
    return color;
}

The do…while loop continues as long as the color is already in a list of usedColors (this is elegantly checked with the functional any from Chapter 3, Structuring Code with Classes and Libraries). On exiting from the loop, we found an unused color, which is added to usedColors in line (8) and also returned. Then, we have to set everything for the twin cell. We search for a free one with the _getFreeRandomCell method in line (5). Here, the do…while loop continues until a (row, column) position is found where cell == null, meaning we haven't yet created a cell there (line (9)). We promptly do this in line (10):

Cell _getFreeRandomCell() {
    var row, column;
    Cell cell;
    do {
      row = randomInt(length);
      column = randomInt(length);
      cell = getCell(row, column);
    } while (cell != null);                               (9)
    return new Cell(this, row, column);                  (10)
}

From line (6) onward, the properties of the twin cell are set and it is added to the list. That's all we need to produce the following result:

Spiral 4 – implementing the rules

Paired colored cells

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.93.169