'Snake' game : type-based snake segments

Blitz3D Forums/Blitz3D Beginners Area/'Snake' game : type-based snake segments

_PJ_(Posted 2008) [#1]
Okay, first off, I apologise in advance for posting the whole code. The reason for this is because I am really unsure of just where the issues may be, although it's most certainly in the movement and checkposition functions.

Now, to the issue itself. As the code stands, it displays a simple menu screen wioth random snakes coiling around (set to change direction at random as well as on encounering other snakes). However, this doesn't seem to happen correctly and 'holes' appear within the snakes and eventually they seem to shrink up to just two segements. (possibly the multiple body segments' positions being equivalent to those of the head/tail.)

As for the code:

 ; Removed (see below)


"Keys.bb" is just to convert keycodes etc. for later redefining key options.
The various fonts, sounds and images are not really important and can be replaced with substitutes.

Again, sorry for posting so much code here, but I'm running out of ideas on how to deal with this...


Stevie G(Posted 2008) [#2]
Come on - help us help you! Make a version that runs without any media and exernal files and I'll happily take a look, otherwise forget it.

Just a quick observation: your select case statement doesn't look right in the BuildSegments function. Not sure if this makes a difference - probably not.

This :

Select Iter
	Case Iter=1				
		Player\SegType=SEG_HEAD
	Case Iter=Size				
		Player\SegType=SEG_TAIL
	Default				
		Player\SegType=SEG_BODY
End Select


Should be :

Select Iter
	Case 1				
		Player\SegType=SEG_HEAD
	Case Size				
		Player\SegType=SEG_TAIL
	Default				
		Player\SegType=SEG_BODY
End Select



_PJ_(Posted 2008) [#3]
Thanks for taking the time to look, Stevie. I really appreciate it.

Here's a version without any media/dependencies. I replaced the draw routine wit text line, so the snakes are visible on screen with their segments made from "O" characters.

I changeed the case as above, but, it didnt seem to make a difference.


Graphics 800,600,32,2
SetBuffer BackBuffer()
SeedRnd MilliSecs()

;Include "keys.bb"

FlushMouse()
FlushKeys()

; Initialise
;Const Datafile_path$="Snkdat.DAT"
;Const SnakeImage_path$="Snkspr.jpg"
;Const WallImage_path$="Wllspr.jpg"
;Const FoodImage_path$="Eatspr.jpg"

Const SNAKE_TYPES=6

Const BANNER_L=5
Const BANNER_R=795
Const BANNER_T=5
Const BANNER_B=35

Const HIGHLIGHT_DIFF=1
Const HIGHLIGHT_START=2
Const HIGHLIGHT_MUS=3
Const HIGHLIGHT_SFX=4
Const HIGHLIGHT_KANT=5
Const HIGHLIGHT_KCLCK=6

Const TP_DIFF=85
Const LT_DIFF=275
Const BM_DIFF=170
Const RT_DIFF=575

Const TP_START=175
Const LT_START=300
Const BM_START=210
Const RT_START=515

Const TP_MUS=250
Const LT_MUS=295
Const BM_MUS=330
Const RT_MUS=527

Const TP_SFX=335
Const LT_SFX=215
Const BM_SFX=430
Const RT_SFX=630

Const TP_KANT=491
Const LT_KANT=173
Const BT_KANT=584
Const RT_KANT=390

Const TP_KCLCK=491
Const LT_KCLCK=526
Const BT_KCLCK=584
Const RT_KCLCK=658

Const SCREEN_TOP=40
Const SCREEN_BOTTOM=600
Const SCREEN_LEFT=0
Const SCREEN_RIGHT=800

Const TILE_MINX=0
Const TILE_MINY=0
Const TILE_MAXX=50
Const TILE_MAXY=35
Const TILE_SIZE=16

Const DIR_UP%=2
Const DIR_LEFT%=3
Const DIR_RIGHT%=0
Const DIR_DOWN%=1

Const CHG_ANTICLOCK=1
Const CHG_CLOCK=2

Const AHEAD_SAFE=0
Const AHEAD_WALL=1
Const AHEAD_FOOD=2
Const AHEAD_SNAKE=3

Const COL_GREEN=0
Const COL_BLUE=1
Const COL_PURPLE=2
Const COL_RED=3
Const COL_YELLOW=4
Const COL_MULTI=5

Const SEG_HEAD=1
Const SEG_BODY=0
Const SEG_TAIL=2

Const SNAKE_SPEED=1

Global Sfx=80
Global Music=70

Global Difficulty%=5

Global Stage=1

Global HiScore1%=250
Global HiScoreName1$="H I"
Global HiScore2%=100
Global HiScoreName2$="MUM"
Global HiScore3%=50
Global HiScoreName3$="..."

Global KeyClockWise%=25
Global KeyAntiClockWise%=16

Global CurrentScore=0;

;; Remove This!
;SaveData()

;LoadData()

;Custom Types
Type Snake
	Field Reference
	Field Direction%
	Field Colour%
	Field Length%
	Field HeadX%
	Field HeadY%
End Type	

Type Segments
	Field Reference%
	Field Direction%
	Field Daddy%
	Field SegType%
	Field X%
	Field Y%
End Type

Type Changepoints
	Field Daddy%
	Field Direction%
	Field X%
	Field Y%
End Type

Dim Wall(TILE_MAXX+1,TILE_MAXY+1)

Global FoodX
Global FoodY

; Load Media
;Font = LoadFont("Year Supply Of Fairy Cakes.ttf",40,0,0,0)
;SetFont Font

;Global SegmentSprites=LoadAnimImage(SnakeImage_path$,TILE_SIZE,TILE_SIZE,0,SNAKE_TYPES)
;Global WallSprites=LoadAnimImage(WallImage_path$,TILE_SIZE,TILE_SIZE,0,1)
;Global FoodSprites=LoadAnimImage(SnakeImage_path$,TILE_SIZE,TILE_SIZE,0,2)

;Global Theme=LoadSound("Thmmus.wav")

;Global background=LoadImage("bcktile.jpg")
;Global system=LoadImage("system.bmp")
Global ticker=CreateImage(BANNER_R-BANNER_L,BANNER_B-BANNER_T)
SetBuffer ImageBuffer(ticker)
	Cls
SetBuffer BackBuffer()


;Menu Setup
Global MenuOn=1
Global KeyOn=0

Global BANNER_TEXT$="WELCOME TO STARVING SNAKE 2009! "
Global Highlighted=HighlightMenuSelect(MouseX(),MouseY())
Global Bannerfeed=0

;LoopSound Theme
;Global Theme_Channel=PlaySound(Theme) 
	tempfloat#=Music
	tempfloat#=tempfloat#/100.0	
;ChannelVolume Theme_Channel,tempfloat#

For Iter=1 To Rand(5,15)
	BuildSnake(Iter,Rand(3,10))
Next
For Master.Snake=Each Snake
	Master\Direction=Rand(0,3)
Next		

Flip

ClsColor 0,0,0
Cls

Global MoveDelay=MilliSecs()
; GO MENU!

While Not (KeyDown(KEY_ESCAPE))

;	DrawImage background,0,0
Cls
	For Master.Snake=Each Snake		
		If Rand(100)	< 5 Then ChangeSnakeDirection(Master\Reference,Rand(1,2))	
	Next	

	If (MilliSecs()-MoveDelay)>333
		MoveSnakes(MenuOn)
		MoveDelay=MilliSecs()
	End If
	DrawSnakes()
;	DrawImage system,0,0
	DrawMenu()
	
	Highlighted=HighlightMenuSelect(MouseX(),MouseY())
	
	Color 255,0,255
;	If KeyOn=0 
;		Text 327,535,ReturnKey$(KeyAntiClockWise),1,1
;		Text 615,535,ReturnKey$(KeyClockWise),1,1
;	End If
		
	Text 400,138,ResolveDifficulty$(),1,1
	Text 400,300,ResolveMusic$(),1,1
	Text 400,393,ResolveSfx$(),1,1
	
	
	If MouseDown(1)
		If MilliSecs()-LastControl>100		
			LastControl=MilliSecs()
			FlushMouse()
				GetMenu()
		End If
	End If
	
	Flip
Wend
	
;Start Game
Function StartGame()
	UpdateBanner()
	Stage=0;
	DoNewLevel()
End Function

;Functions

Function DrawMenu()

	HighlightOpt()
	UpdateBanner()
		
	DrawImageRect ticker,BanenerEdge,BANNER_T,BannerEdge-Bannerfeed,0,BANNER_R-BANNER_L,BANNER_B-BANNER_T	
End Function

Function UpdateBanner()
		Select Highlighted
			Case HIGHLIGHT_DIFF
				BANNER_TEXT="CHALLENGE LEVEL CHANGED TO "+ResolveDifficulty$()+" "
			Case HIGHLIGHT_START
				BANNER_TEXT="SCORE: "+CurrentScore+" HI SCORE: "+HiScore1+" STAGE: "+Stage+" "
			Case HIGHLIGHT_MUS
				BANNER_TEXT="MUSIC VOLUME CHANGED TO "+ResolveMusic$()+" "
			 Case HIGHLIGHT_SFX
				BANNER_TEXT="SOUND EFFECTS VOLUME CHANGED TO "+ResolveSfx$()+" "
		End Select
		
		If KeyOn=1 Then BANNER_TEXT="PRESS A KEY FOR ANTICLOCKWISE "
		If KeyOn=2 Then BANNER_TEXT="PRESS A KEY FOR CLOCKWISE "
		
		If (BANNER_R-BannerEdge)>ImageWidth(ticker)
			Bannerfeed=Bannerfeed+1
			If Bannerfeed>ImageWidth(ticker) 
				Bannerfeed=1
				BannerEdge=BANNER_R-1
			End If
		End If
		If Highlighted<>0
			SetBuffer ImageBuffer(ticker)			
			Cls			
			Color BannerFeed Mod 256,BannerFeed Mod 256,BannerFeed Mod 256			
				Text 0,(BANNER_B-BANNER_T)/2,BANNER_TEXT,False,True
			SetBuffer BackBuffer()
		End If
End Function

Function HighlightMenuSelect(X,Y)
	If X<RT_DIFF And X>LT_DIFF And Y>TP_DIFF And Y<BM_DIFF Then Return HIGHLIGHT_DIFF
	If X<RT_START And X>LT_START And Y>TP_START And Y<BM_START Then Return HIGHLIGHT_START
	If X<RT_MUS And X>LT_MUS And Y>TP_MUS And Y<BM_MUS Then Return HIGHLIGHT_MUS
	If X<RT_SFX And X>LT_SFX And Y>TP_SFX And Y<BM_SFX Then Return HIGHLIGHT_SFX
	If X<RT_KANT And X>LT_KANT And Y>TP_KANT And Y<BM_KANT Then Return HIGHLIGHT_KANT
	If X<RT_KCLCK And X>LT_KCLCK And Y>TP_KCLCK And Y<BM_KCLCK Then Return HIGHLIGHT_KCLCK	
	Return 0
End Function

Function HighlightOpt()
	Color 224,224,224
	Select Highlighted
		Case HIGHLIGHT_DIFF
			Rect LT_DIFF,TP_DIFF,RT_DIFF-LT_DIFF,BM_DIFF-TP_DIFF,0
		Case HIGHLIGHT_START
			Rect LT_START,TP_START,RT_START-LT_START,BM_START-TP_START,0
		Case HIGHLIGHT_MUS
			Rect LT_MUS,TP_MUS,RT_MUS-LT_MUS,BM_MUS-TP_MUS,0
		Case HIGHLIGHT_SFX
			Rect LT_SFX,TP_SFX,RT_SFX-LT_SFX,BM_SFX-TP_SFX,0
		Case HIGHLIGHT_KANT
			Rect LT_KANT,TP_KANT,RT_KANT-LT_KANT,BM_KANT-TP_KANT,0							
		Case HIGHLIGHT_KCLCK
			Rect LT_KCLCK,TP_KCLCK,RT_KCLCK-LT_KCLCK,BM_KCLCK-TP_KCLCK,0			
	End Select			
End Function

Function ResolveDifficulty$()

	Select Difficulty
		Case 1
			Return "EASY-PEASY"
		Case 2
			Return "VERY EASY"
		Case 3
			Return "EASY"
		Case 4
			Return "NORMAL"
		Case 5
			Return "CHALLENGING"
		Case 6
			Return "TOUGH"
		Case 7
			Return "VERY TOUGH"
		Case 8
			Return "TAXING"
		Case 9
			Return "FRUSTRATING"
		Case 10
			Return "IMPOSSIBLE"
	End Select
	
End Function

Function ResolveMusic$()
	Val=Music/5
	Select Val
		Case 0	Return "SILENT"
		Case 1	Return "INAUDIBLE"
		Case 2	Return "WHISPER"
		Case 3	Return "MOUSE"
		Case 4	Return "RABBIT HUNTING"
		Case 5	Return "VERY QUIET"
		Case 6	Return "QUIET"
		Case 7	Return "PEACEFUL"
		Case 8	Return "BELOW NORMAL"
		Case 9	Return "JUST BELOW NORMAL"
		Case 10	Return "NORMAL"
		Case 11	Return "A LITTLE LOUDER"
		Case 12	Return "MORE AUDIBLE"
		Case 13	Return "CLEAR"
		Case 14	Return "HARD OF HEARING"	
		Case 15	Return "LOUD"	
		Case 16	Return "VERY LOUD"
		Case 17	Return "SHOUT"
		Case 18	Return "NOISY"
		Case 19	Return "CACOPHONY"
		Case 20	Return "DEAFENING"
	End Select
End Function

Function ResolveSfx$()
	Val=Sfx/5
	Select Val
		Case 0	Return "SILENT"
		Case 1	Return "INAUDIBLE"
		Case 2	Return "WHISPER"
		Case 3	Return "MOUSE"
		Case 4	Return "RABBIT HUNTING"
		Case 5	Return "VERY QUIET"
		Case 6	Return "QUIET"
		Case 7	Return "PEACEFUL"
		Case 8	Return "BELOW NORMAL"
		Case 9	Return "JUST BELOW NORMAL"
		Case 10	Return "NORMAL"
		Case 11	Return "A LITTLE LOUDER"
		Case 12	Return "MORE AUDIBLE"
		Case 13	Return "CLEAR"
		Case 14	Return "HARD OF HEARING"	
		Case 15	Return "LOUD"	
		Case 16	Return "VERY LOUD"
		Case 17	Return "SHOUT"
		Case 18	Return "NOISY"
		Case 19	Return "CACOPHONY"
		Case 20	Return "DEAFENING"
	End Select
End Function

Function	GetMenu()
	Select Highlighted
		Case HIGHLIGHT_DIFF
			Difficulty=Difficulty+1
			If Difficulty>10 Then Difficulty=1
		Case HIGHLIGHT_START
			StartGame()
		Case HIGHLIGHT_MUS
			Music=Music+5
			If Music>100.0 Then Music=0	
			tempfloat#=Music
			tempfloat#=tempfloat#/100.0			
;			ChannelVolume Theme_Channel,tempfloat#
		 Case HIGHLIGHT_SFX
			Sfx=Sfx+5
			If Sfx>100 Then Sfx=0	
			tempfloat#=Sfx
			tempfloat#=tempfloat#/100.0			
		Case HIGHLIGHT_KANT
			KeyOn=1
		Case HIGHLIGHT_KCLCK
			KeyOn=2	
	End Select
	If Highlighted<>0 Then UpdateBanner()	
	FlushMouse()
	FlushKeys()
End Function
	
Function AddSegmentToSnake(ReferenceNumber)	
	CountSegments=0
	For Master.Snake=Each Snake
		MasterReference=Master\Reference
		If MasterReference=ReferenceNumber Then Exit
	Next		
	
	SnakeLength=Master\Length
	
	For IterSegments.Segments = Each Segments
		If IterSegments\Daddy=MasterReference
			CountSegments=IterSegments\Reference
				If IterSegments\SegType=SEG_TAIL			
					Prev.Segments= Before IterSegments.Segments			
					PlaceAtX= Prev\X
					PlaceAtY= Prev\Y		
					IterSegments\Reference=IterSegments\Reference+1
					SetDir=Prev\Direction								
					Exit
				End If
		End If
	Next	
	MoveSnakeExceptTail(ReferenceNumber)	
	SnakeLength=SnakeLength+1
	
	NewSegment.Segments = New Segments
	
	NewSegment\Reference=CountSegments
	NewSegment\Daddy=MasterReference
	NewSegment\Direction=SetDir
	NewSegment\SegType=SEG_BODY
	NewSegment\X=PlaceAtX
	NewSegment\Y=PlaceAtY	
	Master\Length=SnakeLength
End Function
	
Function BuildSnake(ReferenceNumber,StartLength)
		Player.Snake= New Snake
		Player\Reference=ReferenceNumber
		Player\Direction=DIR_RIGHT
		Player\Colour%=Rand(COL_GREEN,COL_MULTI)
		Player\Length%=StartLength
		Player\HeadX%=((TILE_MAXX/2)+(Player\Length/2))

		If (MenuOn)
			Player\HeadY%=ReferenceNumber*2
		Else
			Player\HeadY%=((TILE_MAXY+-TILE_MINY)/2)	
		EndIf
		BuildSegments(ReferenceNumber,StartLength)
End Function	
	
Function BuildSegments(Ref,Size)	
	For Master.Snake=Each Snake
		MasterReference=Master\Reference
		If MasterReference=Ref Then Exit
	Next			
	For Iter=1 To Size
		Player.Segments = New Segments
		Player\Reference=Iter
		Player\Direction=Master\Direction
		Player\Daddy=MasterReference
		Select Iter
			Case 1				
				Player\SegType=SEG_HEAD
			Case Size				
				Player\SegType=SEG_TAIL
			Default				
				Player\SegType=SEG_BODY
		End Select
		Player\X%=Master\HeadX-Iter
		Player\Y%=Master\HeadY
	Next
End Function

Function MoveSnakeExceptTail(Ref)	
	For Master.Snake=Each Snake
		MasterReference=Master\Reference
		If MasterReference=Ref	
			For IterSegments.Segments = Each Segments
				If ((IterSegments\Daddy=MasterReference) And (IterSegments\SegType<>SEG_TAIL))					
					For iterTurn.Changepoints=Each Changepoints
						If iterTurn\Daddy=MasterReference
							If iterTurn\X=iterSegments\X And iterTurn\Y=iterSegments\Y
								IterSegments\Direction=iterTurn\Direction
							EndIf
						EndIf
					Next				
					MoveSegment(IterSegments.Segments)			
					If IterSegments\SegType=SEG_HEAD Then Master\HeadX=IterSegments\X:	Master\HeadY=IterSegments\Y
					If IterSegments\SegType=SEG_HEAD Then Master\Direction=IterSegments\Direction	
				End If
			Next		
			Exit
		EndIf				
	Next
End Function

Function MoveSnakeTail(Ref)	
	For Master.Snake=Each Snake
		MasterReference=Master\Reference
		If MasterReference=Ref Then Exit
	Next		
	For IterSegments.Segments = Each Segments
		If ((IterSegments\Daddy=MasterReference) And (IterSegments\SegType=SEG_TAIL))
			For iterTurn.Changepoints=Each Changepoints
				If iterTurn\Daddy=MasterReference
					If iterTurn\X=iterSegments\X And iterTurn\Y=iterSegments\Y
						IterSegments\Direction=iterTurn\Direction
						Delete IterTurn
					EndIf
				EndIf
			Next
		MoveSegment(IterSegments.Segments)	
		End If
	Next						
End Function

Function CheckPosition(ReferenceNumber)
	If ReferenceNumber=0 Then Return
	For IterSegments.Segments = Each Segments
		If IterSegments\Reference=ReferenceNumber
			If IterSegments\X<TILE_MINX Then IterSegments\X=TILE_MAXX
			If IterSegments\X>TILE_MAXX Then IterSegments\X=TILE_MINX
			If IterSegments\Y<TILE_MINY Then IterSegments\Y=TILE_MAXY
			If IterSegments\Y>TILE_MAXY Then IterSegments\Y=TILE_MINY
		End If
	Next
End Function

Function GetSegment.Segments(ReferenceNumber)
	For IterSegments.Segments = Each Segments
		If IterSegments\Reference=ReferenceNumber
			Exit		
		End If
	Next
	Return IterSegments
End Function

Function GetSnake.Snake(ReferenceNumber)
	For IterSnake.Snake = Each Snake
		If IterSnake\Reference=ReferenceNumber
			Exit		
		End If
	Next
	Return IterSnake
End Function

Function PixelX(X)
	Return (X*TILE_SIZE)+SCREEN_LEFT
End Function

Function PixelY(Y)
	Return (Y*TILE_SIZE)+SCREEN_TOP
End Function

Function DrawSnakes()
		For iterSegs.Segments = Each Segments
			Master.Snake=GetSnake.Snake(iterSegs\Daddy)
			;DrawImage SegmentSprites,PixelX(iterSegs\X),PixelY(iterSegs\Y),Master\Colour
			Color (((Master\Colour)+1)*32),(((Master\Colour)+1)*32),(((Master\Colour)+1)*32)
			Text PixelX(iterSegs\X),PixelY(iterSegs\Y),"O"
		Next
End Function
	
Function CheckNewLevel()
	If CurrentScore>Difficulty*Stage*10
		DoNewLevel()
	End If
End Function
	
Function DoNewLevel()
	Stage=Stage+1
	GenerateWalls()	
	For AllSegs.Segments=Each Segments
		Delete AllSegs
	Next	
	For AllSnakes.Snake=Each Snake
		Delete AllSnakes
	Next	
	RespawnFood()
	BuildSnake(1,Stage+2)
End Function
	
Function MoveSnakes(IgnoreBackground=False)
	For Moving.Snake= Each Snake
		MoveDir=Moving\Direction
		AheadX=Moving\HeadX
		AheadY=Moving\HeadY	
		Select MoveDir
			Case DIR_UP
				AheadY=((Moving\HeadY)-(SNAKE_SPEED))
			Case DIR_DOWN
				AheadY=((Moving\HeadY)+(SNAKE_SPEED))
			Case DIR_LEFT
				AheadX=((Moving\HeadX)-(SNAKE_SPEED))
			Case DIR_RIGHT
				AheadX=((Moving\HeadX)+(SNAKE_SPEED))
		End Select
		
		If AheadX<TILE_MINX Then AheadX=TILE_MAXX
		If AheadX>TILE_MAXX Then AheadX=TILE_MINX
		If AheadX<TILE_MINY Then AheadX=TILE_MAXY
		If AheadX>TILE_MAXY Then AheadX=TILE_MINY
		
		nRep=ReportTileLocation(AheadX,AheadY)
				
		If (nRep=AHEAD_SNAKE) Then ChangeSnakeDirection(Moving\Reference,Rand(1,2))				
		If (nRep=AHEAD_WALL) Then ChangeSnakeDirection(Moving\Reference,Rand(1,2))	
		
				
		If (nRep<>AHEAD_FOOD) Then MoveSnakeExceptTail(Moving\Reference)
		If (nRep=AHEAD_FOOD) Then EatFood(Moving\Reference)
		MoveSnakeTail(Moving\Reference)
	Next
End Function

Function ChangeSnakeDirection(ReferenceNumber,Change)
		
	Player.Snake=GetSnake.Snake(ReferenceNumber)

	NewDirection=GetNewDirection(Player\Direction,Change)
	
	Turn.Changepoints=New Changepoints
	Turn\Daddy=Player\Reference
	Turn\Direction=NewDirection
	Turn\X=Player\HeadX
	Turn\Y=Player\HeadY
	Player\Direction=NewDirection
End Function

Function GetNewDirection(OldDirection,DirectionChange)
	Select OldDirection
		Case DIR_UP
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_LEFT
			If DirectionChange=CHG_CLOCK Then Return DIR_RIGHT
		Case DIR_DOWN
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_RIGHT
			If DirectionChange=CHG_CLOCK Then Return DIR_LEFT
		Case DIR_LEFT
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_DOWN
			If DirectionChange=CHG_CLOCK Then Return DIR_UP
		Case DIR_RIGHT
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_UP
			If DirectionChange=CHG_CLOCK Then Return DIR_DOWN
	End Select 
End Function

Function EatFood(EatingReference)
	CurrentScore=CurrentScore+(Difficulty*Stage)/2
	AddSegmentToSnake(EatingReference)
	RespawnFood()
End Function

Function RespawnFood()
	nTest=0
	While nTest=0
		FoodX=Rand(TILE_MAXX)
		FoodY=Rand(TILE_MAXY)
		
		nRep=ReportTileLocation(FoodX,FoodY)
		If nRep=AHEAD_FOOD Then nTest=1		
	Wend
	
End Function

Function ReportTileLocation(X,Y)

	If X>TILE_MAXX Then X=MINX
	If Y>TILE_MAXY Then Y=MINY
	
	If X<TILE_MINX Then X=TILE_MAXX
	If Y<TILE_MINY Then Y=TILE_MAXY
	
	If Wall(X,Y)=1 Then Return AHEAD_WALL 		
	If FoodX=X And FoodY=Y Then Return AHEAD_FOOD	

	For iter.Segments=Each Segments
		If Iter\X=X And Iter\Y=Y
			Return AHEAD_SNAKE
		End If
	Next

	Return AHEAD_SAFE		
End Function

Function GenerateWalls()
	If Difficulty>7 Then AddBorderWall
	If Difficulty>5 Or Stage>5
		For f=1 To Difficulty-5
			X=Rand(TILE_MAXX/4)
			Y=Rand(TILE_MAXY/6)
			PlaceWallAtCoord(X,Y)
		Next	
	End If
End Function

Function AddBorderWall()
	If Difficulty>7
		For iter=0 To ((TILE_MAXX/2)-1)
			SetWall(iter,0)
			SetWall(iter,TILE_MAXY)
		
			If iter<=TILE_MAXY
				SetWall(0,iter)
				SetWall(TILE_MAXX,iter)
			End If
		Next
		
		For iter=((TILE_MAXX/2)+1)  To TILE_MAXX
			SetWall(iter,0)
			SetWall(iter,TILE_MAXY)
		
			If iter<=TILE_MAXY
				SetWall(0,iter)
				SetWall(TILE_MAXX,iter)
			End If
		Next
	End If
End Function

Function PlaceWallAtCoord(X,Y)
	MAX=(Difficulty)
	DIRCTN=Rand(0,4)

	MDX=0;
	MDY=0;

	Select DIRCTN
		Case DIR_UP	
				If Y<MAX Then MAX=Y 
				MDY=-1
		Case DIR_DOWN	
				If ((Y+MAX) >(TILE_MAXY/6)) Then MAX=((TILE_MAXY/6)-Y) 	
				MDY=1													
		Case DIR_LEFT	
				If X<MAX Then MAX=X
				MDX=-1
		Case DIR_RIGHT			
				If ((X+MAX) >(TILE_MAXX/4)) Then MAX=((TILE_MAXX/4)-X) 
				MDX=1
	End Select			
		
		LNTH=Rand(1,MAX)		

		For iterate=0 To MAX 
			SetWall(X,Y)
			X=X+MDX
			Y=Y+MDY
		Next
End Function
			
Function SetWall(X,Y)
	Wall(X,Y)=1
	Wall(TILE_MAXX-X,TILE_MAXY-Y)=1
	Wall(X,TILE_MAXY-Y)=1	
	Wall(X-TILE_MAXX,Y)=1	
End Function

Function SaveData()
	Datafile=WriteFile(Datafile_path$)
	WriteInt(Datafile,Sfx%)
      	WriteInt(Datafile,Music%)
	WriteInt(Datafile,Difficulty%)
	WriteInt(Datafile,HiScore1)
	WriteString(Datafile,HiScoreName1$)
	WriteInt(Datafile,HiScore2)
	WriteString(Datafile,HiScoreName2$)
	WriteInt(Datafile,HiScore3)
	WriteString(Datafile,HiScoreName3$)	
	WriteInt(Datafile,KeyClockWise%)
	WriteInt(Datafile,KeyAntiClockWise)
	CloseFile Datafile
End Function

Function MoveSegment(Segment.Segments)
	Select Segment\Direction
		Case DIR_UP
			Segment\Y=((Segment\Y)-(SNAKE_SPEED))

		Case DIR_DOWN
			Segment\Y=((Segment\Y)+(SNAKE_SPEED))

		Case DIR_LEFT
			Segment\X=((Segment\X)-(SNAKE_SPEED))

		Case DIR_RIGHT
			Segment\X=((Segment\X)+(SNAKE_SPEED))
	End Select
	CheckPosition(Segment\Reference)
End Function


Function LoadData()
;	Datafile=ReadFile(Datafile_path$)
;		Sfx%=ReadInt(Datafile)
;		Music%=ReadInt(Datafile)
;		Difficulty%=ReadInt(Datafile)
;		HiScore1%=ReadInt(Datafile)
;		HiScore1Name$=ReadString(Datafile)
;		HiScore2%=ReadInt(Datafile)
;		HiScore2Name$=ReadString(Datafile)
;		HiScore3%=ReadInt(Datafile)
;		HiScore3Name$=ReadString(Datafile)	
;		KeyClockWise%=ReadInt(Datafile)
;		KeyAntiClockWise%=ReadInt(Datafile)		
;	CloseFile Datafile
End Function



_PJ_(Posted 2008) [#4]
For some reason, although when I edit the post, the code is all present, it doesn''t all display. So here's the last half of the above in case it's not visible:


Function GetNewDirection(OldDirection,DirectionChange)
	Select OldDirection
		Case DIR_UP
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_LEFT
			If DirectionChange=CHG_CLOCK Then Return DIR_RIGHT
		Case DIR_DOWN
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_RIGHT
			If DirectionChange=CHG_CLOCK Then Return DIR_LEFT
		Case DIR_LEFT
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_DOWN
			If DirectionChange=CHG_CLOCK Then Return DIR_UP
		Case DIR_RIGHT
			If DirectionChange=CHG_ANTICLOCK Then Return DIR_UP
			If DirectionChange=CHG_CLOCK Then Return DIR_DOWN
	End Select 
End Function

Function EatFood(EatingReference)
	CurrentScore=CurrentScore+(Difficulty*Stage)/2
	AddSegmentToSnake(EatingReference)
	RespawnFood()
End Function

Function RespawnFood()
	nTest=0
	While nTest=0
		FoodX=Rand(TILE_MAXX)
		FoodY=Rand(TILE_MAXY)
		
		nRep=ReportTileLocation(FoodX,FoodY)
		If nRep=AHEAD_FOOD Then nTest=1		
	Wend
	
End Function

Function ReportTileLocation(X,Y)

	If X>TILE_MAXX Then X=MINX
	If Y>TILE_MAXY Then Y=MINY
	
	If X<TILE_MINX Then X=TILE_MAXX
	If Y<TILE_MINY Then Y=TILE_MAXY
	
	If Wall(X,Y)=1 Then Return AHEAD_WALL 		
	If FoodX=X And FoodY=Y Then Return AHEAD_FOOD	

	For iter.Segments=Each Segments
		If Iter\X=X And Iter\Y=Y
			Return AHEAD_SNAKE
		End If
	Next

	Return AHEAD_SAFE		
End Function

Function GenerateWalls()
	If Difficulty>7 Then AddBorderWall
	If Difficulty>5 Or Stage>5
		For f=1 To Difficulty-5
			X=Rand(TILE_MAXX/4)
			Y=Rand(TILE_MAXY/6)
			PlaceWallAtCoord(X,Y)
		Next	
	End If
End Function

Function AddBorderWall()
	If Difficulty>7
		For iter=0 To ((TILE_MAXX/2)-1)
			SetWall(iter,0)
			SetWall(iter,TILE_MAXY)
		
			If iter<=TILE_MAXY
				SetWall(0,iter)
				SetWall(TILE_MAXX,iter)
			End If
		Next
		
		For iter=((TILE_MAXX/2)+1)  To TILE_MAXX
			SetWall(iter,0)
			SetWall(iter,TILE_MAXY)
		
			If iter<=TILE_MAXY
				SetWall(0,iter)
				SetWall(TILE_MAXX,iter)
			End If
		Next
	End If
End Function

Function PlaceWallAtCoord(X,Y)
	MAX=(Difficulty)
	DIRCTN=Rand(0,4)

	MDX=0;
	MDY=0;

	Select DIRCTN
		Case DIR_UP	
				If Y<MAX Then MAX=Y 
				MDY=-1
		Case DIR_DOWN	
				If ((Y+MAX) >(TILE_MAXY/6)) Then MAX=((TILE_MAXY/6)-Y) 	
				MDY=1													
		Case DIR_LEFT	
				If X<MAX Then MAX=X
				MDX=-1
		Case DIR_RIGHT			
				If ((X+MAX) >(TILE_MAXX/4)) Then MAX=((TILE_MAXX/4)-X) 
				MDX=1
	End Select			
		
		LNTH=Rand(1,MAX)		

		For iterate=0 To MAX 
			SetWall(X,Y)
			X=X+MDX
			Y=Y+MDY
		Next
End Function
			
Function SetWall(X,Y)
	Wall(X,Y)=1
	Wall(TILE_MAXX-X,TILE_MAXY-Y)=1
	Wall(X,TILE_MAXY-Y)=1	
	Wall(X-TILE_MAXX,Y)=1	
End Function

Function SaveData()
	Datafile=WriteFile(Datafile_path$)
	WriteInt(Datafile,Sfx%)
      	WriteInt(Datafile,Music%)
	WriteInt(Datafile,Difficulty%)
	WriteInt(Datafile,HiScore1)
	WriteString(Datafile,HiScoreName1$)
	WriteInt(Datafile,HiScore2)
	WriteString(Datafile,HiScoreName2$)
	WriteInt(Datafile,HiScore3)
	WriteString(Datafile,HiScoreName3$)	
	WriteInt(Datafile,KeyClockWise%)
	WriteInt(Datafile,KeyAntiClockWise)
	CloseFile Datafile
End Function

Function MoveSegment(Segment.Segments)
	Select Segment\Direction
		Case DIR_UP
			Segment\Y=((Segment\Y)-(SNAKE_SPEED))

		Case DIR_DOWN
			Segment\Y=((Segment\Y)+(SNAKE_SPEED))

		Case DIR_LEFT
			Segment\X=((Segment\X)-(SNAKE_SPEED))

		Case DIR_RIGHT
			Segment\X=((Segment\X)+(SNAKE_SPEED))
	End Select
	CheckPosition(Segment\Reference)
End Function


Function LoadData()
;	Datafile=ReadFile(Datafile_path$)
;		Sfx%=ReadInt(Datafile)
;		Music%=ReadInt(Datafile)
;		Difficulty%=ReadInt(Datafile)
;		HiScore1%=ReadInt(Datafile)
;		HiScore1Name$=ReadString(Datafile)
;		HiScore2%=ReadInt(Datafile)
;		HiScore2Name$=ReadString(Datafile)
;		HiScore3%=ReadInt(Datafile)
;		HiScore3Name$=ReadString(Datafile)	
;		KeyClockWise%=ReadInt(Datafile)
;		KeyAntiClockWise%=ReadInt(Datafile)		
;	CloseFile Datafile



Ross C(Posted 2009) [#5]
I found the snake game to be tricky, as their is so many different ways to do it. What i did was to have temp variables holding the positions when moving each segment:

The below is assuming the snakes are tile based. And your not giving each snake a head and a tail. I've found this to be pointless. Make the head the first item in the type collection and it will always be the first

move_to_x = the co-ords you want your snake head to be moving to.
move_to_y = same

move_from_x ; these will record each pieces current position.
move_from_y

Start on first piece of the type collection. I'm assuming it's the head.
Record it's current x-y position in the temp variables move_from_x and y.
Move head to the co-ords held in the move_to_x and y vars.

LOOP BACK TO BEGINNING

So, in code, something like:


function move_snake(x,y) ; the co-ords to head is to move to

   local move_to_x = x
   local move_to_y = y

   local move_from_x
   local move_from_y

   for snake_piece.snake = each snake

      move_from_x = snake_piece\x
      move_from_y = snake_piece\y

      snake_piece\x = move_to_x
      snake_piece\y = move_to_y

   next

end function



That should move your whole snake. Your need check for new pieces being generated. The easiest way i reckon is to check in the actual function above, then set a flag, then generate at the end of the move function. Something like:


function move_snake(x,y) ; the co-ords to head is to move to

   local move_to_x = x
   local move_to_y = y

   local move_from_x
   local move_from_y

   local head = 1
   local status = 0 ; set to 2 generate new piece. 1 for collision with snake

   for snake_piece.snake = each snake

      if head = 1 then
          status = check_head_collisions(move_to_x,move_to_y)
          head = 0 ; unset the flag, as the head piece has been found
      end if

      move_from_x = snake_piece\x
      move_from_y = snake_piece\y

      snake_piece\x = move_to_x
      snake_piece\y = move_to_y

      screen_grid(move_to_x,move_to_y) = 1
      screen_grid(move_from_x,move_from_y) = 0

   next


   if status = 2 then
      snake_piece.snake = snake
      snake_piece\x = move_from_x
      snake_piece\y = move_from_y
      screen_grid(snake_piece\x,snake_piece\y) = 1
   elseif status = 1 then
      ;death???
   end if

end function

function check_head_collisions(x,y)

   return screen_grid(x,y)

End function




Where screen_grid is the 2d grid used to place everything.

0 = blank grid spot
1 = snake piece occupies
2 = pill to increase snake size.

Does that help any?


_PJ_(Posted 2009) [#6]
Thanks for the reply, Ross.

Actually, I have worked everything on a ggrid as suggested (references to "TILE_*" and "AHEAD_*" constants deal with the collision detection and grid squares. albeit in perhaps a clunky fashion,

For certain reasons, I need more than one snake at a time, so setting the head to be the first instance in the Segment type isn't possible, that's why I included the "Daddy" field, as this links the segment tpe instances to their parent snake.

The movement was split into two parts, MoveSnakeExceptTail() and MoveSnakeTail() so that segments could be added into the snake at the end before the tail. The Head and 'eating' was dealt with in the MoveSnakeExceptTail() function as trhe Head segment could be checked here.

However, the Head and Tail of each snake are always recognised by the type of segment, The defining of the Head and Tail is done within the "BuildSegments()" function, called on creation of new snakes.

Moving the segments to the head piece is not enough in itself, for when the snake changes direction (at the head), all other segments will ONLY change direction when they reach the same point along the snake's length. THat's where the inclusion of the "Changepoints" Type comes in. (Also, a very inconvenient and problematic issue with the game!)