Question about a connect-four style system...

Blitz3D Forums/Blitz3D Beginners Area/Question about a connect-four style system...

JohnMil(Posted 2004) [#1]
I'm sure this is simple, but I can't figure it out. How would I detect if 4 objects are sitting in a row touching each other? Hope thats enough info... I dont really know how else to describe it. Thanks in advance.


sswift(Posted 2004) [#2]
It is easiest if you make sure the object align to a grid of a certain width, and then divide their locations by that width, and use floor on the resulting number to get an intger that tells you which grid squares they reside in.

Ie: Grid_X = Floor(Peice_X# / Peice_Width#)

Then you can place markers in an array showing where all the peices of a certain color are in the grid, and use that to determine if there are four in a row.

But you need to make sure the peices align to the grid when you place them, or this won't work. Don't bother trying to find a way to figure out if peices are in a row without aligning them to a grid... Then you're getting into artificial intelligence and fuzzy logic. :-)

To check if there are four in a row, you just start at the first peice, and move across to the right... If you see a red peice, increment the counter by 1. If you see a peice which is not red, reset the counter to 0. If the counter is then equal to 4, then you have four in a row.


WolRon(Posted 2004) [#3]
To check if there are four in a row, you just start at the first peice, and move across to the right... If you see a red peice, increment the counter by 1. If you see a peice which is not red, reset the counter to 0. If the counter is then equal to 4, then you have four in a row.
Of course, that only checks for horizontal rows. You will probably need to check for vertical rows if you don't find any hor. ones. And if you don't find any hor. or ver. rows, then you will probably have to check for diagonal ones (which is a bit trickier, but definitely not impossible).


This thread may contain some helpful/useful information...


Bill Sims(Posted 2004) [#4]
In a text-based version of this, I had an array which represented the board (as in Dim board(6,7)) to represent the six rows and seven columns. Each location in the array was filled with '0' if nobody occupied that location, '1' if player 1 occupied it, and '2' if player 2 occupied it. Next I wrote a function which looked like the following (doing this from memory):
Function checkWin( player )
  If board(1,1) = player And board(1,2) = player And board(1,3) = player And board(1,4) = player Then Return True
  If board(1,2) = player And board(1,3) = player And board(1,4) = player and board(1,5) = player Then Return True
  ; Continue this for all rows, columns, and diagonals.
  Return False
End Function


This worked fine and was faster than using loops. Then I used this logic in a Alpha-Beta A* algorithm (for the computer AI). It was way too slow. I then had to encode the board into bits for each player's locations. I then 'And'ed the numbers against a set which represented winning lines. This was VERY fast, but obfuscated. For example, visualize this 3x3 board for Tic-Tac-Toe:

O O X
O X X
X O X

X's representation would be (001 011 101) in binary or 93 in decimal. O's representation would be (110 100 010) or 418 in decimal. The winning positions would be (111 000 000), (000 111 000), (000 000 111), (100 100 100), (010 010 010), (001 001 001), (100 010 001), and (001 010 100). (In decimal this is 384, 56, 7, 292, 146, 73, 273, and 84.)

And X's positions with each winning position to see if they match.
93 And 384 = 0. Fail.
93 And 56 = 24. Fail.
93 And 7 = 5. Fail.
...
93 And 84 = 84. Winner!

This method is insanely fast, but I only recommend it if you are writing an AI for your Connect4 program. Of course, you have to modify my example to work with Connect4, but it takes only a little effort.


Curtastic(Posted 2004) [#5]
this is good. right?
;newpiece is the piece that was just played
For dir=0 To 7
	x=newpiecex
	y=newpiecey
	amount=1
	Repeat
		x=x+Ceil(Cos(dir*45))
		y=y+Ceil(Sin(dir*45))
		If grid(x,y)<>newpieceteam Then Exit
		amount=amount+1
		If amount=4 Then RuntimeError "FOUR!"
	Forever
Next



JohnMil(Posted 2004) [#6]
Thanks alot guys. After messing around with it for awail I was able to get it to work with horizontal and vertical checks. Still having some issues with diagnal. Any ideas for that?


soja(Posted 2004) [#7]
The easiest way probably depends on how you're checking the vertical and horizontal.

What's your board structure? How are you checking for vertical and horizontal completions?


JohnMil(Posted 2004) [#8]
I am doing my system like exactly like sswift suggested above. Except my array is 3 by 3 (Like tic tac toe).


sswift(Posted 2004) [#9]
Diagonal isn't much more difficult than horizontal or vertical. However, you're going to have a hell of a time finding four in a row if your board is only 3x3. :-)

To do a diagonal check, start at the first peice, and then go up and to the right one. If that is outside the grid, stop.

Then move down to the peice below, and go up and to the left, and then up and to the left, and stop because that is outside the grid.

Once you get to the bottom of the grid, do that diagonal line, and then step over one to the right along the bottom, and go up and to the left, etc, till you get outside the grid, and then step to the right again to do the next diagonal row until you get all the way to the right of the board, and then you're done.

That will do this diagonal: /

Now to do this diagonal: \

Do the same thing, but instead of going down the left side first go across the top, and then down the right side. And when stepping from square to square go down and to the left instead of up and to the right.

Of course if you are doing tic tac toe, then you only have to check the two middle diagonals, and that is really simple. You can't get three in a row any other way diagonally in tic tac toe.


StOrM3(Posted 2004) [#10]
Coorrae:
That was a gr8 piece of code m8. I like it, do you care if I use it in my puzzle game, I am going to make a function out of it, and pass in the piecex,piecey of the grid piece the player just shot, and also the identifying number of that piece, and have your code check the 7 dirs from that piece, and then setup if statements outside the loop for 4,3,2 hit combo's and then return the point values.

This is a very easy and efficient piece of code if it will work like I think it will. Here is how I modified it:

function checkpiece(newpiecex,newpiecey,number)
local points
For dir=0 To 7
	x=newpiecex
	y=newpiecey
	amount=1
	Repeat
		x=x+Ceil(Cos(dir*45))
		y=y+Ceil(Sin(dir*45))
		If fruitarray(x,y)\number<>number Then Exit
                if fruitarray(x,y)\number=number then
		  amount=amount+1
                  hideentity fruitarray(x,y)\item
                endif
	Forever
    
        if amount = 4 then 
          points = fruit\points * 4 + 500
          return points
        endif
        if amount = 3 then 
          points = fruit\points * 3 + 250
          return points
        endif
        if amount = 2 then 
          points = fruit\points * 2 + 100
          return points
        endif
        if amount = 1 then
          points = fruit\points
          return points
        endif
Next


What Ya Think ? Will it work ? I am going to call it in my player shoot function, when the player hits an item, take that item x,y coords and its number value, call this function, to test for combos, and return points, for the one the player did hit + any combos connected to it.

Ooops forgot one thing.. need to remove the other combo items connected as well. Soo.. Let me update this.

Okay I edited the code, can someone tell me if they think this will work right ? I am currently at work.

For additional info, the grid I use is 6x6 with items laid out in rows and columns. Will this work ?


StOrM3(Posted 2004) [#11]
Umm hello ? Should I move this post to the Blitz3D side ?


Curtastic(Posted 2004) [#12]
thats cool that you can use it but I see my code wont work for four-ina-row unless the new peice is on an edge of the sequence.
what if
X
X
 <- new piece slides in here
X


well, there are only 2 on top, so thats no fourinarow
there are only 1 on bottom, so thats no fourinarow
but it really would produce
X
X
X
X
which is four in a row
but if youre not making connect four, I guess its ok


I suggest instead of returning points, do pointsadd=pointsadd+points
then at the end of the function do return pointsadd
that way you can get points from multiple directions
like


 <-new pice falls here
XX
X X
X  X

you should get points from both directions
and that would make it get to all entities of the same number that are connected to the newpiece, in all directions.


Curtastic(Posted 2004) [#13]
now this would work for connect four as well as other puzzle games
For dir=0 To 3
	amount=1
	For sign=-1 To +1 Step 2
		x=newpiecex
		y=newpiecey
		Repeat
			x=x+Ceil(Cos(dir*45))*sign
			y=y+Ceil(Sin(dir*45))*sign
			If outofgridboundary(x,y) Then Exit
			If grid(x,y)<>newpieceteam Then Exit
			amount=amount+1
		Forever
	Next
	;here is where the amount is finished adding, for this direction
	
	;just to clarify
	;dir 0 is right and left
	;dir 1 is upleft and downright
	;dir 2 is up and down
	;dir 3 is upright and downleft
	;ex. if dir is 0 and amount is 3, then the new piece created a horizontal 3-in-a-row

Next



StOrM3(Posted 2004) [#14]
Okay but here is the concept I'm working with, this should help understand what I am looking for. All My pieces are on the screen already. The player is just shooting a type of an item at the others, and if it matches what it hits, then it takes it off the screen, at this time I would also like it to remove any other pieces of this type, that are touching the piece hit by the player, and return the point totals, based on how many matching pieces touching it removes. No pieces on the screen need to move or slide, just the player shooting from different directions as it moves around the outside edge.

So I think your first attempt or this one could work also.

All I have to do is make a function, and pass in the item that the player collided with, x, y coords in the grid layout, and also the number of the item collided with so I can check the other items against that number to look for matches. ** Also NOTE ** if the player is not the item he hits on the screen, then it will not remove the item, but instead change the player into that item.

I hope you follow me, if not I can send you a copy of the beta I have done already, and let you play it, then you can see what I am trying to do. This will all be simplified though, since now I am using 3D Objects, but I am planning on converting to 2D objects to lower the system requirements, and make it easier to add special fx to the game, like particle fx, and rotations and stuff.

BTW: ** AD HERE ** I would like to thank everyone on this forum for helping me out, without whom I would have never made this game as far and as good as it is today without you guys!!

** NOTE ** the nSprite lib is the answer if you need fast 2D displays of bmp fonts, and Hud elements etc.. and the guy whom wrote it, is top notch, I gave him 2 bug reports which he promptly fixed by sending me a function he left out, and in the next version everything will be perfecto', since I have informed him of the last memory de-allocation error problem in nS_FreeAll(), when using bmp font system.

Again, thank you for your help, I really appreciate it.


big10p(Posted 2004) [#15]
This is the best way I can think of (at the moment) for doing a connect4 style 4-in-a-row check:

[edit] I've only checked this on paper, but I'm pretty sure it'll work. :P

[edit] Oops. There was a slight bug in original code. Now fixed.

[edit] Fixed a typo error. Last one, I hope. :)

	Const END_ROW = 9
	Const END_COL = 9
	Dim grid(END_ROW,END_COL) ; A 10x10 grid.

; Checks to see if value at row,col makes 4-in-a-row.
; Returns: True if 4-in-a-row, else	False.
Function connect4(row%, col%)
	
	match = grid(row,col)
	
	For n = 0 To 3	; 4 directions to check: horz, vert & 2 diags.
	
		dx = Sgn(n) : dy = Sgn(n-2)		
		connected = 1
		
		For j = 1 To 2
			trow = row : tcol = col

			For j = 1 To 3
				trow = trow + dx : tcol = tcol + dy
				out_of_bounds = (trow<0)+(trow>END_ROW)+(tcol<0)+(tcol>END_COL)
				If out_of_bounds Then Exit
				If grid(trow,tcol) <> match Then Exit
				connected = connected + 1
			Next
		
			If connected >= 4 Then Return True ; Bug was here: changed = test to >= test.
			
			dx = -dx : dy = -dy ; Reverse checking direction.
		Next
						
	Next
	
	Return False
	
End Function



Curtastic(Posted 2004) [#16]
yep, it better be. its almost the same as mine actually


big10p(Posted 2004) [#17]
"it better be" what, exactly? :/


Curtastic(Posted 2004) [#18]
it had better be the best way to do it, because I do it that way!