0
\$\begingroup\$

I'm out of ideas and need help. So far I generate a board nicely using a flood fill algorithm and a 2d-array, I can also move tiles around the board.

Each Tile knows its own row, column and ID (images get set depending on ID, it's also what I use to check for matches).

The version I have for checking matches right now almost works, but it does not remove Tiles correctly if the match happens on row/col that got changed because of the recent move (tiles above that move down because of the match that was made from my move).

I have a lot of code but Ill try to explain it:

I check for matches in my game loop, so each frame I loop through the 8x8 board and check for matches. To check for matches I have these methods:

private void checkIfUpMatch(int row, int col){ //recursive Tile toCheck = array[row][col]; toCheck.setChecked(true); //we set the tile as checked if(toCheck.getRow() >= 7){ //if next row doesnt exist return; } if(array[row + 1][col].getId() == array[row][col].getId()) { verticalMatches ++; checkIfUpMatch((toCheck.getRow() + 1), toCheck.getCol()); } return; } private void checkIfDownMatch(int row, int col){ //recursive Tile toCheck = array[row][col]; toCheck.setChecked(true); if(toCheck.getRow() <= 0){ //if next row doesnt exist return; } if(array[row - 1][col].getId() == array[row][col].getId()) { verticalMatches ++; checkIfDownMatch((toCheck.getRow() - 1), toCheck.getCol()); } return; } private void checkIfLeftMatch(int row, int col){ //recursive Tile toCheck = array[row][col]; toCheck.setChecked(true); if(toCheck.getCol() <= 0){ //if next col doesnt exist return; } if(array[row][col - 1].getId() == array[row][col].getId()) { horizontalMatches ++; checkIfLeftMatch((toCheck.getRow()), toCheck.getCol() - 1); } return; } private void checkIfRightMatch(int row, int col){ //recursive Tile toCheck = array[row][col]; toCheck.setChecked(true); if(toCheck.getCol() >= 7){ //if next col doesnt exist return; } if(array[row][col + 1].getId() == array[row][col].getId()){ horizontalMatches ++; checkIfRightMatch((toCheck.getRow()), toCheck.getCol() + 1); } return; } 

All of these methods do the same thing, just for up/down/left/right. Basically:

  • Set the tile as checked
  • If the tile is on the board and the neighboring tile matches, increase verticalMatches/horizontalMatches.
  • call method again for next Tile (recursive)

I call these methods inside my checkForMatch method that is called in the game loop:

private void checkIfMatch(int row, int col){ checkIfUpMatch(row, col); checkIfDownMatch(row, col); checkIfLeftMatch(row, col); checkIfRightMatch(row, col); if(verticalMatches >= 3 || horizontalMatches >= 3){ for(int i=0; i<ROWS;i++){ for(int j = 0; j < COLS; j++){ if(array[i][j].isChecked()){ array[i][j].setMatched(true); } } } reFill(); }else{ for(int i=0; i<ROWS;i++){ for(int j = 0; j < COLS; j++){ if(array[i][j].isChecked() && !array[i][j].isMatched()){ array[i][j].setChecked(false); } } } } verticalMatches = 1; horizontalMatches = 1; } 
  • If verticalMatches or horizontalMatches >= 3 we set the checked tiles as matched
  • else we set checked back to false for all the tiles and reset verticalMatches/horizontalMatches

As you can see I call refill after we set all the tiles needed as matched, which looks like this:

private void reFill(){ //refill the board after a match has been made for(int i=0; i<ROWS;i++){ for(int j = 0; j < COLS; j++){ if(array[i][j].isMatched()){ fallDown(i, j); array[i][j].setMatched(false); array[i][j].setChecked(false); } } } } private void fallDown(int row, int col){ //replaces the ID of the removed tile from above Tile removedTile = array[row][col]; if(row+verticalMatches > 7){ //we are at the top so just put in new tiles int random = MathUtils.random(1, TILETYPES); array[row][col].setId(random); return; } Tile changeTile = array[row + verticalMatches][col]; //use vertical matches to set the removed tile to the correct ID removedTile.setId(changeTile.getId()); fallDown(changeTile.getRow(), changeTile.getCol()); return; } 
  • Here we loop through all the matched tiles and call fallDown
  • fallDown replaces the removed tile with the tile above recursively until we are at the top of the board, then we insert new random tiles

That is it, any advice, tips or ideas are welcome, I've spent probably a good 20 hours of coding trying to figure this out. Maybe there is some easier way to do this that I am missing, or maybe I'm just making a few mistakes.

EDIT:

Trying to implement Shiro's solution:

private void checkVertical(int row, int col){ // The current id of the tile that we are checking for matches final int currentId = tiles[row][col].getId(); // The matches of the current tile final List<Tile> matchedTiles = new ArrayList<Tile>(); matchedTiles.add(tiles[row][col]); if(row < 7 && tiles[row + 1][col].getId() == currentId){ // 1 up matchedTiles.add(tiles[row + 1][col]); if(row < 6 && tiles[row + 2][col].getId() == currentId) // 2 up matchedTiles.add(tiles[row + 2][col]); } if(row > 0 && tiles[row - 1][col].getId() == currentId) { // 1 down matchedTiles.add(tiles[row - 1][col]); if(row > 1 && tiles[row - 2][col].getId() == currentId) // 2 down matchedTiles.add(tiles[row - 2][col]); } // If we find a match if(matchedTiles.size >= 3) { for(Tile tile : matchedTiles) { final int row = tile.getRow(); final int col = tile.getCol(); tiles[row][col].setId(8); } } } 
\$\endgroup\$
4
  • \$\begingroup\$ @shiro I dont use recursion to check the 2 above tiles, matches can be 4,5,6,7 even 8 tiles in a row, so I check every tile in that direction as long as it has the same ID. Since I do tile.setChecked(true) first thing, means that even if the tile is in the middle, it is marked as checked and will be removed. And I agree that the code is infefficient at parts but this is usually how I write code, I just want to make it work first, then I worry about optimization after \$\endgroup\$ Commented Sep 6, 2015 at 22:38
  • \$\begingroup\$ @Shiro Sounds a lot better than what Iam doing, write up an answer so I can accept :) \$\endgroup\$ Commented Sep 6, 2015 at 22:58
  • \$\begingroup\$ @shiro One question tho, if I dont check tiles in the game loop (every frame), then how will the game know if there has been a match after the fallDown() method \$\endgroup\$ Commented Sep 6, 2015 at 23:03
  • \$\begingroup\$ My answer was referring to how to identify all "matches" from a given array of tiles. You should check for that only if a relevant input is given or if the fallDown() animation has ended. You might wanna introduce a variable isFalling and not include the tiles with isFalling == true when checking for "matches" \$\endgroup\$ Commented Sep 6, 2015 at 23:08

1 Answer 1

1
\$\begingroup\$

In a 3-match game I don't believe you can match more than 5 at a time, so it is in fact 2 at most from up and 2 from below. You don't have to use recursion for that, you can write this code in-line and comment what you are doing for clarity.

Here is a different approach:

We have a temp list of tiles. We start checking every tile. For every tile that we check, we fill the list with the tiles that "match" the current tile's ID in the vertical direction (just the 2 tiles above and 2 tiles below).if list.size() >= 3, then for every tile in that list, we call fallDown() and reset the list, else just reset the list. We do the same for the horizontal direction.

And that's all. No reFill() method, no extra iterations, no matched/checked variables and no recursion.

\$\endgroup\$
12
  • \$\begingroup\$ Thank you, I tried your solution but cannot get it working properly. I edited my answer with what I tried, I call checkVertical after I made a move, passing the moved tiles row and col to the method. Then I set the id to 8 (changing the image of the tile) of the tiles that matched if there is a match just for testing to see if it works, but nothing happens. \$\endgroup\$ Commented Sep 6, 2015 at 23:30
  • \$\begingroup\$ Is this function inside your Tile Class ? Or is it in another class ? \$\endgroup\$ Commented Sep 6, 2015 at 23:34
  • \$\begingroup\$ No, it's inside my "main" class. Where I create the Tiles on board and everything else. I can't put it inside my Tile class, thats just an object. It has no idea what Tiles are neighboring \$\endgroup\$ Commented Sep 6, 2015 at 23:35
  • \$\begingroup\$ ... One sec, I made a huge blunder \$\endgroup\$ Commented Sep 6, 2015 at 23:38
  • 1
    \$\begingroup\$ Thanks man. Although you dont put the Tile we are checking from in the temp-array, so that tile wont get removed if we find a match, only the neighboring ones \$\endgroup\$ Commented Sep 6, 2015 at 23:52

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.