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\$
  • \$\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\$ – Green_qaue Sep 6 '15 at 22:38
  • \$\begingroup\$ @Shiro Sounds a lot better than what Iam doing, write up an answer so I can accept :) \$\endgroup\$ – Green_qaue Sep 6 '15 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\$ – Green_qaue Sep 6 '15 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\$ – dimitris93 Sep 6 '15 at 23:08
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\$
  • \$\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\$ – Green_qaue Sep 6 '15 at 23:30
  • \$\begingroup\$ Is this function inside your Tile Class ? Or is it in another class ? \$\endgroup\$ – dimitris93 Sep 6 '15 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\$ – Green_qaue Sep 6 '15 at 23:35
  • \$\begingroup\$ ... One sec, I made a huge blunder \$\endgroup\$ – Green_qaue Sep 6 '15 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\$ – Green_qaue Sep 6 '15 at 23:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.