AI Food Gathering code

Miscellaneous Forums/Blitz Showcase/AI Food Gathering code

Sunday afternoon coding...

A sort of ant type food gathering AI thing going on...

There's a bunch of food and a bunch of workers.

Workers go find food.

If a worker sees another worker with food and they are closer to the base they will take it off the other one to form a sort of chain.

Anyway... more curious than useful!

Graphics 1024,768
SetBuffer BackBuffer()


Global BaseX = GraphicsWidth()/2
Global BaseY = GraphicsHeight()*.75
Global Food = 0


Type Worker
	Field x#,y#
	Field Carrying
	Field Direction#
	Field NewDirection#
	Field FoodExchanged
End Type

Type Food
	Field x#,y#
	Field Quantity
End Type

Function FoodNew()
	F.Food = New Food
	F\x = Rand(0,GraphicsWidth()-1)
	F\y = Rand(0,GraphicsHeight()-1)
	F\Quantity = Rand(1,100)
End Function

Function WorkerNew()
	W.Worker = New Worker
	W\x = BaseX
	W\y = BaseY
	W\Carrying = 0
	W\direction = Rand(0,359)
	W\NewDirection = w\direction
End Function


Function Draw()
	
	For F.Food = Each Food
		Color 0,255,0
		Rect f\x-10,f\y-5,21,11,True
		Color 0,0,0
		Text f\x,f\y,f\quantity,True,True
		
	Next
	
	For W.Worker = Each Worker
		If w\carrying Then Color 255,255,255 Else Color 200,200,255
		Oval w\x-5,w\y-5,11,11,True
		Color 0,0,0
		Line w\x,w\y,w\x+(Sin(w\direction)*5),w\y-(Cos(w\direction)*5)
	Next
	
	Color 255,0,0
	Oval BaseX - 20, BaseY-20,41,41,False
	Text basex,basey, food,True,True
End Function

Function UpdateWorkers()
	For W.Worker = Each Worker
	
		; if you're carrying food you're at 50% speed
		slowdown# = 1
		If w\carrying Then slowdown# = .5
		ad# = AngleDifference(w\direction,w\newdirection)
		
		If ad>90 Then slowdown = 0
		
		w\Direction = WrapAngle(w\Direction + (ad/20))
		
		w\x = w\x + (Sin(w\direction) * slowdown)
		w\y = w\y - (Cos(w\direction) * slowdown)
		
		If w\x < 0 Then w\newdirection = Rand(45,135)
		If w\y < 0 Then w\newdirection = Rand(135,225)
		If w\x > GraphicsWidth() Then w\newdirection = Rand(225,315)
		If w\y > GraphicsHeight() Then w\newdirection = (Rand(-45,45) + 360) Mod 360
		
		; Find food
		For F.Food = Each Food
			d = Dis(w\x,w\y,f\x,f\y) 
			If d < 100 Then
				w\newdirection = (ATan2(f\y-w\y,f\x-w\x) + 90+360) Mod 360
			EndIf
			
			If d < 10 And w\carrying = 0 Then
				ReduceFood(F.Food)
				w\carrying = True
			EndIf

		Next
		
		If w\foodexchanged > 0 Then w\foodexchanged = w\foodexchanged - 1
		
		; Find another worker with food who's further away from the base than you
		; and take the food from him to free him to search for more
		If w\carrying = False And w\foodexchanged = 0 Then
			For w1.worker = Each worker
				d = Dis(w\x,w\y,w1\x,w1\y) 
				If d < 100 And w1\carrying = True And dis(w\x,w\y,basex,basey) < dis(w1\x,w1\y,basex,basey) Then
					w\newdirection = (ATan2(w1\y-w\y,w1\x-w\x) + 90+360) Mod 360
				EndIf
				
				If d < 15 And w1\carrying = True And w\carrying = False Then
					w\carrying = True
					w1\carrying = False
					w1\foodexchanged = 100
					w1\newdirection = Rand(0,359)
				EndIf
			Next
		EndIf
		
		; If you've got food, head for base
		If w\carrying Then
			w\newdirection = (ATan2(BaseY-w\y,BaseX-w\x) + 90+360) Mod 360
		EndIf	
		
		; If you're at base drop off the food and go searching again		
		If Dis(w\x,w\y,BaseX,BaseY) < 20 And w\carrying Then
			w\carrying = False
			w\newdirection = Rand(0,359)
			Food = Food + 1
			
		EndIf
		
		
		
		
	Next
End Function

Function ReduceFood(F.Food)
	If f\quantity > 0 Then f\quantity = f\quantity - 1
	If f\quantity = 0 Then Delete f
End Function

Function Dis(x1#,y1#,x2#,y2#)
	x=(x1-x2)
	y=(y1-y2)
	Return Sqr((x*x)+(y*y))
End Function

Function AngleDifference#(angle1#,angle2#) 
	Return ((angle2 - angle1) Mod 360 + 540) Mod 360 - 180 
End Function

Function WrapAngle#(value#)
	Return value+360 Mod 360
End Function

For n=1 To 20
	FoodNew()
Next
For n=1 To 100
	workernew()
Next


Repeat
	Cls
	updateworkers()
	Draw()
	Flip
Until KeyHit(1)


hey that's neat. Reminds me of the ants that invaded our dining room last autumn!

cool... could be used in a Comand & Conquer sort of game...

--Mike

Wheres the stomp button? I want to squish the ones in the corner.

Pretty cool, do they depend on food? That would be cool to see them eventually die off due to lack of food.

Just made it more interesting... Added a caller mode. When one finds food if there's no-one else calling it will become a caller to attract the others.

Graphics 1024,768
SetBuffer BackBuffer()




Global BaseX = GraphicsWidth()/2
Global BaseY = GraphicsHeight()*.75
Global Food = 0
SpeedUp = 5


Type Worker
	Field x#,y#
	Field Carrying
	Field Direction#
	Field NewDirection#
	Field FoodExchanged
	Field Caller
End Type

Type Food
	Field x#,y#
	Field Quantity
End Type

Function FoodNew()
	F.Food = New Food
	F\x = Rand(0,GraphicsWidth()-1)
	F\y = Rand(0,GraphicsHeight()-1)
	F\Quantity = Rand(1,100)
End Function

Function WorkerNew()
	W.Worker = New Worker
	W\x = BaseX
	W\y = BaseY
	W\Carrying = 0
	W\direction = Rand(0,359)
	W\NewDirection = w\direction
End Function


Function Draw()
	
	For F.Food = Each Food
		Color 0,255,0
		Rect f\x-10,f\y-5,21,11,True
		Color 0,0,0
		Text f\x,f\y,f\quantity,True,True
		
	Next
	
	For W.Worker = Each Worker
		Color 150,150,150
		If w\carrying Then Color 0,255,0
		If w\caller Then Color 0,255,255: Oval w\x-count,w\y-count,count*2+1,count*2+1,False
		Oval w\x-5,w\y-5,11,11,True
		Color 0,0,0
		Line w\x,w\y,w\x+(Sin(w\direction)*5),w\y-(Cos(w\direction)*5)
	Next
	
	Color 255,0,0
	Oval BaseX - 20, BaseY-20,41,41,False
	Text basex,basey, food,True,True
End Function

Function UpdateWorkers()
	For W.Worker = Each Worker
	
		; if you're carrying food you're at 50% speed
		slowdown# = 1
		If w\carrying Then slowdown# = .5
		If w\caller Then slowdown = 0
		ad# = AngleDifference(w\direction,w\newdirection)
		
		If ad>90 Then slowdown = 0
		
		w\Direction = WrapAngle(w\Direction + (ad/20))
		
		w\x = w\x + (Sin(w\direction) * slowdown)
		w\y = w\y - (Cos(w\direction) * slowdown)
		
		If w\x < 0 Then w\newdirection = Rand(45,135)
		If w\y < 0 Then w\newdirection = Rand(135,225)
		If w\x > GraphicsWidth() Then w\newdirection = Rand(225,315)
		If w\y > GraphicsHeight() Then w\newdirection = (Rand(-45,45) + 360) Mod 360
		
		
		If w\caller = False Then
		
			If w\foodexchanged > 0 Then w\foodexchanged = w\foodexchanged - 1
			
			; Find another worker with food who's further away from the base than you
			; and take the food from him to free him to search for more
			If w\carrying = False And w\foodexchanged = 0 Then
				For w1.worker = Each worker
					d = Dis(w\x,w\y,w1\x,w1\y) 
					If d < 100 And w1\carrying = True And dis(w\x,w\y,basex,basey) < dis(w1\x,w1\y,basex,basey) Then
						w\newdirection = (ATan2(w1\y-w\y,w1\x-w\x) + 90+360) Mod 360
					EndIf
					
					If d < 300 And w1\caller = True 
						w\newdirection = (ATan2(w1\y-w\y,w1\x-w\x) + 90+360) Mod 360
					EndIf
					
					If d < 15 And w1\carrying = True And w\carrying = False Then
						w\carrying = True
						w1\carrying = False
						w1\foodexchanged = 100
						w1\newdirection = Rand(0,359)
					EndIf
				Next
			EndIf
		
		
			; Only find food if you're not carrying food
			If w\carrying = False Then 
				; Find food
				For F.Food = Each Food
					d = Dis(w\x,w\y,f\x,f\y) 
					If d < 100 Then
						w\newdirection = (ATan2(f\y-w\y,f\x-w\x) + 90+360) Mod 360
					EndIf
					
					If d < 20 Then
					; check for callers
						found = False
						For w1.worker = Each worker
							If Dis(w\x,w\y,w1\x,w1\y)<50 And w1\caller = True Then found = True
						Next
						; if no callers become a caller
						If found = False Then w\caller = True
					EndIf
									
					If d < 10 Then
						ReduceFood(F.Food)
						w\carrying = True
					EndIf
		
				Next
			EndIf
			

			
			; If you've got food, head for base
			If w\carrying Then
				w\newdirection = (ATan2(BaseY-w\y,BaseX-w\x) + 90+360) Mod 360
			EndIf
			
	
			
			; If you're at base drop off the food and go searching again		
			If Dis(w\x,w\y,BaseX,BaseY) < 20 And w\carrying Then
				w\carrying = False
				w\newdirection = Rand(0,359)
				Food = Food + 1
				
			EndIf
		Else
		; You are a caller
			Found = False
			For F.Food = Each Food
				If Dis(w\x,w\y,f\x,f\y) < 50 Then found = True
			Next
			If found = False Then w\caller = False
		EndIf
			
			
		
		
	Next
End Function

Function ReduceFood(F.Food)
	If f\quantity > 0 Then f\quantity = f\quantity - 1
	If f\quantity = 0 Then Delete f
End Function

Function Dis(x1#,y1#,x2#,y2#)
	x=(x1-x2)
	y=(y1-y2)
	Return Sqr((x*x)+(y*y))
End Function

Function AngleDifference#(angle1#,angle2#) 
	Return ((angle2 - angle1) Mod 360 + 540) Mod 360 - 180 
End Function

Function WrapAngle#(value#)
	Return value+360 Mod 360
End Function

For n=1 To 100
	FoodNew()
Next
For n=1 To 100
	workernew()
Next

Global count

Repeat
	Cls
	For n=1 To Speedup
	updateworkers()
	Next
	Draw()
	count = (count + 10) Mod 100
	Flip
Until KeyHit(1)


Very good rob ! :) like always :)

Even better :D

I made something comparable to this.

Everyone had a job: Baker, Guard or Thief.

Bakers would make food. Thiefs would steal food (if they cant afford it from the baker) or they would buy it. Guards would buy food, or persue thiefs (if seen when stealing). Everyone had to sleep and eat.

I dont know why I deleted it...

Well why not now add preditors! that try to eat the workers. And guards. who have to guard the workers?

Nice... especially the 'caller' mode.

I did something similar a long way back where the ants could emerge from a 'hive' North, South, East or West. A neural network then evolved the best strategy for sending out workers based on which direction provided the best harvest.

There was also a huge magnifying glass that let you slow down the ants or frazzle them out of existence...

Unfortunately, I didn't understand Blitz collisions very well at the time, and ants kept 'popping' out of the world as they got forced beneath the ground or escaped from the arena. I eventually abandoned it for this reason.

I had a little program myself. This is what I came up with (Blitz3D)

;Ant AI System 0.1
;Chris Bate

	Graphics3D 600,600,0,2


;;===========================;;
;;					VARIABLES					;;
;;------------------------------------------------------;;

;Frame Limiting
;-----------------------
	Global FrameTime
	Global Period
	Global FPS = 30
;-----------------------

;Ant Control
;-----------------------
	OutsideBoundry = 19
	
	SeedRnd MilliSecs() : NestX = Rnd(-10,10) : Delay 1
	SeedRnd MilliSecs() : NestZ = Rnd(-10,10)

	AntAmount = 400
	
	AntSpeed# = 0.1
		AntSpeedRnd = 2

	AntAttentionSpan# = 0.8 ;Amount of time (in seconds) an ant will stay on the same course without changing directions
		AntAttentionRnd# = 2
		
	FoodNumbers = 10
	FoodSpread = 18
	FoodAmount = 500

	ColType_Ant=1
	ColType_Nest=2
	ColType_Food=3

	
;-----------------------


;;===========================;;
;;						SETUP						;;
;;------------------------------------------------------;;

;Scene Setup
;-----------------------
	Camera = CreateCamera()
		CameraClsColor Camera, 0,40,0
		CameraProjMode Camera,2
		CameraZoom Camera,0.05
		PositionEntity Camera,0,20,0
		RotateEntity Camera,90,0,0

	AmbientLight 255,255,255

	Nest = CreateCube()
		PositionEntity Nest, NestX,0,NestZ
		EntityType Nest,ColType_Nest

	AntMesh = CreateCube()
		HideEntity AntMesh
;-----------------------

;Ant Type
;-----------------------
	Type Ant
		Field Model
		Field Size
		Field Speed#
		Field Direction
		Field AttentionSpan#
		Field AttentionLeft ; How many more left cycles until he changes direction
		Field Target ; Current Food Target
		Field Mode$
	End Type 

	For i = 0 To AntAmount
		SeedRnd MilliSecs() : Delay 1

		Hive.Ant = New Ant
			Hive\Model = CopyEntity (AntMesh)
				EntityColor Hive\Model, Rnd(139,180),100,30
				PositionEntity Hive\Model, NestX, 0, NestZ
				ScaleEntity Hive\Model, 0.1,0.1,0.1
				EntityType Hive\Model,ColType_Ant
				EntityRadius Hive\Model,0.1
				TurnEntity Hive\Model,0,Rnd(-0,360),0
			Hive\Speed = AntSpeed * Rnd(1,AntSpeedRnd)
			Hive\AttentionSpan = 0
			Hive\Mode = "Looking For Food"
	Next
;-----------------------

;Food Type
;-----------------------
	Type FoodT
		Field Model#
		Field Amount#
	End Type
		
	For i = 0 To FoodNumbers
		SeedRnd MilliSecs() : Delay 1

		Food.FoodT = New FoodT
			Food\Model = CreateSphere()
			EntityType Food\Model,ColType_Food
			PositionEntity Food\Model, EntityX (Nest),0,EntityZ(Nest)
			If EntityDistance (Food\Model, Nest) < 5 Then
				PositionEntity Food\Model, Rnd(-FoodSpread,FoodSpread),0,Rnd(-FoodSpread,FoodSpread)
			End If
			EntityColor Food\Model, 0,200,0
			Food\Amount = FoodAmount + Rnd (-(FoodAmount/3),+(FoodAmount/3))
			ScaleEntity Food\Model, (Food\Amount/300),1,(Food\Amount/300)
	Next 
;-----------------------


	;SetBuffer BackBuffer()
	Collisions ColType_Ant,ColType_Food,1,2



;;===========================;;
;;					MAIN LOOP					;;
;;------------------------------------------------------;;

	While Not KeyHit(1) 

	;Cls

		If KeyHit(57) Then
			For Hive.Ant = Each Ant
				Hive\Mode = "Looking For Food"
			Next
		End If 


		For Hive.Ant = Each Ant

			Select Hive\Mode

				Case "Looking For Food"
					If Hive\AttentionSpan > 0
						Gosub MoveAnt
						Hive\AttentionSpan = Hive\AttentionSpan - 1
					Else
						SeedRnd MilliSecs() : Delay 1
						TurnEntity Hive\Model,0,Rnd(-90,90),0
						Hive\AttentionSpan = (AntAttentionSpan * Rnd(1,AntAttentionRnd)) * FPS
					End If
					If EntityX (Hive\Model) > OutsideBoundry Or EntityX (Hive\Model) < -OutsideBoundry Or EntityZ (Hive\Model) > OutsideBoundry Or EntityZ (Hive\Model) < -OutsideBoundry Then
						Hive\Mode = "Gone Outside Boundry"
					End If
					If EntityCollided ( Hive\Model,ColType_Food ) Then
						Hive\Target= CollisionEntity (Hive\Model, 1)
						Hive\Mode = "Found Food"
					End If 

				Case "Gone Outside Boundry"
					PointEntity Hive\Model, Nest
					Hive\Mode = "Looking For Food"

				Case "Found Food"
					If EntityDistance (Hive\Model,Nest) <1.2 Then
						PointEntity Hive\Model, Hive\Target
					End If
					If EntityDistance (Hive\Model,Hive\Target) <1.2 Then
						For Food.FoodT = Each FoodT
							If Hive\Target = Food\Model Then Food\Amount = Food\Amount -1
							ScaleEntity Food\Model, (Food\Amount/300),1,(Food\Amount/300)
						Next
						PointEntity Hive\Model, Nest
					End If
					Gosub MoveAnt

			End Select

		Next



		For Food.FoodT = Each FoodT
			If Food\Amount < 0 Then
				For Hive.Ant = Each Ant
					If Hive\Target = Food\Model Then Hive\Mode = "Looking For Food"
				Next
				FreeEntity Food\Model
				Delete Food
			End If
		Next


		LimitFrameRate()
		UpdateWorld
		RenderWorld

;		i=10
;		For Food.FoodT = Each FoodT
;			Text 10,i, "Food Amount=" + Food\Amount
;			i = i+10
;		Next

		Flip
	Wend
	End

;;===========================;;
;;					FUNCTIONS					;;
;;------------------------------------------------------;;

Function LimitFrameRate()
If FrameTime = 0 Then
Period = 1000 / FPS
FrameTime = MilliSecs()
EndIf

; Make sure the framerate isn't above the specified setting
While (FrameTime + Period) > MilliSecs()
Delay 1
Wend
FrameTime = MilliSecs()
End Function

.MoveAnt
	MoveEntity Hive\Model,Rnd(-0.1,0.1),0,Hive\Speed
Return



The only borrowed code is the FPS Limiting Function. I am still working on it. Going to add scent trailers when I get the chance (like the Robs Caller Mode but more based on ants scent trails)