Newer
Older
Import / research / match3 / game.cpp
//  DescriptionHere - game.cpp
//  Created by John Ryland (jryland@xiaofrog.com), 29/10/2017
//  Copyright (c) 2017 InvertedLogic
//  All rights reserved.
#include <cstdio>
#include <cstdlib>
#include "game.h"

bool createNewGame(Game& a_game, int a_width, int a_height, int a_numTileTypes)
{
  bool okay = false;
  if (a_width == 0 || a_height == 0 || a_numTileTypes == 0)
    okay = createBoard(a_game.m_board, 0, 0);
  else
    okay = generatePlayableBoard(a_game.m_board, a_width, a_height, a_numTileTypes);
  if (okay)
  {
    printBoard(a_game.m_board);
    printf("hasAMatch3 returned %s\n", hasAMatch3(a_game.m_board) ? "true" : "false");
    printf("hasMoveAvailable returned %s\n", hasMoveAvailable(a_game.m_board) ? "true" : "false");
    a_game.m_time = 0;
    a_game.m_score = 0;
    a_game.m_tileTypes = a_numTileTypes;
    updateTiles(a_game);
  }
  if (!okay || !loadGameDB(a_game.m_gameDB))
    return false;
  return true;
}

void destroyGame(Game& a_game)
{
  destroyBoard(a_game.m_board);
}

bool processInput(Game& a_game, const Input& a_input)
{
  int x1 = a_input.startTileX;
  int y1 = a_input.startTileY;
  int x2 = a_input.endTileX;
  int y2 = a_input.endTileY;
  return swapTiles(a_game, x1, y1, x2, y2);
}

// normalize the game state
// directly translates the state of the board in to the tiles state so they are synced
void updateTiles(Game& a_game)
{
  a_game.m_boardTileMap.clear();
  a_game.m_tiles.clear();
  int idx = 0;
  for (int j = 0; j < a_game.height(); j++) {
    for (int i = 0; i < a_game.width(); i++) {
      Tile t;
      t.type = BoardEvent::None;
      t.userInit = false;
      t.newX = t.x = i;
      t.newY = t.y = j;
      t.tileTypeId = a_game.m_board.m_board[idx];
      a_game.m_tiles.push_back(t);
      a_game.m_boardTileMap.push_back(idx);
      idx++;
    }
  }
}

void addTile(Game& a_game, int idx, int x1, int y1, int x2, int y2, int value)
{
  BoardEvent added;
  added.type = BoardEvent::Added;
  added.itemIndex = idx;
  added.x = x1;
  added.y = y1;
  added.newX = x2;
  added.newY = y2;
  added.value = value;
  added.userInit = false;
  a_game.m_board.m_board[y2*a_game.width() + x2] = value;
  a_game.m_events.push_back(added);
}

void removeTile(Game& a_game, int idx, int x, int y, int delay)
{
  // printf("remove tile event adding: idx: %i  x,y: %i %i\n", idx, x, y);
  BoardEvent removed;
  removed.type = BoardEvent::Removed;
  removed.itemIndex = idx;
  removed.x = x;
  removed.y = y;
  removed.delay = delay;
  removed.value = a_game.m_board.m_board[y*a_game.width() + x];
  removed.userInit = false;
  a_game.m_events.push_back(removed);
}

void moveTile(Game& a_game, int idx, int x, int y, int x2, int y2, int delay)
{
  BoardEvent moved;
  moved.type = BoardEvent::Moved;
  moved.itemIndex = idx;
  moved.x = x;
  moved.y = y;
  moved.newX = x2;
  moved.newY = y2;
  moved.delay = delay;
  moved.value = a_game.m_board.m_board[y*a_game.width() + x];
  moved.userInit = false;
  a_game.m_events.push_back(moved);
}

void swapTile(Game& a_game, int idx, int idx2, int x1, int y1, int x2, int y2)
{
  BoardEvent swapped;
  swapped.type = BoardEvent::Swapped;
  swapped.itemIndex = idx;
  swapped.newItemIndex = idx2;
  swapped.x = x1;
  swapped.y = y1;
  swapped.newX = x2;
  swapped.newY = y2;
  swapped.value = a_game.m_board.m_board[y1*a_game.width() + x1];
  swapped.userInit = false;
  a_game.m_events.push_back(swapped);
}

void moveTilesDown(Game& a_game, int idx, int x, int y)
{
  for (int i = y; i >= 0; i--) {
    int idx = i * a_game.width() + x;
    moveTile(a_game, idx, x, i, x, i+1, 0);
    a_game.m_board.m_board[(i+1)*a_game.width() + x] = a_game.m_board.m_board[i*a_game.width() + x];
  }
}

bool checkForMoreMatches_old(Game& a_game)
{
  int x1, y1, x2, y2, x3, y3;
  bool hasMatches = getMatch3(a_game.m_board, x1, y1, x2, y2, x3, y3);
  int w = a_game.width();
  int h = a_game.height();
  int newIdx = w*h;
  int idx;
  if (hasMatches)
  {
    idx = y1 * w + x1;
    removeTile(a_game, idx, x1, y1, 0);
    moveTilesDown(a_game, idx, x1, y1-1);
    addTile(a_game, newIdx++, x1, -1, x1, 0, rand() % 5);

    idx = y2 * w + x2;
    removeTile(a_game, idx, x2, y2, 0);
    moveTilesDown(a_game, idx, x2, y2-1);
    addTile(a_game, newIdx++, x2, -1, x2, 0, rand() % 5);

    idx = y3 * w + x3;
    removeTile(a_game, idx, x3, y3, 0);
    moveTilesDown(a_game, idx, x3, y3-1);
    addTile(a_game, newIdx++, x3, -1, x3, 0, rand() % 5);
  }
  else
  {
    if (!hasMoveAvailable(a_game.m_board))
    {
      BoardEvent noMoreMoves;
      noMoreMoves.type = BoardEvent::NoMoreMoves;
      noMoreMoves.userInit = false;
      a_game.m_events.push_back(noMoreMoves);
    }
  }
  return hasMatches;
}

void handleMatch(Game& a_game, const Pattern& a_pattern, int a_boardX, int a_boardY)
{
  int w = a_game.width();
  int h = a_game.height();
  int newIdx = w*h;
  const int pw = 5;
  uint8_t offsets[pw];
  uint8_t lengths[pw];
  uint64_t po = a_pattern.m_offsets;
  uint64_t pl = a_pattern.m_lengths;
  for (int i = pw-1; i >= 0; i--) {
    offsets[i] = po & 0xff;
    lengths[i] = pl & 0xff;
    po >>= 8;
    pl >>= 8;
  }

  int removeDelay = 0;
  for (int i = 0; i < pw; i++) {
    int x = a_boardX + i;
    int len = lengths[i];
    int y1 = a_boardY + offsets[i];
    int y2 = y1 + len;
    for (int y = y1; y < y2; y++)
    {
      int idx = y * w + x;
      removeTile(a_game, idx, x, y, removeDelay++);
    }
    if (y1 && len) {
      for (int i = y1-1; i >= 0; i--) {
        int idx = i * w + x;
        moveTile(a_game, idx, x, i, x, i+len, -(i-(y1-1)));
        a_game.m_board.m_board[(i+len)*w + x] = a_game.m_board.m_board[i*w + x];
      }
    }
    for (int j = 0; j < len; j++) {
      addTile(a_game, newIdx++, x, j - len, x, j, rand() % a_game.m_tileTypes);
    }
  }
}

bool checkForMoreMatches(Game& a_game)
{
  int w = a_game.width();
  int h = a_game.height();
  std::vector<Pattern>& patterns = a_game.m_gameDB.m_patterns;
  //foreach(Pattern& p, patterns)
  for (int pi = 0; pi < patterns.size(); pi++)
  {
    const Pattern& p = patterns[pi];
    const int pw = 5;
    uint8_t offsets[pw];
    uint8_t lengths[pw];
    uint64_t po = p.m_offsets;
    uint64_t pl = p.m_lengths;
    for (int i = pw-1; i >= 0; i--) {
      offsets[i] = po & 0xff;
      lengths[i] = pl & 0xff;
      po >>= 8;
      pl >>= 8;
    }
    for (int j = 0; j < h; j++)
      for (int i = 0; i < w; i++) {
        if (j + offsets[0] >= h)
          continue;
        int val = a_game.m_board.m_board[(j + offsets[0]) * w + i];
        bool foundMatch = true;
        for (int x = 0; x < pw; x++) {
          int y1 = j + offsets[x];
          int len = lengths[x];
          if (len) {
            int y2 = y1 + len - 1;
            if (y2 >= h)
              foundMatch = false;
            else {
              for (int y = y1; y <= y2; y++)
                if ((i+x) >= w || val != a_game.m_board.m_board[y * w + i + x])
                  foundMatch = false;
            }
          }
        }
        if (foundMatch) {
          //printf("found match\n");
          handleMatch(a_game, p, i, j);
          a_game.m_score += p.m_score;
          printf("new score: %i\n", a_game.m_score);
          return true;
        }
      }
  }

  if (!hasMoveAvailable(a_game.m_board))
  {
    BoardEvent noMoreMoves;
    noMoreMoves.type = BoardEvent::NoMoreMoves;
    noMoreMoves.userInit = false;
    a_game.m_events.push_back(noMoreMoves);
  }

  return false;
}

bool swapTiles(Game& a_game, int x1, int y1, int x2, int y2)
{
  Board& a_board = a_game.m_board;
  int w = a_game.width();
  int h = a_game.height();
  int newIdx = w*h;

  if (!canSwapTiles(a_board, x1, y1, x2, y2))
    return false;

  int idx = y1 * w + x1;
  int idx2 = y2 * w + x2;
  swapTile(a_game, idx, idx2, x1, y1, x2, y2);

  // Actually swap them
  int tmp = a_board.m_board[y1*w + x1];
  a_board.m_board[y1*w + x1] = a_board.m_board[y2*w + x2];
  a_board.m_board[y2*w + x2] = tmp;

  bool hasMatches = true;
  //while (hasMatches)
    hasMatches = checkForMoreMatches(a_game);

  return true;
}