Match-3 code
BlitzMax Forums/BlitzMax Programming/Match-3 code
| ||
I want to try an make a Match-3 game, I know is boring, but I have got a good idea for the game. Now I ran into trouble with the match algoritme! Any one have some source to help me getting started. I have a grid of 20x20 and want to check for match 3 og more. And I only need to match horizontally and vertically. Anything that can get me started would be nice (sourcecode) |
| ||
There's a basic match-3 game in the BlitzMax samples. Maybe you can take a look to that source code. It is the tiledrop sample. |
| ||
Work out a pseudo code algorithm first and then test it out, something like: - Start in top left corner. - Store the value of the current item - Horizontal testing: Move right to the next tile. If the value doesn't match, move onto the vertical testing below. If it does match, keep going right. When you either reach a non-matching item or the right edge, you can see if the number of matches is 3 or greater. I used to store the matches in a separate grid array so I could explode them all at once later. - Vertical testing: Move down to the next tile. Test for a match or hitting the bottom of the grid etc. Pretty much the same as horizontal testing. That's it, it's pretty easy. What is harder is testing for *possible* matches that a player could make. That algorithm needs a lot more work. Then when you start to introduce locked items, gaps, falling shapes, bonuses etc it gets even more complex. |
| ||
What is harder is testing for *possible* matches that a player could make. That algorithm needs a lot more work. Then when you start to introduce locked items, gaps, falling shapes, bonuses etc it gets even more complex. I thought that the worst bit was getting it to check for matches and let the player take a turn and all the rest of it, when tiles are already moving/falling.There was a thread recently where somebody was complaining along the lines of "are Match Three games all Blitzmax is capable of" - like match three games are some sort of irrelevant easy-to-write bile. Its straightforward to write a working prototype but its difficult to get it to play well. I started mine two years ago (don't ask), and up until about a month ago its had a bug that I just couldn't get to the bottom of. I thought I'd fixed it at least half a dozen times, but then it appeared again. One of those "once in a blue moon" bugs. I ended up spending a day rewriting several of the major code functions and using a totally different implementation of keeping track of everything. Its rock solid now. </tangent> Anyway, following on from what Jake said, my code keeps track of what's moved/been moving and only check for matches where those tiles can affect things. There's little point in checking for matches on whole sections of tiles that haven't gone anywhere. |
| ||
I have a source code for the one Qube(David Templer) did in my download's link. If you want to take a look at it. I think it's comercial quality. http://www.filefront.com/12217541/holiday-swapz-BMX-sourcem3.zip |
| ||
Acually I hate to toot my own horn, but this would be extremely easy in TileMax.. Basically, when a piece moved all you would have to do is test to see if adjacent tiles contain like item id's and if the count is sufficient Start the fireworks! In TileMax.. All tiles are referenced relatively... connected in 9 directions xy (-1 to 1)+1 Something like... Function Match(ID:Int=ID_GREENGEM, MT:TMTile) Local Count=0 For X:int=0 to 2 For Y:int=0 to 2 if x=1 and y=1 ' This equates to a tiles warp point Else If MT.Nav(x,y) and MT.Nav(x,y).Actors if MT.Nav(x,y).Actors.First().ID=ID Count:+1 Endif EndIF Endif Next If Count>2 LetTheFireworksGo() Next End Function |
| ||
Besides the match 3 source included with BM, there have been two complete match 3 games that have had the source released and one half-assed (sorry Chroma) attempt that had the source released. |
| ||
There was a thread recently where somebody was complaining along the lines of "are Match Three games all Blitzmax is capable of" - like match three games are some sort of irrelevant easy-to-write bile. Its straightforward to write a working prototype but its difficult to get it to play well. 100% agree here. I saw that comment too. As well as the mechanics, a commercial quality match-3 will test blitzmax out in many other ways. |
| ||
TomToads full match 3 engine with source (blitzmax). http://www.blitzbasic.com/Community/posts.php?topic=70555#792829 |
| ||
Thanks alot guys! |
| ||
Qube also released the source to his Christmas match-3 game |
| ||
Qube also released the source to his Christmas match-3 game really? ;) |
| ||
really? ;) Yes and it was commercial grade, ready for release. Qube had posted it on his forums. |
| ||
LOL I was being sarcastic look at post #5 |
| ||
Qube also released the source to his Christmas match-3 game I did? oh no, quick grab it back! :p Before anyone asks, no, you're not getting your mitts on the vastly improved match-3 engine which accompanies the iPhone version of holiday swapz (available in all good stockists) :D |
| ||
@Qube if you don't want me to have It available I can take it of my filefront download. I just thought it might be helpful for other trying to understand the logic behind it. No harm intended. |
| ||
if you don't want me to have It available I can take it of my filefront download. I just thought it might be helpful for other trying to understand the logic behind it. No harm intended. I was joking, hence the :p - The old PC version of Holiday Swapz I released as public domain so it's good there's a backup :) |
| ||
I was being sarcastic look at post #5 Gotcha, I mised that one ;) |
| ||
I can't get it right, I mean I can find 3(or more) in a row/col, but I want to find "1" like in ex 1. Ex 1. 000000000 000000000 000011100 000000110 000000000 Look at ex 1, I want to find all "1" in the grid, that touched. I try to figure it out but I can't get IT!! Any help would be very nice. |
| ||
What you're asking for is the equivalent of a 'flood fill' function, which basically boils down to using recursion. Pseudocode: Find the first tile in the grid (i.e 5,2) Set its 'marked' flag to True Call Search() function 'when you get to here, all '1' tiles will be marked Iterate through all tiles Remove any with 'marked = true' Function Search() Check all four adjoining tiles If they're the same value (i.e. 1) AND tile.marked = False Set its 'marked' flag to True. Call Search() function. EndIf EndFunction |
| ||
Thanks GFK it works now. I finally got it to work, thanks everyone! Heres my code if anyones interested, it proberly could be optimized but for now it should work. It checks for matching fields that touch. Remark to code: PlayField is playfield CheckArray is filled with PlayField values for testing Marked is to mark fields to remove Nabors just checks for nabors. Graphics 1024, 768 Global Rows:Int = 20 Global Cols:Int = 20 Global PlayField:Int[Rows, Cols] Global CheckArray:Int[Rows, Cols] Global Marked:Int[Rows, Cols] Global Nabors:Int[Rows, Cols] Global CountingYs:Int = 0 Global ones:Int = 0 '** Place random values in playfield PlaceRandomFields(50, 6, 7, 13) '** Fyld arrays FillArraysWithZero() 'InsertDataIntoPlayField() CopyFromPlayFieldIntoCheckArray() While Not KeyHit(KEY_ESCAPE) Cls DisplayCheckArray(100, 40) DisplayNabors(500, 40) DisplayMarked(100, 420) DisplayPlayField(500, 420) If KeyHit(KEY_SPACE) And ones=0 For Local ys:Int=1 To 6 RemoveNonCheckTiles(ys) FindAndRemoveStandAloneTiles(ys) FindFirstTitle(ys) FindAndRemoveTwos(ys) RemoveFromPlayField() FillArraysWithZero() CopyFromPlayFieldIntoCheckArray() Next ones=1 End If If KeyHit(KEY_TAB) FillArraysWithZero() PlaceRandomFields(50, 6, 7, 13) 'InsertDataIntoPlayField() CopyFromPlayFieldIntoCheckArray() ones = 0 End If '** Flip Flip -2 Wend '********************************************* '* Function: Remove fields not to be checked * '********************************************* Function RemoveNonCheckTiles(checkTile:Int) For Local x:Int=0 To Rows-1 For Local y:Int= 0 To Cols-1 If CheckArray[x, y] <> checkTile Then CheckArray[x,y] = 0 Next Next End Function '**************************************************** '* Function: Fill CheckArray with PlayField * '**************************************************** Function CopyFromPlayFieldIntoCheckArray() '** Copy PlauField into CheckArray For Local x2:Int=0 To Rows-1 For Local y2:Int= 0 To Cols-1 CheckArray[x2,y2] = PlayField[x2, y2] Next Next End Function '******************************************** '* Function: Fill arrays with zeros * '******************************************** Function FillArraysWithZero() For Local x:Int=0 To Rows-1 For Local y:Int= 0 To Cols-1 CheckArray[x,y] = 0 Marked[x,y] = 0 Nabors[x,y] = 0 Next Next End Function '******************************************** '* Function_ Find first nabor field * '******************************************** Function FindFirstTitle(tileType:Int) Local tileValue:Int For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 tileValue = CheckArray[x,y] If tileValue = tileType Marked[x,y] = 1 SearchTiles(x,y, tileType) End If Next Next End Function '************************************************************************ '* Function: Loop through all fields to find who has nabors * '************************************************************************ Function SearchTiles(x:Int, y:Int, tileType:Int) If CountingYs = 0 Then y = y + 1 If y > Cols-1 Then x = x + 1 If x > Rows-1 Then x = 0 y = 0 End If End If If CheckArray[x,y] = tileType And checknabors(x, y) And Marked[x,y] = 0 Marked[x,y] = 1 SearchTiles(x,y, tileType) End If End Function '**************************************************** '* Function: Remove all '1' in Nabors and Marked * '**************************************************** Function FindAndRemoveTwos(tileType:Int) Local naborTiles:Int For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 '** We found a match If CheckArray[x, y] = tileType '** Its got match from side fields naborTiles = checknaborsTwo(x,y) If naborTiles <> 0 If naborTiles = 1 Or naborTiles = 2 Or naborTiles= 4 Or naborTiles = 8 Then Nabors[x,y] = 1 If naborTiles = 3 Or naborTiles = 6 Or naborTiles = 12 Or naborTiles = 9 Or naborTiles = 10 Or naborTiles = 5 Then Nabors[x,y] = 2 If naborTiles =7 Or naborTiles = 14 Or naborTiles= 13 Or naborTiles = 11 Then Nabors[x,y] = 3 If naborTiles = 15 Then Nabors[x,y] = 4 End If End If Next Next '** Loop through Nabors, find all '2' and make next '1' field to '2' Local numberTwoTiles:Int Local naborTilesOfOnes:Int For Local x2:Int=0 To Rows-1 For Local y2:Int=0 To Cols-1 numberTwoTiles = Nabors[x2, y2] '** We found a double If numberTwoTiles = 2 Or numberTwoTiles = 3 Or numberTwoTiles = 4 If Nabors[x2+1,y2] = 1 Then Nabors[x2+1,y2]=2 If Nabors[x2-1,y2] = 1 Then Nabors[x2-1,y2]=2 If Nabors[x2,y2+1] = 1 Then Nabors[x2,y2+1]=2 If Nabors[x2,y2-1] = 1 Then Nabors[x2,y2-1]=2 End If Next Next '** Remove all double '1' in Marked Local numberOneTiles:Int For Local x3:Int=0 To Rows-1 For Local y3:Int=0 To Cols-1 numberOneTiles = Nabors[x3, y3] If numberOneTiles = 1 Marked[x3,y3] = 0 End If Next Next End Function '************************************************* '* Function: Remove all '1' fields in CheckArray * '************************************************* Function FindAndRemoveStandAloneTiles(tileType:Int) Local checknabor:Int Local tilevalue:Int Local in3x:Int = False Local in3y:Int = False '** Gennemgå array og fjern single tal For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 tilevalue = CheckArray[x,y] If tilevalue <> 0 '** Onlu check if current field is different from zero checknabor = checknabors(x,y) If checknabor = False CheckArray[x,y] = 0 End If End If Next Next End Function '******************************************** '* Function: Check next fields * '******************************************** Function checknabors:Int(xpos:Int, ypos:Int) Local zeros:Int = 0 Local checkvalue:Int '** Checks if next x is zero If xpos <=Rows-1 '** Only if its not the last checkvalue = CheckArray[xpos+1, ypos] If checkvalue <> 0 zeros = zeros + 1 End If End If '** Check if previous x is zero If xpos >= 1 '** Only if its not the first one checkvalue = CheckArray[xpos-1, ypos] If checkvalue <> 0 zeros = zeros + 1 End If End If '** Check if next y is zero If ypos <= Cols-1 '** Only if its not the last checkvalue = CheckArray[xpos, ypos+1] If checkvalue <> 0 zeros = zeros + 1 End If End If '** Check if previous y is zero If ypos >= 1 '** Only if its not the first one checkvalue = CheckArray[xpos, ypos-1] If checkvalue <> 0 zeros = zeros + 1 End If End If If zeros = 0 Return False Else Return True End If End Function '***************************************************** '* Function: Check all next to '2' field in Nabors * '***************************************************** Function checknaborsTwo:Int(xpos:Int, ypos:Int) Local zeros:Int = 0 Local checkvalue:Int '** Checks if next x is zero If xpos <=Rows-1 '** Only if its not the last checkvalue = CheckArray[xpos+1, ypos] If checkvalue <> 0 zeros = zeros + 2 End If End If '** Check if previous x is zero If xpos >= 1 '** Only if its not the first one checkvalue = CheckArray[xpos-1, ypos] If checkvalue <> 0 zeros = zeros + 8 End If End If '** Check if next y is zero If ypos <= Cols-1 '** Only if its not the last checkvalue = CheckArray[xpos, ypos+1] If checkvalue <> 0 zeros = zeros + 4 End If End If '** Check if previous y is zero If ypos >= 1 '** Only if its not the first one checkvalue = CheckArray[xpos, ypos-1] If checkvalue <> 0 zeros = zeros + 1 End If End If If zeros = 0 Return 0 Else Return zeros End If End Function '********************************** '* Function: Place random fields * '********************************** Function PlaceRandomFields(numberOfFields:Int, maxNumber:Int, startGrid:Int, endGrid:Int) Local randomX:Int, randomY:Int, fieldValue:Int For Local x:Int = 1 To numberOfFields randomX = Rand(startGrid,endGrid) randomY = Rand(startGrid,endGrid) fieldValue = Rand(1,maxNumber) PlayField[randomX, randomY] = fieldValue 'CheckArray[randomX, randomY] = fieldValue Next End Function '********************************************** '* Function: Remove from PlayField via Marked * '********************************************** Function RemoveFromPlayField() For Local x:Int = 0 To Rows-1 For Local y:Int = 0 To Cols-1 If Marked[x, y] = 1 Then PlayField[x, y] = 0 Next Next End Function '**************************************** '* Function: Insert data into PlayField * '**************************************** Function InsertDataIntoPlayField() PlayField[6,1] = 1 PlayField[7,1] = 1 PlayField[8,1] = 1 PlayField[10,1] = 1 PlayField[1,4] = 2 PlayField[1,5] = 2 PlayField[1,6] = 2 PlayField[5,3] = 3 PlayField[6,3] = 3 PlayField[7,3] = 3 PlayField[7,4] = 3 PlayField[8,4] = 3 PlayField[5,13] = 4 PlayField[6,13] = 4 PlayField[7,13] = 4 PlayField[7,14] = 4 PlayField[8,14] = 4 PlayField[5,7] = 5 PlayField[6,7] = 5 PlayField[7,7] = 5 PlayField[7,8] = 5 PlayField[8,8] = 5 PlayField[5,10] = 6 PlayField[6,10] = 6 PlayField[7,10] = 6 PlayField[7,11] = 6 PlayField[8,11] = 6 PlayField[12,12] = 6 PlayField[12,13] = 6 PlayField[12,14] = 6 End Function '******************************************** '* Function: Show CheckArray * '******************************************** Function DisplayCheckArray(startXPos:Int, startYPos:Int) DrawText "CheckArray", startXpos, startYPos-16 For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 If CheckArray[x,y] <> 0 DrawText CheckArray[x,y], startXPos + x * 16, startYPos + y * 16 End If Next Next End Function '******************************************** '* Function: Show PlayField * '******************************************** Function DisplayPlayField(startXPos:Int, startYPos:Int) DrawText "PlayField", startXpos, startYPos-16 For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 If PlayField[x,y] <> 0 DrawText PlayField[x,y], startXPos + x * 16, startYPos + y * 16 End If Next Next End Function '******************************************** '* Function: Show Marked fields * '******************************************** Function DisplayMarked(startXPos:Int, startYPos:Int) DrawText "Marked", startXpos, startYPos-16 For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 If Marked[x,y] <> 0 DrawText Marked[x,y], startXPos + x * 16, startYPos + y * 16 End If Next Next End Function '******************************************** '* Function: Show Nabors fields * '******************************************** Function DisplayNabors(startXPos:Int, startYPos:Int) DrawText "Nabors", startXpos, startYPos-16 For Local x:Int=0 To Rows-1 For Local y:Int=0 To Cols-1 If Nabors[x,y] <> 0 DrawText Nabors[x,y], startXPos + x * 16, startYPos + y * 16 End If Next Next End Function |
| ||
I put up my final code!! |
| ||
@GaryV: you must not have followed that old thread fully. It was working great and I got a lot of compliments on it. But I got bored because Match-3 is no challenge. And it's not commercially viable these days anyways imo. Most people here are trying to make a game they can turn a quick profit on. I'm more into making something unique. I look at coding as a means of expression. Not turning a quick buck. |
| ||
@GaryV: you must not have followed that old thread fully. I was it showed the initial promise of your game framework and quickly suffered the same fate as your game framework ;) Most people here are trying to make a game they can turn a quick profit on. I'm more into making something unique. I look at coding as a means of expression. Not turning a quick buck. I agree 100% |
| ||
Everyone please welcome GaryV as the newest member of the Chroma fan club! :) And what sir have YOU finished/released? hehe TriGaDa: Are you scanning through the whole lists and storing matches and popping all present matches at the same time? (Didn't look) |
| ||
I take the play array and make an new array with the tiles I want to check. Then I deletes all single tiles. Then I make an new array where i put in how many nabors each tile have. Compare two arrays and delete all tiles with only 1 nabor. BUT my code has an error, it can not check border tiles. So en a 20x20 grid it can only check from 2 to 19. |
| ||
Everyone please welcome GaryV as the newest member of the Chroma fan club! :) I actually am, get the chip off your shoulder. I was really looking forward to your game framework. IMHO, it was the only one that showed promise after Gray's was pulled off the market. |
| ||
Ah ok...sorry then. I guess I'm a defensive product of the mostly negative attitudes around here. I haven't been keeping up with the Blitz Community Framework. How is that one shaping up? |
| ||
Its running fine thank you for asking. :) Why not helping the Community Framework ? it could be the challenge You are looking for... |