tick method

void tick()

Implementation

void tick() {
  assert(!_gameCompleter.isCompleted, 'Cannot tick after game is over!');

  final snakeHeadPoint = _snake.last;
  final itemAtSnakeHead = _getItemAtPoint(snakeHeadPoint, _snakelessBoard);

  // Update the last used direction.
  _lastTickedDirection = _direction;

  final nextSnakeHeadPoint = _offsetPoint(snakeHeadPoint, _direction, 1);

  // If the snake head is not on food, remove the last segment.
  final isOnFood = itemAtSnakeHead == SnakeItem.food;

  // End the game if the next head point is off-screen.
  var endGame = nextSnakeHeadPoint.y < 0 ||
      nextSnakeHeadPoint.x < 0 ||
      nextSnakeHeadPoint.y > _snakelessBoard.length - 1 ||
      nextSnakeHeadPoint.x > _snakelessBoard[nextSnakeHeadPoint.y].length - 1;
  // End the game if the next head point collides with the snake body, taking
  // the last segment removal into account.
  endGame = endGame ||
      (_snake.contains(nextSnakeHeadPoint) &&
          (isOnFood || nextSnakeHeadPoint != _snake.first));
  // End the game without updating the board if any of the above conditions
  // are met.
  if (endGame) {
    _gameCompleter.complete();
    return;
  }

  // If the snake head is on food, remove the food.
  // Otherwise, remove the last segment.
  if (isOnFood) {
    _setItemAtPoint(SnakeItem.empty, snakeHeadPoint, _snakelessBoard);
    _ticksSinceLastAte = 0;
    _ticksTillNextFood = _calculateTicksTillNextFood();
  } else {
    _snake.removeFirst();
  }

  // Add the next head point to the snake.
  _snake.add(nextSnakeHeadPoint);

  // If the ticks since last ate is set to -1, food is on the board waiting
  // to be eaten. No further food-related action is necessary.
  if (_ticksSinceLastAte != -1) {
    if (_ticksSinceLastAte == _ticksTillNextFood) {
      // If the timeout is complete, add food to the board.
      _addFood(_random, _snakelessBoard, _snake);
      _ticksSinceLastAte = -1;
    } else {
      // Otherwise, increment the ticks since last ate.
      ++_ticksSinceLastAte;
    }
  }

  // Trigger a render.
  renderer();
}