Recently, I've been trying to make a simple Match-3 game. As the core worked, I tried to expand it by adding blocking fields and diagonal movement. This has proven more difficult than I thought.
For those who don't know the game mechanics - the essence of the game is a rectangular array filled with items (usually 5 or 6 types), Three or more items of the same kind in a vertical or horizontal line create a match, which gets removed from the game. The items on the top fall down to fill the empty space. The empty cells on the top get refilled with more randomly generated items.
It all works fine when all cells are filled with items and gravity makes them fall only vertically. After adding cells that block the movement, this no longer works, because cells below will never be filled. For example:
1 2 3 4 5 1 2 3
2 3 4 5 1 2 3 4
3 4 5 # 2 3 4 5
4 5 1 0 3 4 5 1
5 1 2 0 4 5 1 2
1 2 3 0 5 1 2 3
2 3 4 0 1 2 3 4
3 4 5 0 2 3 4 5
(where 0 means an empty field and # is a block)
The solution here is moving items diagonally to empty spaces that can't normally be filled in any other way. Unfortunately, I tried several times from the scratch and I were unable to make an algorithm working as intended. Items either fall dawn diagonally when they shouldn't, or don't fill any empty spaces. The most promising one moved everything where it should go, but failed in another way:
moves = 0
do {
foreach (cell in array) {
if (cell_value == 0)
list.Add(cell)
}
foreach (cell in list) {
if (cell is on the top)
continue;
x = cell.x
y = cell.y
if (array[x,y-1] > 0) //if the cell above is not empty or a block...
Move(array[x, y-1], cell)
else if (array[x,y-1] == -1) { //the cell above is a block, this cell will never be filled...
if (array[x-1,y-1] > 0) //check upper-left cell
Move (array[x-1,y-1], cell)
else if (array[x+1,y-1] > 0) //check upper-right cell
Move (array[x+1,y-1], cell)
}
if (moved)
moves++
}
}
while (moves > 0)
It kinda works... except it doesn't really care about the order of items falling. It's pretty egregious on boards like this:
# # 3 4 5 1 # #
# 3 4 5 1 2 3 #
3 4 5 1 2 3 4 5
4 5 1 2 3 4 5 1
5 1 2 3 4 5 1 2
1 2 3 4 5 1 2 3
2 3 4 5 1 2 3 4
3 4 5 6 2 3 4 5
It frequently happens that some fields diagonally poach items from other fields, which makes the result weird:
First iteration:
# # 1 2 3 4 # #
# 0 0 0 0 0 0 #
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Second iteration:
# # 0 0 0 0 # #
# 0 1 2 3 0 4 #
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Third iteration:
# # 0 0 0 0 # #
# 0 0 0 0 0 0 #
0 0 1 2 3 0 0 4
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
I should probably sort the list, but couldn't find the right criterion. Any ideas how to improve this algorithm? Or am I overthinking it and there is a better way to do this?