From 1bd165caf326435272cbf196bc88befc1d4cc1a6 Mon Sep 17 00:00:00 2001
From: StutenEXE <alexandre.bidaux78@gmail.com>
Date: Mon, 30 Dec 2024 12:13:50 +0100
Subject: [PATCH 1/7] Started hornets

---
 backend/server/websocketserver.go             |  10 +-
 backend/simulation/agent/agent.go             | 145 +++++++++++++++---
 backend/simulation/agent/beeAgent.go          | 116 +++-----------
 backend/simulation/agent/exagent.go           |   8 +-
 backend/simulation/agent/hornetAgent.go       | 122 +++++++++++++--
 .../agent/vision/equilateralTriangleVision.go |  46 ++++++
 .../agent/vision/explorerBeeVision.go         |  38 +----
 .../simulation/agent/vision/hornetVision.go   |  13 ++
 backend/simulation/environment/environment.go |  17 +-
 backend/simulation/environment/position.go    |  48 +++---
 backend/simulation/simulation.go              |  25 ++-
 backend/utils/configreader.go                 |   8 +
 12 files changed, 393 insertions(+), 203 deletions(-)
 create mode 100644 backend/simulation/agent/vision/equilateralTriangleVision.go
 create mode 100644 backend/simulation/agent/vision/hornetVision.go

diff --git a/backend/server/websocketserver.go b/backend/server/websocketserver.go
index f828a65..7ba89b1 100644
--- a/backend/server/websocketserver.go
+++ b/backend/server/websocketserver.go
@@ -26,8 +26,9 @@ func (server *WebSocketServer) newSimulation(conn *websocket.Conn) {
 		server.simulation.Stop()
 	}
 	nAgts := utils.GetNumberBees()
-	nObjs := utils.GetNumberBees()
-	server.simulation = simulation.NewSimulation(nAgts, nObjs, conn)
+	nObjs := utils.GetNumberFlowers()
+	nHorn := utils.GetNumberHornets()
+	server.simulation = simulation.NewSimulation(nAgts, nObjs, nHorn, conn)
 }
 
 func (server *WebSocketServer) launchSimulation(
@@ -35,8 +36,9 @@ func (server *WebSocketServer) launchSimulation(
 ) {
 	if server.simulation == nil {
 		nAgts := utils.GetNumberBees()
-		nObjs := utils.GetNumberBees()
-		server.simulation = simulation.NewSimulation(nAgts, nObjs, conn)
+		nObjs := utils.GetNumberFlowers()
+		nHorn := utils.GetNumberHornets()
+		server.simulation = simulation.NewSimulation(nAgts, nObjs, nHorn, conn)
 	}
 
 	if server.simulation.IsRunning() {
diff --git a/backend/simulation/agent/agent.go b/backend/simulation/agent/agent.go
index 339ae4e..0d864ac 100644
--- a/backend/simulation/agent/agent.go
+++ b/backend/simulation/agent/agent.go
@@ -8,6 +8,23 @@ import (
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 )
 
+type objectiveType string
+
+const (
+	None     objectiveType = "none"
+	Position objectiveType = "position"
+	Flower   objectiveType = "flower"
+	Hive     objectiveType = "hive"
+	Bee      objectiveType = "bee"
+)
+
+// BeeAgent structure to be marshalled in json
+type objective struct {
+	TargetedElem interface{}      `json:"targetedElem"`
+	Position     *envpkg.Position `json:"position"`
+	Type         objectiveType    `json:"type"`
+}
+
 // Abstract class used with a template pattern
 // - iagt: An interface representing the agent's actions
 // - id: An identifier for the agent
@@ -23,6 +40,8 @@ type Agent struct {
 	syncChan    chan bool
 	speed       int
 	lastPos     *envpkg.Position
+	alive       bool
+	objective   objective
 }
 
 // Agent is launched as a microservice
@@ -31,8 +50,8 @@ func (agt *Agent) Start() {
 	go func() {
 		for {
 			run := <-agt.syncChan
-			if !run {
-				agt.syncChan <- run
+			if !run || !agt.alive {
+				agt.syncChan <- !run || !agt.alive
 				break
 			}
 			agt.iagt.Percept()
@@ -67,51 +86,68 @@ func (agt Agent) Orientation() envpkg.Orientation {
 	return agt.orientation
 }
 
+// Additionally we check the agent is on the position
 func (agt *Agent) goNorth() bool {
-	success := agt.pos.GoNorth(agt.env.GetMap())
+	success := agt.pos.GoNorth(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.North
+	if agt.env.GetAt(agt.pos.X, agt.pos.Y) == nil {
+		agt.env.GetMap()[agt.pos.X][agt.pos.Y] = agt
+	}
 	return success
 }
 
+func (agt *Agent) forceAgentInPlace(env *envpkg.Environment) {
+	if env.GetAt(agt.pos.X, agt.pos.Y) == nil {
+		env.GetMap()[agt.pos.X][agt.pos.Y] = agt
+	}
+}
+
 func (agt *Agent) goSouth() bool {
-	success := agt.pos.GoSouth(agt.env.GetMap())
+	success := agt.pos.GoSouth(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.South
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goEast() bool {
-	success := agt.pos.GoEast(agt.env.GetMap())
+	success := agt.pos.GoEast(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.East
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goWest() bool {
-	success := agt.pos.GoWest(agt.env.GetMap())
+	success := agt.pos.GoWest(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.West
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goNorthEast() bool {
-	success := agt.pos.GoNorthEast(agt.env.GetMap())
+	success := agt.pos.GoNorthEast(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.NorthEast
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goSouthEast() bool {
-	success := agt.pos.GoSouthEast(agt.env.GetMap())
+	success := agt.pos.GoSouthEast(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.SouthEast
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goNorthWest() bool {
-	success := agt.pos.GoNorthWest(agt.env.GetMap())
+	success := agt.pos.GoNorthWest(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.NorthWest
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goSouthWest() bool {
-	success := agt.pos.GoSouthWest(agt.env.GetMap())
+	success := agt.pos.GoSouthWest(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.SouthWest
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
@@ -122,7 +158,7 @@ func (agt *Agent) gotoNextStepTowards(pos *envpkg.Position) {
 	if len(chain) > 0 && chain[0].Equal(agt.pos) {
 		chain = chain[1:]
 	}
-	fmt.Printf("\n[%s] Going to [%d %d] : [%d %d] -> ", agt.id, pos.X, pos.Y, agt.pos.X, agt.pos.Y)
+	//fmt.Printf("\n[%s] Going to [%d %d] : [%d %d] -> ", agt.id, pos.X, pos.Y, agt.pos.X, agt.pos.Y)
 	for i := 0; i < agt.speed && i < len(chain); i++ {
 		pos := chain[i]
 		if agt.pos.X < pos.X {
@@ -151,6 +187,72 @@ func (agt *Agent) gotoNextStepTowards(pos *envpkg.Position) {
 	}
 }
 
+func (agt *Agent) wander() {
+	if agt.pos == nil {
+		return
+	}
+	var closestBorder *envpkg.Position = nil
+	minBorderDistance := agt.pos.DistanceFrom(&envpkg.Position{X: 0, Y: 0})
+	isCloseToBorder := false
+	// If we are close to the border, we go in the opposite direction
+	// We put -1 in the list to test the flat border cases
+	for _, x := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
+		for _, y := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
+			isCorner := true
+			if x == -1 && y == -1 {
+				continue
+			}
+			// Flat north or south border
+			if x == -1 {
+				x = agt.pos.X
+				isCorner = false
+			}
+			if y == -1 {
+				// Flat west or east border
+				y = agt.pos.Y
+				isCorner = false
+			}
+			// Corner case
+			distance := agt.pos.DistanceFrom(&envpkg.Position{X: x, Y: y})
+			isCloseToBorder = distance < float64(agt.speed)
+			// We allow some leeway to border cases to avoid getting stuck
+			if (isCloseToBorder && distance < minBorderDistance) || (isCloseToBorder && isCorner && distance <= minBorderDistance) {
+				closestBorder = &envpkg.Position{X: x, Y: y}
+				minBorderDistance = distance
+			}
+		}
+	}
+	if closestBorder != nil {
+		// If we are too close to the border, we go to the opposite side
+		keepAwayFromBorderPos := agt.pos.Copy()
+		if closestBorder.X == 0 {
+			keepAwayFromBorderPos.GoEast(nil, nil)
+		} else if closestBorder.X == agt.env.GetMapDimension()-1 {
+			keepAwayFromBorderPos.GoWest(nil, nil)
+		}
+		if closestBorder.Y == 0 {
+			keepAwayFromBorderPos.GoSouth(nil, nil)
+		} else if closestBorder.Y == agt.env.GetMapDimension()-1 {
+			keepAwayFromBorderPos.GoNorth(nil, nil)
+		}
+		agt.objective.Position = keepAwayFromBorderPos.Copy()
+		agt.objective.Type = Position
+		fmt.Printf("[%s] Too close to border (%d %d), going to (%d %d)\n", agt.id, closestBorder.X, closestBorder.Y, agt.objective.Position.X, agt.objective.Position.Y)
+	} else {
+		// While we don't have an objective, we wander
+		for agt.objective.Type == None {
+			newObjective := agt.getNextWanderingPosition()
+			elemAtObjective := agt.env.GetAt(newObjective.X, newObjective.Y)
+			if elemAtObjective != nil {
+				continue
+			}
+			fmt.Printf("[%s] Wandering towards %v\n", agt.id, *newObjective)
+			agt.objective.Type = Position
+			agt.objective.Position = newObjective.Copy()
+		}
+	}
+}
+
 func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 	surroundings := agt.pos.GetNeighbours(agt.speed)
 	// We remove the positions that are occupied
@@ -225,21 +327,21 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 	for i := 0; i < agt.speed; i++ {
 		switch nextWanderingOrientation {
 		case envpkg.North:
-			newObjective.GoNorth(nil)
+			newObjective.GoNorth(nil, nil)
 		case envpkg.South:
-			newObjective.GoSouth(nil)
+			newObjective.GoSouth(nil, nil)
 		case envpkg.East:
-			newObjective.GoEast(nil)
+			newObjective.GoEast(nil, nil)
 		case envpkg.West:
-			newObjective.GoWest(nil)
+			newObjective.GoWest(nil, nil)
 		case envpkg.NorthEast:
-			newObjective.GoNorthEast(nil)
+			newObjective.GoNorthEast(nil, nil)
 		case envpkg.NorthWest:
-			newObjective.GoNorthWest(nil)
+			newObjective.GoNorthWest(nil, nil)
 		case envpkg.SouthEast:
-			newObjective.GoSouthEast(nil)
+			newObjective.GoSouthEast(nil, nil)
 		case envpkg.SouthWest:
-			newObjective.GoSouthWest(nil)
+			newObjective.GoSouthWest(nil, nil)
 		}
 	}
 	// Find the closest available position in surroundings
@@ -259,3 +361,8 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 
 	return newObjective
 }
+
+func (agt *Agent) die() {
+	agt.alive = false
+	agt.pos = nil
+}
diff --git a/backend/simulation/agent/beeAgent.go b/backend/simulation/agent/beeAgent.go
index bb0207d..06ac55a 100644
--- a/backend/simulation/agent/beeAgent.go
+++ b/backend/simulation/agent/beeAgent.go
@@ -30,28 +30,11 @@ type BeeAgent struct {
 	job                job
 	seenElems          []*vision.SeenElem
 	availablePositions []envpkg.Position
-	objective          objective
-}
-
-type objectiveType string
-
-const (
-	None     objectiveType = "none"
-	Position objectiveType = "position"
-	Flower   objectiveType = "flower"
-	Hive     objectiveType = "hive"
-)
-
-// BeeAgent structure to be marshalled in json
-type objective struct {
-	TargetedElem envpkg.IObject
-	Position     *envpkg.Position `json:"position"`
-	Type         objectiveType    `json:"type"`
 }
 
 type BeeAgentJson struct {
 	ID          string             `json:"id"`
-	Position    envpkg.Position    `json:"position"`
+	Position    *envpkg.Position   `json:"position"`
 	Orientation envpkg.Orientation `json:"orientation"`
 	SeenElems   []*vision.SeenElem `json:"seenElems"`
 	MaxNectar   int                `json:"maxNectar"`
@@ -69,16 +52,17 @@ func NewBeeAgent(id string, env *envpkg.Environment, syncChan chan bool, speed i
 		syncChan:   syncChan,
 		visionFunc: nil,
 		speed:      speed,
+		alive:      true,
+		objective:  objective{Position: nil, Type: None},
 	}
 	beeAgent.hive = hive
 	beeAgent.birthDate = dob
 	beeAgent.maxNectar = maxnectar
 	beeAgent.job = job
 	beeAgent.nectar = 0
-	beeAgent.objective = objective{Position: nil, Type: None}
 	beeAgent.availablePositions = []envpkg.Position{}
 	beeAgent.seenElems = []*vision.SeenElem{}
-	beeAgent.pos = hive.Position().Copy()
+	//beeAgent.pos = hive.Position().Copy()
 	return beeAgent
 }
 
@@ -103,11 +87,13 @@ func (agt *BeeAgent) Act() {
 	}
 }
 
+func (agt *BeeAgent) Kill() {
+	agt.env.RemoveAgent(agt)
+	agt.die()
+}
+
 func (agt *BeeAgent) hasFlowerObjective() bool {
-	if agt.objective.Type == Flower {
-		return true
-	}
-	return false
+	return agt.objective.Type == Flower
 }
 
 func (agt *BeeAgent) foragerPerception() {
@@ -165,66 +151,7 @@ func (agt *BeeAgent) foragerDeliberation() {
 	}
 	// If has no objective, wander
 	if agt.objective.Type == None {
-		var closestBorder *envpkg.Position = nil
-		minBorderDistance := agt.pos.DistanceFrom(&envpkg.Position{X: 0, Y: 0})
-		isCloseToBorder := false
-		// If we are close to the border, we go in the opposite direction
-		// We put -1 in the list to test the flat border cases
-		for _, x := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
-			for _, y := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
-				isCorner := true
-				if x == -1 && y == -1 {
-					continue
-				}
-				// Flat north or south border
-				if x == -1 {
-					x = agt.pos.X
-					isCorner = false
-				}
-				if y == -1 {
-					// Flat west or east border
-					y = agt.pos.Y
-					isCorner = false
-				}
-				// Corner case
-				distance := agt.pos.DistanceFrom(&envpkg.Position{X: x, Y: y})
-				isCloseToBorder = distance < float64(agt.speed)
-				// We allow some leeway to border cases to avoid getting stuck
-				if (isCloseToBorder && distance < minBorderDistance) || (isCloseToBorder && isCorner && distance <= minBorderDistance) {
-					closestBorder = &envpkg.Position{X: x, Y: y}
-					minBorderDistance = distance
-				}
-			}
-		}
-		if closestBorder != nil {
-			// If we are too close to the border, we go to the opposite side
-			keepAwayFromBorderPos := agt.pos.Copy()
-			if closestBorder.X == 0 {
-				keepAwayFromBorderPos.GoEast(nil)
-			} else if closestBorder.X == agt.env.GetMapDimension()-1 {
-				keepAwayFromBorderPos.GoWest(nil)
-			}
-			if closestBorder.Y == 0 {
-				keepAwayFromBorderPos.GoSouth(nil)
-			} else if closestBorder.Y == agt.env.GetMapDimension()-1 {
-				keepAwayFromBorderPos.GoNorth(nil)
-			}
-			agt.objective.Position = keepAwayFromBorderPos.Copy()
-			agt.objective.Type = Position
-			fmt.Printf("[%s] Too close to border (%d %d), going to (%d %d)\n", agt.id, closestBorder.X, closestBorder.Y, agt.objective.Position.X, agt.objective.Position.Y)
-		} else {
-			// While we don't have an objective, we wander
-			for agt.objective.Type == None {
-				newObjective := agt.getNextWanderingPosition()
-				elemAtObjective := agt.env.GetAt(newObjective.X, newObjective.Y)
-				if elemAtObjective != nil {
-					continue
-				}
-				fmt.Printf("[%s] Wandering towards %v\n", agt.id, *newObjective)
-				agt.objective.Type = Position
-				agt.objective.Position = newObjective.Copy()
-			}
-		}
+		agt.wander()
 	}
 }
 
@@ -237,6 +164,9 @@ func (agt *BeeAgent) foragerAction() {
 				agt.objective.Type = None
 			} else {
 				agt.gotoNextStepTowards(objf.Position.Copy())
+				if agt.pos.Equal(objf.Position) {
+					agt.objective.Type = None
+				}
 			}
 		case Flower:
 			if flower, ok := objf.TargetedElem.(*obj.Flower); ok {
@@ -244,7 +174,7 @@ func (agt *BeeAgent) foragerAction() {
 					agt.nectar += flower.RetreiveNectar(agt.maxNectar - agt.nectar)
 					agt.objective.Type = None
 				} else {
-					fmt.Printf("[%s] Going to flower %v\n", agt.id, objf.TargetedElem.ID())
+					fmt.Printf("[%s] Going to flower %v\n", agt.id, objf.TargetedElem.(envpkg.IObject).ID())
 					agt.gotoNextStepTowards(objf.Position.Copy())
 				}
 			}
@@ -256,7 +186,7 @@ func (agt *BeeAgent) foragerAction() {
 					agt.objective.Type = None
 				}
 			} else {
-				fmt.Printf("[%s] Going to hive %v\n", agt.id, objf.TargetedElem.ID())
+				fmt.Printf("[%s] Going to hive %v\n", agt.id, objf.TargetedElem.(envpkg.IObject).ID())
 				agt.gotoNextStepTowards(objf.Position.Copy())
 			}
 		}
@@ -275,25 +205,21 @@ func (agt *BeeAgent) workerAction() {
 
 		agt.pos = agt.hive.Position().Copy()
 		if xFactor == 0 {
-			agt.pos.GoWest(agt.env.GetMap())
-			agt.orientation = envpkg.West
+			agt.goWest()
 		} else {
-			agt.pos.GoEast(agt.env.GetMap())
-			agt.orientation = envpkg.East
+			agt.goEast()
 		}
 		if yFactor == 0 {
-			agt.pos.GoNorth(agt.env.GetMap())
-			agt.orientation = envpkg.North
+			agt.goNorth()
 		} else {
-			agt.pos.GoSouth(agt.env.GetMap())
-			agt.orientation = envpkg.South
+			agt.goSouth()
 		}
 	}
 }
 
 func (agt *BeeAgent) ToJsonObj() interface{} {
 	return BeeAgentJson{ID: string(agt.id),
-		Position:    *agt.pos,
+		Position:    agt.pos,
 		Orientation: agt.orientation,
 		SeenElems:   agt.seenElems,
 		MaxNectar:   agt.maxNectar,
diff --git a/backend/simulation/agent/exagent.go b/backend/simulation/agent/exagent.go
index 83f955b..b7d04bd 100644
--- a/backend/simulation/agent/exagent.go
+++ b/backend/simulation/agent/exagent.go
@@ -96,13 +96,13 @@ func (agt *ExAgent) Act() {
 	agt.value += agt.toAdd
 	switch agt.movement {
 	case envpkg.North:
-		agt.pos.GoNorth(agt.env.GetMap())
+		agt.goNorth()
 	case envpkg.East:
-		agt.pos.GoEast(agt.env.GetMap())
+		agt.goEast()
 	case envpkg.South:
-		agt.pos.GoSouth(agt.env.GetMap())
+		agt.goSouth()
 	case envpkg.West:
-		agt.pos.GoWest(agt.env.GetMap())
+		agt.goWest()
 	}
 	agt.orientation = agt.movement
 }
diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index 73f6c27..a51dd6e 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -1,8 +1,11 @@
 package agent
 
 import (
+	"fmt"
+	"math/rand"
 	"time"
 
+	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/agent/vision"
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 )
 
@@ -11,43 +14,128 @@ import (
 type HornetAgent struct {
 	Agent
 	birthDate time.Time
-	maxNectar int
-	job       int
+	seenElems []*vision.SeenElem
 }
 
 type HornetAgentJson struct {
-	ID string `json:"id"`
+	ID          string             `json:"id"`
+	Position    *envpkg.Position   `json:"position"`
+	Orientation envpkg.Orientation `json:"orientation"`
+	Objective   objective          `json:"objective"`
+	SeenElems   []*vision.SeenElem `json:"seenElems"`
 }
 
 func NewHornetAgent(id string, env *envpkg.Environment, syncChan chan bool, s int) *HornetAgent {
 	hAgent := &HornetAgent{}
 	hAgent.Agent = Agent{
-		iagt:     hAgent,
-		id:       envpkg.AgentID(id),
-		env:      env,
-		syncChan: syncChan,
-		speed:    s,
+		iagt:       hAgent,
+		id:         envpkg.AgentID(id),
+		env:        env,
+		syncChan:   syncChan,
+		speed:      s,
+		visionFunc: vision.HornetVision,
+		alive:      true,
+		objective:  objective{Position: nil, Type: None},
 	}
-
+	hAgent.birthDate = time.Now()
 	return hAgent
 }
 
-func (agt *HornetAgent) Start() {
-}
-
-func (agt *HornetAgent) Stop() {
-
-}
-
 func (agt *HornetAgent) Percept() {
+	if agt.pos == nil {
+		chance := rand.Intn(2)
+		if chance == 0 {
+			// either spawns at the top or the bottom or the left or the right
+			chance = rand.Intn(4)
+			x := 0
+			y := 0
+			switch chance {
+			case 0:
+				x = 0
+				y = rand.Intn(agt.env.GetMapDimension())
+				agt.orientation = envpkg.East
+			case 1:
+				x = agt.env.GetMapDimension() - 1
+				y = rand.Intn(agt.env.GetMapDimension())
+				agt.orientation = envpkg.West
+			case 2:
+				x = rand.Intn(agt.env.GetMapDimension())
+				y = 0
+				agt.orientation = envpkg.South
+			case 3:
+				x = rand.Intn(agt.env.GetMapDimension())
+				y = agt.env.GetMapDimension() - 1
+				agt.orientation = envpkg.North
+			}
+			agt.pos = envpkg.NewPosition(x, y, agt.env.GetMapDimension(), agt.env.GetMapDimension())
+			agt.env.AddAgent(agt)
+			fmt.Printf("[%s] Hornet spawned at %d %d\n", agt.id, x, y)
+			agt.objective.Type = None
+		}
+	}
+	if agt.pos != nil {
+		agt.seenElems = agt.see()
+	}
 }
 
+// The hornet agent always targets the closest bee
 func (agt *HornetAgent) Deliberate() {
+	distanceToTarget := float64(agt.env.GetMapDimension())
+	if agt.objective.Type == Bee {
+		if bee, ok := agt.objective.TargetedElem.(BeeAgent); ok {
+			distanceToTarget = bee.Position().DistanceFrom(agt.Position())
+		}
+	}
+	for _, seen := range agt.seenElems {
+		if seen.Elem != nil {
+			switch seen.Elem.(type) {
+			case *BeeAgent:
+				bee := seen.Elem.(*BeeAgent)
+				distance := bee.Position().DistanceFrom(agt.Position())
+				if distance < distanceToTarget {
+					agt.objective.Type = Bee
+					agt.objective.TargetedElem = bee
+					agt.objective.Position = bee.Position().Copy()
+					distanceToTarget = distance
+				}
+			case BeeAgent:
+				fmt.Println("=================================Hornet sees a bee wtf")
+			}
+		}
+	}
+	if agt.objective.Type == None {
+		agt.wander()
+	}
 }
 
 func (agt *HornetAgent) Act() {
+	if agt.objective.Type == None {
+		return
+	}
+	switch agt.objective.Type {
+	case Position:
+		agt.gotoNextStepTowards(agt.objective.Position.Copy())
+		if agt.Position().Equal(agt.objective.Position) {
+			agt.objective.Type = None
+		}
+	case Bee:
+		fmt.Println("=====================Hornet attacking bee !")
+		bee := agt.objective.TargetedElem.(*BeeAgent)
+		agt.gotoNextStepTowards(bee.Position().Copy())
+		if agt.Position().Near(bee.pos.Copy(), 1) {
+			bee.Kill()
+			agt.objective.Type = None
+			fmt.Println("=====================Hornet killed bee !!!")
+		}
+	}
 }
 
 func (agt *HornetAgent) ToJsonObj() interface{} {
-	return BeeAgentJson{ID: string(agt.id)}
+	return HornetAgentJson{
+		ID:          string(agt.id),
+		Position:    agt.pos,
+		Orientation: agt.orientation,
+		Objective:   agt.objective,
+		SeenElems:   agt.seenElems,
+	}
 }
diff --git a/backend/simulation/agent/vision/equilateralTriangleVision.go b/backend/simulation/agent/vision/equilateralTriangleVision.go
new file mode 100644
index 0000000..f24f61e
--- /dev/null
+++ b/backend/simulation/agent/vision/equilateralTriangleVision.go
@@ -0,0 +1,46 @@
+package vision
+
+import (
+	"math"
+	"sort"
+
+	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
+)
+
+// We decide that the returned list is sorted by proximity to the agent
+func EquilateralTriangleVision(agt envpkg.IAgent, env *envpkg.Environment, distance float64) []*SeenElem {
+	// Side of the triangle - formula for an equilateral triangle
+	sideSize := (2*(distance))/math.Sqrt(3) + 1
+	// Vision triangle coordinates
+	topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY := getTriangleCoordinates(*agt.Position(), distance, sideSize, agt.Orientation())
+	// Getting the bounding box of the triangle
+	minX := utils.Min(topCornerX, utils.Min(leftCornerX, rightCornerX))
+	maxX := utils.Max(topCornerX, utils.Max(leftCornerX, rightCornerX))
+	minY := utils.Min(topCornerY, utils.Min(leftCornerY, rightCornerY))
+	maxY := utils.Max(topCornerY, utils.Max(leftCornerY, rightCornerY))
+
+	// Contains all the elements seen by the agent
+	seenElems := make([]*SeenElem, 0)
+
+	addElemToList := func(x, y int) {
+		if env.IsValidPosition(x, y) {
+			if pointIsInTriangle(float64(x), float64(y), topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY) {
+				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, env.GetAt(x, y)))
+			}
+		}
+	}
+
+	for x := utils.Round(minX) + 1; x <= utils.Round(maxX)+1; x++ {
+		for y := utils.Round(minY); y <= utils.Round(maxY)+1; y++ {
+			addElemToList(x, y)
+		}
+	}
+
+	// Sorting the list by proximity to the agent
+	sort.Slice(seenElems, func(i, j int) bool {
+		return agt.Position().DistanceFrom(seenElems[i].Pos) < agt.Position().DistanceFrom(seenElems[j].Pos)
+	})
+
+	return seenElems
+}
diff --git a/backend/simulation/agent/vision/explorerBeeVision.go b/backend/simulation/agent/vision/explorerBeeVision.go
index d9e381a..27b91a4 100644
--- a/backend/simulation/agent/vision/explorerBeeVision.go
+++ b/backend/simulation/agent/vision/explorerBeeVision.go
@@ -1,9 +1,6 @@
 package vision
 
 import (
-	"math"
-	"sort"
-
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
 )
@@ -11,39 +8,6 @@ import (
 // For now we decide that the vision of a bee is an equilateral triangle
 // We decide that the returned list is sorted by proximity to the agent
 func ExplorerBeeVision(agt envpkg.IAgent, env *envpkg.Environment) []*SeenElem {
-	// Height of the triangle
 	distance := utils.GetBeeAgentVisionRange()
-	// Side of the triangle - formula for an equilateral triangle
-	sideSize := (2*(distance))/math.Sqrt(3) + 1
-	// Vision triangle coordinates
-	topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY := getTriangleCoordinates(*agt.Position(), distance, sideSize, agt.Orientation())
-	// Getting the bounding box of the triangle
-	minX := utils.Min(topCornerX, utils.Min(leftCornerX, rightCornerX))
-	maxX := utils.Max(topCornerX, utils.Max(leftCornerX, rightCornerX))
-	minY := utils.Min(topCornerY, utils.Min(leftCornerY, rightCornerY))
-	maxY := utils.Max(topCornerY, utils.Max(leftCornerY, rightCornerY))
-
-	// Contains all the elements seen by the agent
-	seenElems := make([]*SeenElem, 0)
-
-	addElemToList := func(x, y int) {
-		if env.IsValidPosition(x, y) {
-			if pointIsInTriangle(float64(x), float64(y), topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY) {
-				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, env.GetAt(x, y)))
-			}
-		}
-	}
-
-	for x := utils.Round(minX) + 1; x <= utils.Round(maxX)+1; x++ {
-		for y := utils.Round(minY); y <= utils.Round(maxY)+1; y++ {
-			addElemToList(x, y)
-		}
-	}
-
-	// Sorting the list by proximity to the agent
-	sort.Slice(seenElems, func(i, j int) bool {
-		return agt.Position().DistanceFrom(seenElems[i].Pos) < agt.Position().DistanceFrom(seenElems[j].Pos)
-	})
-
-	return seenElems
+	return EquilateralTriangleVision(agt, env, distance)
 }
diff --git a/backend/simulation/agent/vision/hornetVision.go b/backend/simulation/agent/vision/hornetVision.go
new file mode 100644
index 0000000..865dc62
--- /dev/null
+++ b/backend/simulation/agent/vision/hornetVision.go
@@ -0,0 +1,13 @@
+package vision
+
+import (
+	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
+)
+
+// For now we decide that the vision of a hornet is an equilateral triangle
+// We decide that the returned list is sorted by proximity to the agent
+func HornetVision(agt envpkg.IAgent, env *envpkg.Environment) []*SeenElem {
+	distance := utils.GetHornetAgentVisionRange()
+	return EquilateralTriangleVision(agt, env, distance)
+}
diff --git a/backend/simulation/environment/environment.go b/backend/simulation/environment/environment.go
index 0dbe031..6830ee8 100644
--- a/backend/simulation/environment/environment.go
+++ b/backend/simulation/environment/environment.go
@@ -51,7 +51,7 @@ func (env *Environment) IsValidPosition(x int, y int) bool {
 
 func (env *Environment) AddAgent(agt IAgent) bool {
 	pos := agt.Position()
-	if env.GetAt(pos.X, pos.Y) == nil {
+	if pos != nil && env.GetAt(pos.X, pos.Y) == nil {
 		//return false
 		env.grid[pos.X][pos.Y] = agt
 	}
@@ -59,6 +59,16 @@ func (env *Environment) AddAgent(agt IAgent) bool {
 	return true
 }
 
+func (env *Environment) RemoveAgent(agt IAgent) {
+	for i, a := range env.agts {
+		if a.ID() == agt.ID() {
+			env.agts = append(env.agts[:i], env.agts[i+1:]...)
+			env.grid[a.Position().X][a.Position().Y] = nil
+			break
+		}
+	}
+}
+
 func (env *Environment) AddObject(obj IObject) bool {
 	pos := obj.Position()
 	if env.GetAt(pos.X, pos.Y) != nil {
@@ -139,7 +149,10 @@ func (env *Environment) PathFinding(start *Position, end *Position, numberMoves
 	}
 
 	path := []*Position{}
-	currentNode := openList[len(openList)-1]
+	var currentNode *node = nil
+	if len(openList) > 0 {
+		currentNode = openList[len(openList)-1]
+	}
 	for currentNode != nil {
 		path = append([]*Position{currentNode.position.Copy()}, path...)
 		currentNode = currentNode.parent
diff --git a/backend/simulation/environment/position.go b/backend/simulation/environment/position.go
index 7fdd438..983bc6e 100644
--- a/backend/simulation/environment/position.go
+++ b/backend/simulation/environment/position.go
@@ -1,6 +1,7 @@
 package environment
 
 import (
+	"fmt"
 	"math"
 
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
@@ -19,7 +20,7 @@ func NewPosition(x int, y int, maxX int, maxY int) *Position {
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) move(grid [][]interface{}, newX int, newY int) bool {
+func (p *Position) move(grid [][]interface{}, newX int, newY int, elem interface{}) bool {
 	// Out of bounds
 	if newX < 0 || newY < 0 || newX >= p.maxX || newY >= p.maxY {
 		return false
@@ -30,7 +31,12 @@ func (p *Position) move(grid [][]interface{}, newX int, newY int) bool {
 	}
 	// Not a simulation
 	if grid != nil {
-		grid[p.X][p.Y], grid[newX][newY] = nil, grid[p.X][p.Y]
+		if grid[p.X][p.Y] == elem {
+			fmt.Println("Grid is not nil : ", grid[p.X][p.Y])
+			grid[p.X][p.Y], grid[newX][newY] = nil, grid[p.X][p.Y]
+		} else {
+			grid[newX][newY] = elem
+		}
 	}
 	p.X = newX
 	p.Y = newY
@@ -38,43 +44,43 @@ func (p *Position) move(grid [][]interface{}, newX int, newY int) bool {
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoNorth(grid [][]interface{}) bool {
-	return p.move(grid, p.X, p.Y-1)
+func (p *Position) GoNorth(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X, p.Y-1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoSouth(grid [][]interface{}) bool {
-	return p.move(grid, p.X, p.Y+1)
+func (p *Position) GoSouth(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X, p.Y+1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoWest(grid [][]interface{}) bool {
-	return p.move(grid, p.X-1, p.Y)
+func (p *Position) GoWest(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X-1, p.Y, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoEast(grid [][]interface{}) bool {
-	return p.move(grid, p.X+1, p.Y)
+func (p *Position) GoEast(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X+1, p.Y, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoNorthEast(grid [][]interface{}) bool {
-	return p.move(grid, p.X+1, p.Y-1)
+func (p *Position) GoNorthEast(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X+1, p.Y-1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoNorthWest(grid [][]interface{}) bool {
-	return p.move(grid, p.X-1, p.Y-1)
+func (p *Position) GoNorthWest(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X-1, p.Y-1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoSouthEast(grid [][]interface{}) bool {
-	return p.move(grid, p.X+1, p.Y+1)
+func (p *Position) GoSouthEast(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X+1, p.Y+1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoSouthWest(grid [][]interface{}) bool {
-	return p.move(grid, p.X-1, p.Y+1)
+func (p *Position) GoSouthWest(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X-1, p.Y+1, elem)
 }
 
 func (p Position) DistanceFrom(p2 *Position) float64 {
@@ -86,9 +92,9 @@ func (p Position) Near(p2 *Position, distance int) bool {
 }
 
 // Returns the mirrored position of the point according to this point
-func (p Position) GetSymmetricOfPoint(origin Position) *Position {
-	symX := 2*origin.X - p.X
-	symY := 2*origin.Y - p.Y
+func (p Position) GetSymmetricOfPoint(p2 Position) *Position {
+	symX := 2*p.X - p2.X
+	symY := 2*p.Y - p2.Y
 	return &Position{X: symX, Y: symY, maxX: p.maxX, maxY: p.maxY}
 }
 
diff --git a/backend/simulation/simulation.go b/backend/simulation/simulation.go
index 5e9af8f..64fe28e 100644
--- a/backend/simulation/simulation.go
+++ b/backend/simulation/simulation.go
@@ -30,7 +30,7 @@ type SimulationJson struct {
 	Environment interface{}   `json:"environment"`
 }
 
-func NewSimulation(nbees int, nflowers int, maWs *websocket.Conn) *Simulation {
+func NewSimulation(nbees int, nflowers int, nhornets int, maWs *websocket.Conn) *Simulation {
 	simu := &Simulation{}
 	simu.ws = maWs
 	env := envpkg.NewEnvironment([]envpkg.IAgent{}, []envpkg.IObject{})
@@ -116,6 +116,17 @@ func NewSimulation(nbees int, nflowers int, maWs *websocket.Conn) *Simulation {
 		simu.env.AddAgent(agt)
 	}
 
+	for i := 0; i < nhornets; i++ {
+		// Creating a hornet
+		id := fmt.Sprintf("Hornet #%d", i)
+		syncChan := make(chan bool)
+		agt := agt.NewHornetAgent(id, simu.env, syncChan, rand.Intn(2)+1)
+		// ajout de l'agent à la simulation
+		simu.agts = append(simu.agts, agt)
+		// ajout de l'agent à l'environnement
+		//simu.env.AddAgent(agt) // Pas encore car pas spawn
+	}
+
 	simu.sendState()
 	return simu
 }
@@ -147,11 +158,14 @@ func (simu *Simulation) Run(maWs *websocket.Conn) {
 
 	// Boucle de simulation
 	for simu.IsRunning() {
-		for _, agt := range simu.agts {
+		for i, agt := range simu.agts {
 			c := agt.GetSyncChan()
 			simu.env.Lock()
 			c <- true
-			<-c
+			// If dead
+			if !<-c {
+				simu.agts = append(simu.agts[:i], simu.agts[i+1:]...)
+			}
 			simu.env.Unlock()
 			time.Sleep(time.Second / 100) // 100 Tour / Sec
 		}
@@ -196,7 +210,7 @@ func (simu *Simulation) print() {
 	startTime := time.Now()
 	for simu.IsRunning() {
 		fmt.Printf("\rRunning simulation for %vms...  - ", time.Since(startTime).Milliseconds())
-		time.Sleep(time.Second / 60) // 60 fps
+		time.Sleep(time.Second / 30) // 60 fps
 	}
 }
 
@@ -212,5 +226,8 @@ func (simu *Simulation) ToJsonObj() SimulationJson {
 		objects = append(objects, obj.ToJsonObj())
 	}
 
+	simu.env.Lock()
+	defer simu.env.Unlock()
+
 	return SimulationJson{Agents: agents, Objects: objects, Environment: simu.env.ToJsonObj()}
 }
diff --git a/backend/utils/configreader.go b/backend/utils/configreader.go
index a93beab..239dba5 100644
--- a/backend/utils/configreader.go
+++ b/backend/utils/configreader.go
@@ -57,6 +57,10 @@ func GetNumberBees() int {
 	return getIntAttributeFromConfigFile("NumberBees")
 }
 
+func GetNumberHornets() int {
+	return getIntAttributeFromConfigFile("NumberHornets")
+}
+
 func GetMaxNectar() int {
 	return getIntAttributeFromConfigFile("MaxNectar")
 }
@@ -81,6 +85,10 @@ func GetBeeAgentVisionRange() float64 {
 	return getFloat64AttributeFromConfigFile("BeeAgentVisionRange")
 }
 
+func GetHornetAgentVisionRange() float64 {
+	return getFloat64AttributeFromConfigFile("HornetAgentVisionRange")
+}
+
 func GetExName() string {
 	return getStringAttributeFromConfigFile("ExName")
 }
-- 
GitLab


From a07cf9d7685363007995a42d406032c233def220 Mon Sep 17 00:00:00 2001
From: StutenEXE <alexandre.bidaux78@gmail.com>
Date: Mon, 30 Dec 2024 12:13:50 +0100
Subject: [PATCH 2/7] Started hornets

---
 backend/server/websocketserver.go             |  10 +-
 backend/simulation/agent/agent.go             | 145 +++++++++++++++---
 backend/simulation/agent/beeAgent.go          | 116 +++-----------
 backend/simulation/agent/exagent.go           |   8 +-
 backend/simulation/agent/hornetAgent.go       | 122 +++++++++++++--
 .../agent/vision/equilateralTriangleVision.go |  46 ++++++
 .../agent/vision/explorerBeeVision.go         |  38 +----
 .../simulation/agent/vision/hornetVision.go   |  13 ++
 backend/simulation/environment/environment.go |  17 +-
 backend/simulation/environment/position.go    |  48 +++---
 backend/simulation/simulation.go              |  25 ++-
 backend/utils/configreader.go                 |   8 +
 config.yaml                                   |   6 +-
 frontend-ws-test/index.html                   |  25 ++-
 14 files changed, 420 insertions(+), 207 deletions(-)
 create mode 100644 backend/simulation/agent/vision/equilateralTriangleVision.go
 create mode 100644 backend/simulation/agent/vision/hornetVision.go

diff --git a/backend/server/websocketserver.go b/backend/server/websocketserver.go
index f828a65..7ba89b1 100644
--- a/backend/server/websocketserver.go
+++ b/backend/server/websocketserver.go
@@ -26,8 +26,9 @@ func (server *WebSocketServer) newSimulation(conn *websocket.Conn) {
 		server.simulation.Stop()
 	}
 	nAgts := utils.GetNumberBees()
-	nObjs := utils.GetNumberBees()
-	server.simulation = simulation.NewSimulation(nAgts, nObjs, conn)
+	nObjs := utils.GetNumberFlowers()
+	nHorn := utils.GetNumberHornets()
+	server.simulation = simulation.NewSimulation(nAgts, nObjs, nHorn, conn)
 }
 
 func (server *WebSocketServer) launchSimulation(
@@ -35,8 +36,9 @@ func (server *WebSocketServer) launchSimulation(
 ) {
 	if server.simulation == nil {
 		nAgts := utils.GetNumberBees()
-		nObjs := utils.GetNumberBees()
-		server.simulation = simulation.NewSimulation(nAgts, nObjs, conn)
+		nObjs := utils.GetNumberFlowers()
+		nHorn := utils.GetNumberHornets()
+		server.simulation = simulation.NewSimulation(nAgts, nObjs, nHorn, conn)
 	}
 
 	if server.simulation.IsRunning() {
diff --git a/backend/simulation/agent/agent.go b/backend/simulation/agent/agent.go
index 339ae4e..0d864ac 100644
--- a/backend/simulation/agent/agent.go
+++ b/backend/simulation/agent/agent.go
@@ -8,6 +8,23 @@ import (
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 )
 
+type objectiveType string
+
+const (
+	None     objectiveType = "none"
+	Position objectiveType = "position"
+	Flower   objectiveType = "flower"
+	Hive     objectiveType = "hive"
+	Bee      objectiveType = "bee"
+)
+
+// BeeAgent structure to be marshalled in json
+type objective struct {
+	TargetedElem interface{}      `json:"targetedElem"`
+	Position     *envpkg.Position `json:"position"`
+	Type         objectiveType    `json:"type"`
+}
+
 // Abstract class used with a template pattern
 // - iagt: An interface representing the agent's actions
 // - id: An identifier for the agent
@@ -23,6 +40,8 @@ type Agent struct {
 	syncChan    chan bool
 	speed       int
 	lastPos     *envpkg.Position
+	alive       bool
+	objective   objective
 }
 
 // Agent is launched as a microservice
@@ -31,8 +50,8 @@ func (agt *Agent) Start() {
 	go func() {
 		for {
 			run := <-agt.syncChan
-			if !run {
-				agt.syncChan <- run
+			if !run || !agt.alive {
+				agt.syncChan <- !run || !agt.alive
 				break
 			}
 			agt.iagt.Percept()
@@ -67,51 +86,68 @@ func (agt Agent) Orientation() envpkg.Orientation {
 	return agt.orientation
 }
 
+// Additionally we check the agent is on the position
 func (agt *Agent) goNorth() bool {
-	success := agt.pos.GoNorth(agt.env.GetMap())
+	success := agt.pos.GoNorth(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.North
+	if agt.env.GetAt(agt.pos.X, agt.pos.Y) == nil {
+		agt.env.GetMap()[agt.pos.X][agt.pos.Y] = agt
+	}
 	return success
 }
 
+func (agt *Agent) forceAgentInPlace(env *envpkg.Environment) {
+	if env.GetAt(agt.pos.X, agt.pos.Y) == nil {
+		env.GetMap()[agt.pos.X][agt.pos.Y] = agt
+	}
+}
+
 func (agt *Agent) goSouth() bool {
-	success := agt.pos.GoSouth(agt.env.GetMap())
+	success := agt.pos.GoSouth(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.South
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goEast() bool {
-	success := agt.pos.GoEast(agt.env.GetMap())
+	success := agt.pos.GoEast(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.East
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goWest() bool {
-	success := agt.pos.GoWest(agt.env.GetMap())
+	success := agt.pos.GoWest(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.West
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goNorthEast() bool {
-	success := agt.pos.GoNorthEast(agt.env.GetMap())
+	success := agt.pos.GoNorthEast(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.NorthEast
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goSouthEast() bool {
-	success := agt.pos.GoSouthEast(agt.env.GetMap())
+	success := agt.pos.GoSouthEast(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.SouthEast
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goNorthWest() bool {
-	success := agt.pos.GoNorthWest(agt.env.GetMap())
+	success := agt.pos.GoNorthWest(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.NorthWest
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
 func (agt *Agent) goSouthWest() bool {
-	success := agt.pos.GoSouthWest(agt.env.GetMap())
+	success := agt.pos.GoSouthWest(agt.env.GetMap(), agt)
 	agt.orientation = envpkg.SouthWest
+	agt.forceAgentInPlace(agt.env)
 	return success
 }
 
@@ -122,7 +158,7 @@ func (agt *Agent) gotoNextStepTowards(pos *envpkg.Position) {
 	if len(chain) > 0 && chain[0].Equal(agt.pos) {
 		chain = chain[1:]
 	}
-	fmt.Printf("\n[%s] Going to [%d %d] : [%d %d] -> ", agt.id, pos.X, pos.Y, agt.pos.X, agt.pos.Y)
+	//fmt.Printf("\n[%s] Going to [%d %d] : [%d %d] -> ", agt.id, pos.X, pos.Y, agt.pos.X, agt.pos.Y)
 	for i := 0; i < agt.speed && i < len(chain); i++ {
 		pos := chain[i]
 		if agt.pos.X < pos.X {
@@ -151,6 +187,72 @@ func (agt *Agent) gotoNextStepTowards(pos *envpkg.Position) {
 	}
 }
 
+func (agt *Agent) wander() {
+	if agt.pos == nil {
+		return
+	}
+	var closestBorder *envpkg.Position = nil
+	minBorderDistance := agt.pos.DistanceFrom(&envpkg.Position{X: 0, Y: 0})
+	isCloseToBorder := false
+	// If we are close to the border, we go in the opposite direction
+	// We put -1 in the list to test the flat border cases
+	for _, x := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
+		for _, y := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
+			isCorner := true
+			if x == -1 && y == -1 {
+				continue
+			}
+			// Flat north or south border
+			if x == -1 {
+				x = agt.pos.X
+				isCorner = false
+			}
+			if y == -1 {
+				// Flat west or east border
+				y = agt.pos.Y
+				isCorner = false
+			}
+			// Corner case
+			distance := agt.pos.DistanceFrom(&envpkg.Position{X: x, Y: y})
+			isCloseToBorder = distance < float64(agt.speed)
+			// We allow some leeway to border cases to avoid getting stuck
+			if (isCloseToBorder && distance < minBorderDistance) || (isCloseToBorder && isCorner && distance <= minBorderDistance) {
+				closestBorder = &envpkg.Position{X: x, Y: y}
+				minBorderDistance = distance
+			}
+		}
+	}
+	if closestBorder != nil {
+		// If we are too close to the border, we go to the opposite side
+		keepAwayFromBorderPos := agt.pos.Copy()
+		if closestBorder.X == 0 {
+			keepAwayFromBorderPos.GoEast(nil, nil)
+		} else if closestBorder.X == agt.env.GetMapDimension()-1 {
+			keepAwayFromBorderPos.GoWest(nil, nil)
+		}
+		if closestBorder.Y == 0 {
+			keepAwayFromBorderPos.GoSouth(nil, nil)
+		} else if closestBorder.Y == agt.env.GetMapDimension()-1 {
+			keepAwayFromBorderPos.GoNorth(nil, nil)
+		}
+		agt.objective.Position = keepAwayFromBorderPos.Copy()
+		agt.objective.Type = Position
+		fmt.Printf("[%s] Too close to border (%d %d), going to (%d %d)\n", agt.id, closestBorder.X, closestBorder.Y, agt.objective.Position.X, agt.objective.Position.Y)
+	} else {
+		// While we don't have an objective, we wander
+		for agt.objective.Type == None {
+			newObjective := agt.getNextWanderingPosition()
+			elemAtObjective := agt.env.GetAt(newObjective.X, newObjective.Y)
+			if elemAtObjective != nil {
+				continue
+			}
+			fmt.Printf("[%s] Wandering towards %v\n", agt.id, *newObjective)
+			agt.objective.Type = Position
+			agt.objective.Position = newObjective.Copy()
+		}
+	}
+}
+
 func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 	surroundings := agt.pos.GetNeighbours(agt.speed)
 	// We remove the positions that are occupied
@@ -225,21 +327,21 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 	for i := 0; i < agt.speed; i++ {
 		switch nextWanderingOrientation {
 		case envpkg.North:
-			newObjective.GoNorth(nil)
+			newObjective.GoNorth(nil, nil)
 		case envpkg.South:
-			newObjective.GoSouth(nil)
+			newObjective.GoSouth(nil, nil)
 		case envpkg.East:
-			newObjective.GoEast(nil)
+			newObjective.GoEast(nil, nil)
 		case envpkg.West:
-			newObjective.GoWest(nil)
+			newObjective.GoWest(nil, nil)
 		case envpkg.NorthEast:
-			newObjective.GoNorthEast(nil)
+			newObjective.GoNorthEast(nil, nil)
 		case envpkg.NorthWest:
-			newObjective.GoNorthWest(nil)
+			newObjective.GoNorthWest(nil, nil)
 		case envpkg.SouthEast:
-			newObjective.GoSouthEast(nil)
+			newObjective.GoSouthEast(nil, nil)
 		case envpkg.SouthWest:
-			newObjective.GoSouthWest(nil)
+			newObjective.GoSouthWest(nil, nil)
 		}
 	}
 	// Find the closest available position in surroundings
@@ -259,3 +361,8 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 
 	return newObjective
 }
+
+func (agt *Agent) die() {
+	agt.alive = false
+	agt.pos = nil
+}
diff --git a/backend/simulation/agent/beeAgent.go b/backend/simulation/agent/beeAgent.go
index bb0207d..06ac55a 100644
--- a/backend/simulation/agent/beeAgent.go
+++ b/backend/simulation/agent/beeAgent.go
@@ -30,28 +30,11 @@ type BeeAgent struct {
 	job                job
 	seenElems          []*vision.SeenElem
 	availablePositions []envpkg.Position
-	objective          objective
-}
-
-type objectiveType string
-
-const (
-	None     objectiveType = "none"
-	Position objectiveType = "position"
-	Flower   objectiveType = "flower"
-	Hive     objectiveType = "hive"
-)
-
-// BeeAgent structure to be marshalled in json
-type objective struct {
-	TargetedElem envpkg.IObject
-	Position     *envpkg.Position `json:"position"`
-	Type         objectiveType    `json:"type"`
 }
 
 type BeeAgentJson struct {
 	ID          string             `json:"id"`
-	Position    envpkg.Position    `json:"position"`
+	Position    *envpkg.Position   `json:"position"`
 	Orientation envpkg.Orientation `json:"orientation"`
 	SeenElems   []*vision.SeenElem `json:"seenElems"`
 	MaxNectar   int                `json:"maxNectar"`
@@ -69,16 +52,17 @@ func NewBeeAgent(id string, env *envpkg.Environment, syncChan chan bool, speed i
 		syncChan:   syncChan,
 		visionFunc: nil,
 		speed:      speed,
+		alive:      true,
+		objective:  objective{Position: nil, Type: None},
 	}
 	beeAgent.hive = hive
 	beeAgent.birthDate = dob
 	beeAgent.maxNectar = maxnectar
 	beeAgent.job = job
 	beeAgent.nectar = 0
-	beeAgent.objective = objective{Position: nil, Type: None}
 	beeAgent.availablePositions = []envpkg.Position{}
 	beeAgent.seenElems = []*vision.SeenElem{}
-	beeAgent.pos = hive.Position().Copy()
+	//beeAgent.pos = hive.Position().Copy()
 	return beeAgent
 }
 
@@ -103,11 +87,13 @@ func (agt *BeeAgent) Act() {
 	}
 }
 
+func (agt *BeeAgent) Kill() {
+	agt.env.RemoveAgent(agt)
+	agt.die()
+}
+
 func (agt *BeeAgent) hasFlowerObjective() bool {
-	if agt.objective.Type == Flower {
-		return true
-	}
-	return false
+	return agt.objective.Type == Flower
 }
 
 func (agt *BeeAgent) foragerPerception() {
@@ -165,66 +151,7 @@ func (agt *BeeAgent) foragerDeliberation() {
 	}
 	// If has no objective, wander
 	if agt.objective.Type == None {
-		var closestBorder *envpkg.Position = nil
-		minBorderDistance := agt.pos.DistanceFrom(&envpkg.Position{X: 0, Y: 0})
-		isCloseToBorder := false
-		// If we are close to the border, we go in the opposite direction
-		// We put -1 in the list to test the flat border cases
-		for _, x := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
-			for _, y := range []int{-1, 0, agt.env.GetMapDimension() - 1} {
-				isCorner := true
-				if x == -1 && y == -1 {
-					continue
-				}
-				// Flat north or south border
-				if x == -1 {
-					x = agt.pos.X
-					isCorner = false
-				}
-				if y == -1 {
-					// Flat west or east border
-					y = agt.pos.Y
-					isCorner = false
-				}
-				// Corner case
-				distance := agt.pos.DistanceFrom(&envpkg.Position{X: x, Y: y})
-				isCloseToBorder = distance < float64(agt.speed)
-				// We allow some leeway to border cases to avoid getting stuck
-				if (isCloseToBorder && distance < minBorderDistance) || (isCloseToBorder && isCorner && distance <= minBorderDistance) {
-					closestBorder = &envpkg.Position{X: x, Y: y}
-					minBorderDistance = distance
-				}
-			}
-		}
-		if closestBorder != nil {
-			// If we are too close to the border, we go to the opposite side
-			keepAwayFromBorderPos := agt.pos.Copy()
-			if closestBorder.X == 0 {
-				keepAwayFromBorderPos.GoEast(nil)
-			} else if closestBorder.X == agt.env.GetMapDimension()-1 {
-				keepAwayFromBorderPos.GoWest(nil)
-			}
-			if closestBorder.Y == 0 {
-				keepAwayFromBorderPos.GoSouth(nil)
-			} else if closestBorder.Y == agt.env.GetMapDimension()-1 {
-				keepAwayFromBorderPos.GoNorth(nil)
-			}
-			agt.objective.Position = keepAwayFromBorderPos.Copy()
-			agt.objective.Type = Position
-			fmt.Printf("[%s] Too close to border (%d %d), going to (%d %d)\n", agt.id, closestBorder.X, closestBorder.Y, agt.objective.Position.X, agt.objective.Position.Y)
-		} else {
-			// While we don't have an objective, we wander
-			for agt.objective.Type == None {
-				newObjective := agt.getNextWanderingPosition()
-				elemAtObjective := agt.env.GetAt(newObjective.X, newObjective.Y)
-				if elemAtObjective != nil {
-					continue
-				}
-				fmt.Printf("[%s] Wandering towards %v\n", agt.id, *newObjective)
-				agt.objective.Type = Position
-				agt.objective.Position = newObjective.Copy()
-			}
-		}
+		agt.wander()
 	}
 }
 
@@ -237,6 +164,9 @@ func (agt *BeeAgent) foragerAction() {
 				agt.objective.Type = None
 			} else {
 				agt.gotoNextStepTowards(objf.Position.Copy())
+				if agt.pos.Equal(objf.Position) {
+					agt.objective.Type = None
+				}
 			}
 		case Flower:
 			if flower, ok := objf.TargetedElem.(*obj.Flower); ok {
@@ -244,7 +174,7 @@ func (agt *BeeAgent) foragerAction() {
 					agt.nectar += flower.RetreiveNectar(agt.maxNectar - agt.nectar)
 					agt.objective.Type = None
 				} else {
-					fmt.Printf("[%s] Going to flower %v\n", agt.id, objf.TargetedElem.ID())
+					fmt.Printf("[%s] Going to flower %v\n", agt.id, objf.TargetedElem.(envpkg.IObject).ID())
 					agt.gotoNextStepTowards(objf.Position.Copy())
 				}
 			}
@@ -256,7 +186,7 @@ func (agt *BeeAgent) foragerAction() {
 					agt.objective.Type = None
 				}
 			} else {
-				fmt.Printf("[%s] Going to hive %v\n", agt.id, objf.TargetedElem.ID())
+				fmt.Printf("[%s] Going to hive %v\n", agt.id, objf.TargetedElem.(envpkg.IObject).ID())
 				agt.gotoNextStepTowards(objf.Position.Copy())
 			}
 		}
@@ -275,25 +205,21 @@ func (agt *BeeAgent) workerAction() {
 
 		agt.pos = agt.hive.Position().Copy()
 		if xFactor == 0 {
-			agt.pos.GoWest(agt.env.GetMap())
-			agt.orientation = envpkg.West
+			agt.goWest()
 		} else {
-			agt.pos.GoEast(agt.env.GetMap())
-			agt.orientation = envpkg.East
+			agt.goEast()
 		}
 		if yFactor == 0 {
-			agt.pos.GoNorth(agt.env.GetMap())
-			agt.orientation = envpkg.North
+			agt.goNorth()
 		} else {
-			agt.pos.GoSouth(agt.env.GetMap())
-			agt.orientation = envpkg.South
+			agt.goSouth()
 		}
 	}
 }
 
 func (agt *BeeAgent) ToJsonObj() interface{} {
 	return BeeAgentJson{ID: string(agt.id),
-		Position:    *agt.pos,
+		Position:    agt.pos,
 		Orientation: agt.orientation,
 		SeenElems:   agt.seenElems,
 		MaxNectar:   agt.maxNectar,
diff --git a/backend/simulation/agent/exagent.go b/backend/simulation/agent/exagent.go
index 83f955b..b7d04bd 100644
--- a/backend/simulation/agent/exagent.go
+++ b/backend/simulation/agent/exagent.go
@@ -96,13 +96,13 @@ func (agt *ExAgent) Act() {
 	agt.value += agt.toAdd
 	switch agt.movement {
 	case envpkg.North:
-		agt.pos.GoNorth(agt.env.GetMap())
+		agt.goNorth()
 	case envpkg.East:
-		agt.pos.GoEast(agt.env.GetMap())
+		agt.goEast()
 	case envpkg.South:
-		agt.pos.GoSouth(agt.env.GetMap())
+		agt.goSouth()
 	case envpkg.West:
-		agt.pos.GoWest(agt.env.GetMap())
+		agt.goWest()
 	}
 	agt.orientation = agt.movement
 }
diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index 73f6c27..a51dd6e 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -1,8 +1,11 @@
 package agent
 
 import (
+	"fmt"
+	"math/rand"
 	"time"
 
+	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/agent/vision"
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 )
 
@@ -11,43 +14,128 @@ import (
 type HornetAgent struct {
 	Agent
 	birthDate time.Time
-	maxNectar int
-	job       int
+	seenElems []*vision.SeenElem
 }
 
 type HornetAgentJson struct {
-	ID string `json:"id"`
+	ID          string             `json:"id"`
+	Position    *envpkg.Position   `json:"position"`
+	Orientation envpkg.Orientation `json:"orientation"`
+	Objective   objective          `json:"objective"`
+	SeenElems   []*vision.SeenElem `json:"seenElems"`
 }
 
 func NewHornetAgent(id string, env *envpkg.Environment, syncChan chan bool, s int) *HornetAgent {
 	hAgent := &HornetAgent{}
 	hAgent.Agent = Agent{
-		iagt:     hAgent,
-		id:       envpkg.AgentID(id),
-		env:      env,
-		syncChan: syncChan,
-		speed:    s,
+		iagt:       hAgent,
+		id:         envpkg.AgentID(id),
+		env:        env,
+		syncChan:   syncChan,
+		speed:      s,
+		visionFunc: vision.HornetVision,
+		alive:      true,
+		objective:  objective{Position: nil, Type: None},
 	}
-
+	hAgent.birthDate = time.Now()
 	return hAgent
 }
 
-func (agt *HornetAgent) Start() {
-}
-
-func (agt *HornetAgent) Stop() {
-
-}
-
 func (agt *HornetAgent) Percept() {
+	if agt.pos == nil {
+		chance := rand.Intn(2)
+		if chance == 0 {
+			// either spawns at the top or the bottom or the left or the right
+			chance = rand.Intn(4)
+			x := 0
+			y := 0
+			switch chance {
+			case 0:
+				x = 0
+				y = rand.Intn(agt.env.GetMapDimension())
+				agt.orientation = envpkg.East
+			case 1:
+				x = agt.env.GetMapDimension() - 1
+				y = rand.Intn(agt.env.GetMapDimension())
+				agt.orientation = envpkg.West
+			case 2:
+				x = rand.Intn(agt.env.GetMapDimension())
+				y = 0
+				agt.orientation = envpkg.South
+			case 3:
+				x = rand.Intn(agt.env.GetMapDimension())
+				y = agt.env.GetMapDimension() - 1
+				agt.orientation = envpkg.North
+			}
+			agt.pos = envpkg.NewPosition(x, y, agt.env.GetMapDimension(), agt.env.GetMapDimension())
+			agt.env.AddAgent(agt)
+			fmt.Printf("[%s] Hornet spawned at %d %d\n", agt.id, x, y)
+			agt.objective.Type = None
+		}
+	}
+	if agt.pos != nil {
+		agt.seenElems = agt.see()
+	}
 }
 
+// The hornet agent always targets the closest bee
 func (agt *HornetAgent) Deliberate() {
+	distanceToTarget := float64(agt.env.GetMapDimension())
+	if agt.objective.Type == Bee {
+		if bee, ok := agt.objective.TargetedElem.(BeeAgent); ok {
+			distanceToTarget = bee.Position().DistanceFrom(agt.Position())
+		}
+	}
+	for _, seen := range agt.seenElems {
+		if seen.Elem != nil {
+			switch seen.Elem.(type) {
+			case *BeeAgent:
+				bee := seen.Elem.(*BeeAgent)
+				distance := bee.Position().DistanceFrom(agt.Position())
+				if distance < distanceToTarget {
+					agt.objective.Type = Bee
+					agt.objective.TargetedElem = bee
+					agt.objective.Position = bee.Position().Copy()
+					distanceToTarget = distance
+				}
+			case BeeAgent:
+				fmt.Println("=================================Hornet sees a bee wtf")
+			}
+		}
+	}
+	if agt.objective.Type == None {
+		agt.wander()
+	}
 }
 
 func (agt *HornetAgent) Act() {
+	if agt.objective.Type == None {
+		return
+	}
+	switch agt.objective.Type {
+	case Position:
+		agt.gotoNextStepTowards(agt.objective.Position.Copy())
+		if agt.Position().Equal(agt.objective.Position) {
+			agt.objective.Type = None
+		}
+	case Bee:
+		fmt.Println("=====================Hornet attacking bee !")
+		bee := agt.objective.TargetedElem.(*BeeAgent)
+		agt.gotoNextStepTowards(bee.Position().Copy())
+		if agt.Position().Near(bee.pos.Copy(), 1) {
+			bee.Kill()
+			agt.objective.Type = None
+			fmt.Println("=====================Hornet killed bee !!!")
+		}
+	}
 }
 
 func (agt *HornetAgent) ToJsonObj() interface{} {
-	return BeeAgentJson{ID: string(agt.id)}
+	return HornetAgentJson{
+		ID:          string(agt.id),
+		Position:    agt.pos,
+		Orientation: agt.orientation,
+		Objective:   agt.objective,
+		SeenElems:   agt.seenElems,
+	}
 }
diff --git a/backend/simulation/agent/vision/equilateralTriangleVision.go b/backend/simulation/agent/vision/equilateralTriangleVision.go
new file mode 100644
index 0000000..f24f61e
--- /dev/null
+++ b/backend/simulation/agent/vision/equilateralTriangleVision.go
@@ -0,0 +1,46 @@
+package vision
+
+import (
+	"math"
+	"sort"
+
+	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
+)
+
+// We decide that the returned list is sorted by proximity to the agent
+func EquilateralTriangleVision(agt envpkg.IAgent, env *envpkg.Environment, distance float64) []*SeenElem {
+	// Side of the triangle - formula for an equilateral triangle
+	sideSize := (2*(distance))/math.Sqrt(3) + 1
+	// Vision triangle coordinates
+	topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY := getTriangleCoordinates(*agt.Position(), distance, sideSize, agt.Orientation())
+	// Getting the bounding box of the triangle
+	minX := utils.Min(topCornerX, utils.Min(leftCornerX, rightCornerX))
+	maxX := utils.Max(topCornerX, utils.Max(leftCornerX, rightCornerX))
+	minY := utils.Min(topCornerY, utils.Min(leftCornerY, rightCornerY))
+	maxY := utils.Max(topCornerY, utils.Max(leftCornerY, rightCornerY))
+
+	// Contains all the elements seen by the agent
+	seenElems := make([]*SeenElem, 0)
+
+	addElemToList := func(x, y int) {
+		if env.IsValidPosition(x, y) {
+			if pointIsInTriangle(float64(x), float64(y), topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY) {
+				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, env.GetAt(x, y)))
+			}
+		}
+	}
+
+	for x := utils.Round(minX) + 1; x <= utils.Round(maxX)+1; x++ {
+		for y := utils.Round(minY); y <= utils.Round(maxY)+1; y++ {
+			addElemToList(x, y)
+		}
+	}
+
+	// Sorting the list by proximity to the agent
+	sort.Slice(seenElems, func(i, j int) bool {
+		return agt.Position().DistanceFrom(seenElems[i].Pos) < agt.Position().DistanceFrom(seenElems[j].Pos)
+	})
+
+	return seenElems
+}
diff --git a/backend/simulation/agent/vision/explorerBeeVision.go b/backend/simulation/agent/vision/explorerBeeVision.go
index d9e381a..27b91a4 100644
--- a/backend/simulation/agent/vision/explorerBeeVision.go
+++ b/backend/simulation/agent/vision/explorerBeeVision.go
@@ -1,9 +1,6 @@
 package vision
 
 import (
-	"math"
-	"sort"
-
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
 )
@@ -11,39 +8,6 @@ import (
 // For now we decide that the vision of a bee is an equilateral triangle
 // We decide that the returned list is sorted by proximity to the agent
 func ExplorerBeeVision(agt envpkg.IAgent, env *envpkg.Environment) []*SeenElem {
-	// Height of the triangle
 	distance := utils.GetBeeAgentVisionRange()
-	// Side of the triangle - formula for an equilateral triangle
-	sideSize := (2*(distance))/math.Sqrt(3) + 1
-	// Vision triangle coordinates
-	topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY := getTriangleCoordinates(*agt.Position(), distance, sideSize, agt.Orientation())
-	// Getting the bounding box of the triangle
-	minX := utils.Min(topCornerX, utils.Min(leftCornerX, rightCornerX))
-	maxX := utils.Max(topCornerX, utils.Max(leftCornerX, rightCornerX))
-	minY := utils.Min(topCornerY, utils.Min(leftCornerY, rightCornerY))
-	maxY := utils.Max(topCornerY, utils.Max(leftCornerY, rightCornerY))
-
-	// Contains all the elements seen by the agent
-	seenElems := make([]*SeenElem, 0)
-
-	addElemToList := func(x, y int) {
-		if env.IsValidPosition(x, y) {
-			if pointIsInTriangle(float64(x), float64(y), topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY) {
-				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, env.GetAt(x, y)))
-			}
-		}
-	}
-
-	for x := utils.Round(minX) + 1; x <= utils.Round(maxX)+1; x++ {
-		for y := utils.Round(minY); y <= utils.Round(maxY)+1; y++ {
-			addElemToList(x, y)
-		}
-	}
-
-	// Sorting the list by proximity to the agent
-	sort.Slice(seenElems, func(i, j int) bool {
-		return agt.Position().DistanceFrom(seenElems[i].Pos) < agt.Position().DistanceFrom(seenElems[j].Pos)
-	})
-
-	return seenElems
+	return EquilateralTriangleVision(agt, env, distance)
 }
diff --git a/backend/simulation/agent/vision/hornetVision.go b/backend/simulation/agent/vision/hornetVision.go
new file mode 100644
index 0000000..865dc62
--- /dev/null
+++ b/backend/simulation/agent/vision/hornetVision.go
@@ -0,0 +1,13 @@
+package vision
+
+import (
+	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
+)
+
+// For now we decide that the vision of a hornet is an equilateral triangle
+// We decide that the returned list is sorted by proximity to the agent
+func HornetVision(agt envpkg.IAgent, env *envpkg.Environment) []*SeenElem {
+	distance := utils.GetHornetAgentVisionRange()
+	return EquilateralTriangleVision(agt, env, distance)
+}
diff --git a/backend/simulation/environment/environment.go b/backend/simulation/environment/environment.go
index 0dbe031..6830ee8 100644
--- a/backend/simulation/environment/environment.go
+++ b/backend/simulation/environment/environment.go
@@ -51,7 +51,7 @@ func (env *Environment) IsValidPosition(x int, y int) bool {
 
 func (env *Environment) AddAgent(agt IAgent) bool {
 	pos := agt.Position()
-	if env.GetAt(pos.X, pos.Y) == nil {
+	if pos != nil && env.GetAt(pos.X, pos.Y) == nil {
 		//return false
 		env.grid[pos.X][pos.Y] = agt
 	}
@@ -59,6 +59,16 @@ func (env *Environment) AddAgent(agt IAgent) bool {
 	return true
 }
 
+func (env *Environment) RemoveAgent(agt IAgent) {
+	for i, a := range env.agts {
+		if a.ID() == agt.ID() {
+			env.agts = append(env.agts[:i], env.agts[i+1:]...)
+			env.grid[a.Position().X][a.Position().Y] = nil
+			break
+		}
+	}
+}
+
 func (env *Environment) AddObject(obj IObject) bool {
 	pos := obj.Position()
 	if env.GetAt(pos.X, pos.Y) != nil {
@@ -139,7 +149,10 @@ func (env *Environment) PathFinding(start *Position, end *Position, numberMoves
 	}
 
 	path := []*Position{}
-	currentNode := openList[len(openList)-1]
+	var currentNode *node = nil
+	if len(openList) > 0 {
+		currentNode = openList[len(openList)-1]
+	}
 	for currentNode != nil {
 		path = append([]*Position{currentNode.position.Copy()}, path...)
 		currentNode = currentNode.parent
diff --git a/backend/simulation/environment/position.go b/backend/simulation/environment/position.go
index 7fdd438..983bc6e 100644
--- a/backend/simulation/environment/position.go
+++ b/backend/simulation/environment/position.go
@@ -1,6 +1,7 @@
 package environment
 
 import (
+	"fmt"
 	"math"
 
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
@@ -19,7 +20,7 @@ func NewPosition(x int, y int, maxX int, maxY int) *Position {
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) move(grid [][]interface{}, newX int, newY int) bool {
+func (p *Position) move(grid [][]interface{}, newX int, newY int, elem interface{}) bool {
 	// Out of bounds
 	if newX < 0 || newY < 0 || newX >= p.maxX || newY >= p.maxY {
 		return false
@@ -30,7 +31,12 @@ func (p *Position) move(grid [][]interface{}, newX int, newY int) bool {
 	}
 	// Not a simulation
 	if grid != nil {
-		grid[p.X][p.Y], grid[newX][newY] = nil, grid[p.X][p.Y]
+		if grid[p.X][p.Y] == elem {
+			fmt.Println("Grid is not nil : ", grid[p.X][p.Y])
+			grid[p.X][p.Y], grid[newX][newY] = nil, grid[p.X][p.Y]
+		} else {
+			grid[newX][newY] = elem
+		}
 	}
 	p.X = newX
 	p.Y = newY
@@ -38,43 +44,43 @@ func (p *Position) move(grid [][]interface{}, newX int, newY int) bool {
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoNorth(grid [][]interface{}) bool {
-	return p.move(grid, p.X, p.Y-1)
+func (p *Position) GoNorth(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X, p.Y-1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoSouth(grid [][]interface{}) bool {
-	return p.move(grid, p.X, p.Y+1)
+func (p *Position) GoSouth(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X, p.Y+1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoWest(grid [][]interface{}) bool {
-	return p.move(grid, p.X-1, p.Y)
+func (p *Position) GoWest(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X-1, p.Y, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoEast(grid [][]interface{}) bool {
-	return p.move(grid, p.X+1, p.Y)
+func (p *Position) GoEast(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X+1, p.Y, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoNorthEast(grid [][]interface{}) bool {
-	return p.move(grid, p.X+1, p.Y-1)
+func (p *Position) GoNorthEast(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X+1, p.Y-1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoNorthWest(grid [][]interface{}) bool {
-	return p.move(grid, p.X-1, p.Y-1)
+func (p *Position) GoNorthWest(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X-1, p.Y-1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoSouthEast(grid [][]interface{}) bool {
-	return p.move(grid, p.X+1, p.Y+1)
+func (p *Position) GoSouthEast(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X+1, p.Y+1, elem)
 }
 
 // Movement can be simulated with by passing a nil grid
-func (p *Position) GoSouthWest(grid [][]interface{}) bool {
-	return p.move(grid, p.X-1, p.Y+1)
+func (p *Position) GoSouthWest(grid [][]interface{}, elem interface{}) bool {
+	return p.move(grid, p.X-1, p.Y+1, elem)
 }
 
 func (p Position) DistanceFrom(p2 *Position) float64 {
@@ -86,9 +92,9 @@ func (p Position) Near(p2 *Position, distance int) bool {
 }
 
 // Returns the mirrored position of the point according to this point
-func (p Position) GetSymmetricOfPoint(origin Position) *Position {
-	symX := 2*origin.X - p.X
-	symY := 2*origin.Y - p.Y
+func (p Position) GetSymmetricOfPoint(p2 Position) *Position {
+	symX := 2*p.X - p2.X
+	symY := 2*p.Y - p2.Y
 	return &Position{X: symX, Y: symY, maxX: p.maxX, maxY: p.maxY}
 }
 
diff --git a/backend/simulation/simulation.go b/backend/simulation/simulation.go
index 5e9af8f..64fe28e 100644
--- a/backend/simulation/simulation.go
+++ b/backend/simulation/simulation.go
@@ -30,7 +30,7 @@ type SimulationJson struct {
 	Environment interface{}   `json:"environment"`
 }
 
-func NewSimulation(nbees int, nflowers int, maWs *websocket.Conn) *Simulation {
+func NewSimulation(nbees int, nflowers int, nhornets int, maWs *websocket.Conn) *Simulation {
 	simu := &Simulation{}
 	simu.ws = maWs
 	env := envpkg.NewEnvironment([]envpkg.IAgent{}, []envpkg.IObject{})
@@ -116,6 +116,17 @@ func NewSimulation(nbees int, nflowers int, maWs *websocket.Conn) *Simulation {
 		simu.env.AddAgent(agt)
 	}
 
+	for i := 0; i < nhornets; i++ {
+		// Creating a hornet
+		id := fmt.Sprintf("Hornet #%d", i)
+		syncChan := make(chan bool)
+		agt := agt.NewHornetAgent(id, simu.env, syncChan, rand.Intn(2)+1)
+		// ajout de l'agent à la simulation
+		simu.agts = append(simu.agts, agt)
+		// ajout de l'agent à l'environnement
+		//simu.env.AddAgent(agt) // Pas encore car pas spawn
+	}
+
 	simu.sendState()
 	return simu
 }
@@ -147,11 +158,14 @@ func (simu *Simulation) Run(maWs *websocket.Conn) {
 
 	// Boucle de simulation
 	for simu.IsRunning() {
-		for _, agt := range simu.agts {
+		for i, agt := range simu.agts {
 			c := agt.GetSyncChan()
 			simu.env.Lock()
 			c <- true
-			<-c
+			// If dead
+			if !<-c {
+				simu.agts = append(simu.agts[:i], simu.agts[i+1:]...)
+			}
 			simu.env.Unlock()
 			time.Sleep(time.Second / 100) // 100 Tour / Sec
 		}
@@ -196,7 +210,7 @@ func (simu *Simulation) print() {
 	startTime := time.Now()
 	for simu.IsRunning() {
 		fmt.Printf("\rRunning simulation for %vms...  - ", time.Since(startTime).Milliseconds())
-		time.Sleep(time.Second / 60) // 60 fps
+		time.Sleep(time.Second / 30) // 60 fps
 	}
 }
 
@@ -212,5 +226,8 @@ func (simu *Simulation) ToJsonObj() SimulationJson {
 		objects = append(objects, obj.ToJsonObj())
 	}
 
+	simu.env.Lock()
+	defer simu.env.Unlock()
+
 	return SimulationJson{Agents: agents, Objects: objects, Environment: simu.env.ToJsonObj()}
 }
diff --git a/backend/utils/configreader.go b/backend/utils/configreader.go
index a93beab..239dba5 100644
--- a/backend/utils/configreader.go
+++ b/backend/utils/configreader.go
@@ -57,6 +57,10 @@ func GetNumberBees() int {
 	return getIntAttributeFromConfigFile("NumberBees")
 }
 
+func GetNumberHornets() int {
+	return getIntAttributeFromConfigFile("NumberHornets")
+}
+
 func GetMaxNectar() int {
 	return getIntAttributeFromConfigFile("MaxNectar")
 }
@@ -81,6 +85,10 @@ func GetBeeAgentVisionRange() float64 {
 	return getFloat64AttributeFromConfigFile("BeeAgentVisionRange")
 }
 
+func GetHornetAgentVisionRange() float64 {
+	return getFloat64AttributeFromConfigFile("HornetAgentVisionRange")
+}
+
 func GetExName() string {
 	return getStringAttributeFromConfigFile("ExName")
 }
diff --git a/config.yaml b/config.yaml
index f1d295f..f6a07c5 100644
--- a/config.yaml
+++ b/config.yaml
@@ -6,6 +6,10 @@ NumberBees: 20
 BeeAgentVisionRange: 4.0
 MaxNectar: 70 # in mg -> https://wildflowermeadows.com/2024/01/how-far-do-honeybees-fly/
 
+# Hornets
+NumberHornets: 2
+HornetAgentVisionRange: 4.0
+
 # Flowers
-NumberFlowers: 10
+NumberFlowers: 20
 NumberFlowerPatches: 3
diff --git a/frontend-ws-test/index.html b/frontend-ws-test/index.html
index cfec1ae..9909fc2 100644
--- a/frontend-ws-test/index.html
+++ b/frontend-ws-test/index.html
@@ -118,6 +118,9 @@
 		}
 
         function drawAgent(ctx, agent, cellSize) {
+            if (!agent.position) {
+                return;
+            }
             text = agent.id.replace('ExAgent ', 'ExA');
             let agtX = agent.position.x * cellSize;
             let agtY = agent.position.y * cellSize;
@@ -129,9 +132,20 @@
                 ctx.fillStyle = 'yellow';
             }
             // drawing yellow square
+            orientationColor = 'red';
+            if (agent.id.includes('Bee')) {
+                ctx.fillStyle = 'yellow';
+                text = agent.id.replace('Bee ', 'B');
+                orientationColor = 'red';
+            }
+            if (agent.id.includes('Hornet')) {
+                ctx.fillStyle = 'red';
+                text = agent.id.replace('Hornet ', 'Ho');
+                orientationColor = 'white';
+            }
             ctx.fillRect(agtX, agtY, cellSize, cellSize);
             // drawing movement
-            ctx.strokeStyle = 'red';
+            ctx.strokeStyle = orientationColor;
             if (agent.orientation === "N") {
                 canvas_arrow(ctx, agtX + cellSize / 2, agtY + cellSize, agtX + cellSize / 2, agtY);
             } else if (agent.orientation === "E") {
@@ -158,13 +172,16 @@
 
         function drawObject(ctx, object, cellSize) {
             let text = object.id;
+            let textColor = 'black';
             if (object.id.includes('Flower')) {
-                ctx.fillStyle = 'red';
+                ctx.fillStyle = 'blue';
+                textColor = 'white';
                 text = object.id.replace('Flower ', 'F');
             } 
             else if (object.id.includes('Hive')) {
                 ctx.fillStyle = 'orange';
                 text = object.id.replace('Hive ', 'H');
+                textColor = 'black';
             }
             else {
                 ctx.fillStyle = 'green';
@@ -173,7 +190,7 @@
             objY = object.position.y * cellSize;
             ctx.fillRect(objX, objY, cellSize, cellSize);
             font = findRightFont(ctx, 10, text, cellSize, cellSize);
-            ctx.fillStyle = 'black';
+            ctx.fillStyle = textColor;
             ctx.font = font + 'px Arial';
             ctx.fillText(text, objX, objY + cellSize / 2);
         }
@@ -188,6 +205,8 @@
             }
         
             const data = JSON.parse(event.data);
+            console.log(data.agents);
+            console.log(data.environment);
 
             const table = document.createElement('table');
             const headerRow = document.createElement('tr');
-- 
GitLab


From 21deb252a22d2d000ce88595f4c221caf54e0cc4 Mon Sep 17 00:00:00 2001
From: StutenEXE <alexandre.bidaux78@gmail.com>
Date: Tue, 31 Dec 2024 11:24:11 +0100
Subject: [PATCH 3/7] Basic Hornet behavior and added function to agent
 interface

---
 backend/simulation/agent/agent.go             | 33 ++++++++++++++---
 backend/simulation/agent/beeAgent.go          | 25 +++++++++----
 backend/simulation/agent/exagent.go           |  4 +++
 backend/simulation/agent/hornetAgent.go       | 36 ++++++++++---------
 .../agent/vision/equilateralTriangleVision.go |  5 ++-
 backend/simulation/environment/environment.go | 24 ++++++++++---
 backend/simulation/environment/iagent.go      |  9 +++++
 backend/simulation/environment/position.go    |  2 --
 backend/simulation/simulation.go              |  6 ++--
 config.yaml                                   |  8 ++---
 10 files changed, 111 insertions(+), 41 deletions(-)

diff --git a/backend/simulation/agent/agent.go b/backend/simulation/agent/agent.go
index 0d864ac..9feb8e3 100644
--- a/backend/simulation/agent/agent.go
+++ b/backend/simulation/agent/agent.go
@@ -51,13 +51,16 @@ func (agt *Agent) Start() {
 		for {
 			run := <-agt.syncChan
 			if !run || !agt.alive {
-				agt.syncChan <- !run || !agt.alive
+				agt.syncChan <- false
 				break
 			}
+			fmt.Printf("[%s] === Percieving === [%s]\n", agt.ID(), agt.ID())
 			agt.iagt.Percept()
+			fmt.Printf("[%s] === Deliberating === [%s]\n", agt.ID(), agt.ID())
 			agt.iagt.Deliberate()
+			fmt.Printf("[%s] === Acting === [%s]\n", agt.ID(), agt.ID())
 			agt.iagt.Act()
-			agt.syncChan <- run
+			agt.syncChan <- agt.alive
 		}
 		fmt.Printf("[%s] Stopping Agent\n", agt.ID())
 	}()
@@ -71,6 +74,18 @@ func (agt Agent) GetSyncChan() chan bool {
 	return agt.syncChan
 }
 
+func (agt *Agent) Percept() {
+	agt.iagt.Percept()
+}
+
+func (agt *Agent) Deliberate() {
+	agt.iagt.Deliberate()
+}
+
+func (agt *Agent) Act() {
+	agt.iagt.Act()
+}
+
 func (agt Agent) Position() *envpkg.Position {
 	if agt.pos == nil {
 		return nil
@@ -345,7 +360,7 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 		}
 	}
 	// Find the closest available position in surroundings
-	closestPosition := newObjective.Copy()
+	closestPosition := agt.pos.Copy()
 	minDistance := agt.pos.DistanceFrom(newObjective)
 	for _, pos := range surroundings {
 		distance := pos.DistanceFrom(newObjective)
@@ -362,7 +377,17 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 	return newObjective
 }
 
-func (agt *Agent) die() {
+func (agt *Agent) Kill() {
+	agt.env.RemoveAgent(agt)
 	agt.alive = false
 	agt.pos = nil
+
+}
+
+func (agt Agent) Type() envpkg.AgentType {
+	return agt.iagt.Type()
+}
+
+func (agt *Agent) ToJsonObj() interface{} {
+	return agt.iagt.ToJsonObj()
 }
diff --git a/backend/simulation/agent/beeAgent.go b/backend/simulation/agent/beeAgent.go
index 06ac55a..1154495 100644
--- a/backend/simulation/agent/beeAgent.go
+++ b/backend/simulation/agent/beeAgent.go
@@ -2,6 +2,7 @@ package agent
 
 import (
 	"fmt"
+	"reflect"
 	"time"
 
 	"math/rand"
@@ -87,11 +88,6 @@ func (agt *BeeAgent) Act() {
 	}
 }
 
-func (agt *BeeAgent) Kill() {
-	agt.env.RemoveAgent(agt)
-	agt.die()
-}
-
 func (agt *BeeAgent) hasFlowerObjective() bool {
 	return agt.objective.Type == Flower
 }
@@ -122,6 +118,7 @@ func (agt *BeeAgent) foragerDeliberation() {
 		if seen.Elem != nil {
 			switch elem := (seen.Elem).(type) {
 			case *HornetAgent:
+				fmt.Printf("[%s] *****************Close to hornet %v\n", agt.id, reflect.TypeOf(seen.Elem))
 				closestHornet = elem
 			case *obj.Flower:
 				if !hasAlreadySeenCloserFlower {
@@ -135,8 +132,8 @@ func (agt *BeeAgent) foragerDeliberation() {
 					agt.objective.Type = Flower
 					hasAlreadySeenCloserFlower = true
 				}
-			default:
-				fmt.Printf("[%s] Unknown element seen : %v\n", agt.id, elem)
+				//default:
+				//fmt.Printf("[%s] Unknown element seen : %v\n", agt.id, elem)
 			}
 		}
 		if closestHornet != nil {
@@ -148,6 +145,16 @@ func (agt *BeeAgent) foragerDeliberation() {
 		fmt.Printf("[%s] Hornet seen, fleeing in opposite direction\n", agt.id)
 		agt.objective.Position = agt.pos.GetSymmetricOfPoint(*closestHornet.pos.Copy())
 		agt.objective.Type = Position
+		if agt.objective.Position.X < 0 {
+			agt.objective.Position.X = 0
+		} else if agt.objective.Position.X >= agt.env.GetMapDimension() {
+			agt.objective.Position.X = agt.env.GetMapDimension() - 1
+		}
+		if agt.objective.Position.Y < 0 {
+			agt.objective.Position.Y = 0
+		} else if agt.objective.Position.Y >= agt.env.GetMapDimension() {
+			agt.objective.Position.Y = agt.env.GetMapDimension() - 1
+		}
 	}
 	// If has no objective, wander
 	if agt.objective.Type == None {
@@ -217,6 +224,10 @@ func (agt *BeeAgent) workerAction() {
 	}
 }
 
+func (agt BeeAgent) Type() envpkg.AgentType {
+	return envpkg.Bee
+}
+
 func (agt *BeeAgent) ToJsonObj() interface{} {
 	return BeeAgentJson{ID: string(agt.id),
 		Position:    agt.pos,
diff --git a/backend/simulation/agent/exagent.go b/backend/simulation/agent/exagent.go
index b7d04bd..7c896da 100644
--- a/backend/simulation/agent/exagent.go
+++ b/backend/simulation/agent/exagent.go
@@ -107,6 +107,10 @@ func (agt *ExAgent) Act() {
 	agt.orientation = agt.movement
 }
 
+func (agt *ExAgent) Type() envpkg.AgentType {
+	return envpkg.ExAgent
+}
+
 func (agt ExAgent) ToJsonObj() interface{} {
 	return ExAgentJson{ID: string(agt.id),
 		Value:       agt.value,
diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index a51dd6e..8bc574f 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -82,24 +82,24 @@ func (agt *HornetAgent) Percept() {
 func (agt *HornetAgent) Deliberate() {
 	distanceToTarget := float64(agt.env.GetMapDimension())
 	if agt.objective.Type == Bee {
-		if bee, ok := agt.objective.TargetedElem.(BeeAgent); ok {
+		if bee, ok := agt.objective.TargetedElem.(*BeeAgent); ok {
 			distanceToTarget = bee.Position().DistanceFrom(agt.Position())
 		}
 	}
 	for _, seen := range agt.seenElems {
-		if seen.Elem != nil {
-			switch seen.Elem.(type) {
-			case *BeeAgent:
-				bee := seen.Elem.(*BeeAgent)
-				distance := bee.Position().DistanceFrom(agt.Position())
-				if distance < distanceToTarget {
-					agt.objective.Type = Bee
-					agt.objective.TargetedElem = bee
-					agt.objective.Position = bee.Position().Copy()
-					distanceToTarget = distance
+		if seen.Elem != nil && seen.Elem != agt {
+			switch elem := seen.Elem.(type) {
+			case *Agent:
+				if elem.Type() == envpkg.Bee {
+					fmt.Printf("[%s] Found a close bee (%s) \n", agt.id, elem.ID())
+					distance := elem.Position().DistanceFrom(agt.Position())
+					if distance < distanceToTarget {
+						agt.objective.Type = Bee
+						agt.objective.TargetedElem = elem
+						agt.objective.Position = elem.Position().Copy()
+						distanceToTarget = distance
+					}
 				}
-			case BeeAgent:
-				fmt.Println("=================================Hornet sees a bee wtf")
 			}
 		}
 	}
@@ -119,17 +119,21 @@ func (agt *HornetAgent) Act() {
 			agt.objective.Type = None
 		}
 	case Bee:
-		fmt.Println("=====================Hornet attacking bee !")
-		bee := agt.objective.TargetedElem.(*BeeAgent)
+		bee := agt.objective.TargetedElem.(*Agent)
+		fmt.Printf("[%s] Hornet attacking %s !\n", agt.id, bee.ID())
 		agt.gotoNextStepTowards(bee.Position().Copy())
 		if agt.Position().Near(bee.pos.Copy(), 1) {
 			bee.Kill()
 			agt.objective.Type = None
-			fmt.Println("=====================Hornet killed bee !!!")
+			fmt.Printf("[%s] Hornet killed %s !!!\n", agt.id, bee.ID())
 		}
 	}
 }
 
+func (agt HornetAgent) Type() envpkg.AgentType {
+	return envpkg.Hornet
+}
+
 func (agt *HornetAgent) ToJsonObj() interface{} {
 	return HornetAgentJson{
 		ID:          string(agt.id),
diff --git a/backend/simulation/agent/vision/equilateralTriangleVision.go b/backend/simulation/agent/vision/equilateralTriangleVision.go
index f24f61e..2f500df 100644
--- a/backend/simulation/agent/vision/equilateralTriangleVision.go
+++ b/backend/simulation/agent/vision/equilateralTriangleVision.go
@@ -5,6 +5,7 @@ import (
 	"sort"
 
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
 )
 
@@ -26,7 +27,9 @@ func EquilateralTriangleVision(agt envpkg.IAgent, env *envpkg.Environment, dista
 	addElemToList := func(x, y int) {
 		if env.IsValidPosition(x, y) {
 			if pointIsInTriangle(float64(x), float64(y), topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY) {
-				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, env.GetAt(x, y)))
+				elem := env.GetAt(x, y)
+				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, elem))
+				// fmt.Printf("Agent %s sees element %v at position (%d, %d) (%s)\n", agt.ID(), elem, x, y, reflect.TypeOf(elem))
 			}
 		}
 	}
diff --git a/backend/simulation/environment/environment.go b/backend/simulation/environment/environment.go
index 6830ee8..207609a 100644
--- a/backend/simulation/environment/environment.go
+++ b/backend/simulation/environment/environment.go
@@ -34,7 +34,18 @@ func (env *Environment) GetAt(x int, y int) interface{} {
 	if x < 0 || y < 0 || x >= mapDimension || y >= mapDimension {
 		return nil
 	}
-	return env.grid[x][y]
+	elem := env.grid[x][y]
+	if elem == nil {
+		return nil
+	}
+	switch obj := elem.(type) {
+	case IAgent:
+		return obj
+	case IObject:
+		return obj
+	default:
+		return obj
+	}
 }
 
 func (env *Environment) GetMap() [][]interface{} {
@@ -49,13 +60,14 @@ func (env *Environment) IsValidPosition(x int, y int) bool {
 	return x >= 0 && y >= 0 && x < mapDimension && y < mapDimension
 }
 
+// Returns true is added on grid, false if not
 func (env *Environment) AddAgent(agt IAgent) bool {
 	pos := agt.Position()
+	env.agts = append(env.agts, agt)
 	if pos != nil && env.GetAt(pos.X, pos.Y) == nil {
-		//return false
 		env.grid[pos.X][pos.Y] = agt
+		return false
 	}
-	env.agts = append(env.agts, agt)
 	return true
 }
 
@@ -107,9 +119,10 @@ func (env *Environment) PathFinding(start *Position, end *Position, numberMoves
 	startNode := &node{position: start.Copy(), cost: 0, heuristic: start.ManhattanDistance(end)}
 	openList = append(openList, startNode)
 
-	// We allow pathfinding to last 50 iterations
+	// We allow pathfinding to last 3 times the number of moves
 	cpt := 0
-	for len(openList) > 0 && cpt < numberMoves*2 {
+	for len(openList) > 0 && cpt < numberMoves*3 {
+		//fmt.Printf("%d ", cpt)
 		currentNode := openList[0]
 		currentIndex := 0
 		for index, node := range openList {
@@ -147,6 +160,7 @@ func (env *Environment) PathFinding(start *Position, end *Position, numberMoves
 		}
 		cpt++
 	}
+	//fmt.Println("")
 
 	path := []*Position{}
 	var currentNode *node = nil
diff --git a/backend/simulation/environment/iagent.go b/backend/simulation/environment/iagent.go
index 432a60e..8c41bf4 100644
--- a/backend/simulation/environment/iagent.go
+++ b/backend/simulation/environment/iagent.go
@@ -15,6 +15,14 @@ const (
 	SouthWest Orientation = "SW"
 )
 
+type AgentType string
+
+const (
+	ExAgent AgentType = "ExAgent"
+	Bee     AgentType = "Bee"
+	Hornet  AgentType = "Hornet"
+)
+
 // IAgent is an interface representing the agent's actions
 // limits
 type IAgent interface {
@@ -27,4 +35,5 @@ type IAgent interface {
 	Percept()
 	Deliberate()
 	Act()
+	Type() AgentType
 }
diff --git a/backend/simulation/environment/position.go b/backend/simulation/environment/position.go
index 983bc6e..ed4dde2 100644
--- a/backend/simulation/environment/position.go
+++ b/backend/simulation/environment/position.go
@@ -1,7 +1,6 @@
 package environment
 
 import (
-	"fmt"
 	"math"
 
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
@@ -32,7 +31,6 @@ func (p *Position) move(grid [][]interface{}, newX int, newY int, elem interface
 	// Not a simulation
 	if grid != nil {
 		if grid[p.X][p.Y] == elem {
-			fmt.Println("Grid is not nil : ", grid[p.X][p.Y])
 			grid[p.X][p.Y], grid[newX][newY] = nil, grid[p.X][p.Y]
 		} else {
 			grid[newX][newY] = elem
diff --git a/backend/simulation/simulation.go b/backend/simulation/simulation.go
index 64fe28e..ceca97c 100644
--- a/backend/simulation/simulation.go
+++ b/backend/simulation/simulation.go
@@ -162,8 +162,10 @@ func (simu *Simulation) Run(maWs *websocket.Conn) {
 			c := agt.GetSyncChan()
 			simu.env.Lock()
 			c <- true
-			// If dead
-			if !<-c {
+			isAlive := <-c
+			// If dead, remove agent from simulation
+			if !isAlive {
+				fmt.Printf("{{SIMULATION}} - [%s] is dead\n", agt.ID())
 				simu.agts = append(simu.agts[:i], simu.agts[i+1:]...)
 			}
 			simu.env.Unlock()
diff --git a/config.yaml b/config.yaml
index f6a07c5..4fa1ffe 100644
--- a/config.yaml
+++ b/config.yaml
@@ -1,15 +1,15 @@
 # Environment 
-MapDimension: 40
+MapDimension: 25
 
 # Bees
-NumberBees: 20
+NumberBees: 10
 BeeAgentVisionRange: 4.0
 MaxNectar: 70 # in mg -> https://wildflowermeadows.com/2024/01/how-far-do-honeybees-fly/
 
 # Hornets
-NumberHornets: 2
+NumberHornets: 1
 HornetAgentVisionRange: 4.0
 
 # Flowers
-NumberFlowers: 20
+NumberFlowers: 15
 NumberFlowerPatches: 3
-- 
GitLab


From 33aa4a0f44771673f92e0d56e5e98f320a5ebb76 Mon Sep 17 00:00:00 2001
From: StutenEXE <alexandre.bidaux78@gmail.com>
Date: Tue, 31 Dec 2024 11:24:11 +0100
Subject: [PATCH 4/7] Basic Hornet behavior and added function to agent
 interface

---
 backend/simulation/agent/agent.go             | 33 ++++++++++++++---
 backend/simulation/agent/beeAgent.go          | 25 +++++++++----
 backend/simulation/agent/exagent.go           |  4 +++
 backend/simulation/agent/hornetAgent.go       | 36 ++++++++++---------
 .../agent/vision/equilateralTriangleVision.go |  5 ++-
 backend/simulation/agent/vision/visionFunc.go |  2 +-
 backend/simulation/environment/environment.go | 24 ++++++++++---
 backend/simulation/environment/iagent.go      |  9 +++++
 backend/simulation/environment/position.go    |  2 --
 backend/simulation/simulation.go              |  6 ++--
 config.yaml                                   |  8 ++---
 11 files changed, 112 insertions(+), 42 deletions(-)

diff --git a/backend/simulation/agent/agent.go b/backend/simulation/agent/agent.go
index 0d864ac..9feb8e3 100644
--- a/backend/simulation/agent/agent.go
+++ b/backend/simulation/agent/agent.go
@@ -51,13 +51,16 @@ func (agt *Agent) Start() {
 		for {
 			run := <-agt.syncChan
 			if !run || !agt.alive {
-				agt.syncChan <- !run || !agt.alive
+				agt.syncChan <- false
 				break
 			}
+			fmt.Printf("[%s] === Percieving === [%s]\n", agt.ID(), agt.ID())
 			agt.iagt.Percept()
+			fmt.Printf("[%s] === Deliberating === [%s]\n", agt.ID(), agt.ID())
 			agt.iagt.Deliberate()
+			fmt.Printf("[%s] === Acting === [%s]\n", agt.ID(), agt.ID())
 			agt.iagt.Act()
-			agt.syncChan <- run
+			agt.syncChan <- agt.alive
 		}
 		fmt.Printf("[%s] Stopping Agent\n", agt.ID())
 	}()
@@ -71,6 +74,18 @@ func (agt Agent) GetSyncChan() chan bool {
 	return agt.syncChan
 }
 
+func (agt *Agent) Percept() {
+	agt.iagt.Percept()
+}
+
+func (agt *Agent) Deliberate() {
+	agt.iagt.Deliberate()
+}
+
+func (agt *Agent) Act() {
+	agt.iagt.Act()
+}
+
 func (agt Agent) Position() *envpkg.Position {
 	if agt.pos == nil {
 		return nil
@@ -345,7 +360,7 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 		}
 	}
 	// Find the closest available position in surroundings
-	closestPosition := newObjective.Copy()
+	closestPosition := agt.pos.Copy()
 	minDistance := agt.pos.DistanceFrom(newObjective)
 	for _, pos := range surroundings {
 		distance := pos.DistanceFrom(newObjective)
@@ -362,7 +377,17 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 	return newObjective
 }
 
-func (agt *Agent) die() {
+func (agt *Agent) Kill() {
+	agt.env.RemoveAgent(agt)
 	agt.alive = false
 	agt.pos = nil
+
+}
+
+func (agt Agent) Type() envpkg.AgentType {
+	return agt.iagt.Type()
+}
+
+func (agt *Agent) ToJsonObj() interface{} {
+	return agt.iagt.ToJsonObj()
 }
diff --git a/backend/simulation/agent/beeAgent.go b/backend/simulation/agent/beeAgent.go
index 06ac55a..1154495 100644
--- a/backend/simulation/agent/beeAgent.go
+++ b/backend/simulation/agent/beeAgent.go
@@ -2,6 +2,7 @@ package agent
 
 import (
 	"fmt"
+	"reflect"
 	"time"
 
 	"math/rand"
@@ -87,11 +88,6 @@ func (agt *BeeAgent) Act() {
 	}
 }
 
-func (agt *BeeAgent) Kill() {
-	agt.env.RemoveAgent(agt)
-	agt.die()
-}
-
 func (agt *BeeAgent) hasFlowerObjective() bool {
 	return agt.objective.Type == Flower
 }
@@ -122,6 +118,7 @@ func (agt *BeeAgent) foragerDeliberation() {
 		if seen.Elem != nil {
 			switch elem := (seen.Elem).(type) {
 			case *HornetAgent:
+				fmt.Printf("[%s] *****************Close to hornet %v\n", agt.id, reflect.TypeOf(seen.Elem))
 				closestHornet = elem
 			case *obj.Flower:
 				if !hasAlreadySeenCloserFlower {
@@ -135,8 +132,8 @@ func (agt *BeeAgent) foragerDeliberation() {
 					agt.objective.Type = Flower
 					hasAlreadySeenCloserFlower = true
 				}
-			default:
-				fmt.Printf("[%s] Unknown element seen : %v\n", agt.id, elem)
+				//default:
+				//fmt.Printf("[%s] Unknown element seen : %v\n", agt.id, elem)
 			}
 		}
 		if closestHornet != nil {
@@ -148,6 +145,16 @@ func (agt *BeeAgent) foragerDeliberation() {
 		fmt.Printf("[%s] Hornet seen, fleeing in opposite direction\n", agt.id)
 		agt.objective.Position = agt.pos.GetSymmetricOfPoint(*closestHornet.pos.Copy())
 		agt.objective.Type = Position
+		if agt.objective.Position.X < 0 {
+			agt.objective.Position.X = 0
+		} else if agt.objective.Position.X >= agt.env.GetMapDimension() {
+			agt.objective.Position.X = agt.env.GetMapDimension() - 1
+		}
+		if agt.objective.Position.Y < 0 {
+			agt.objective.Position.Y = 0
+		} else if agt.objective.Position.Y >= agt.env.GetMapDimension() {
+			agt.objective.Position.Y = agt.env.GetMapDimension() - 1
+		}
 	}
 	// If has no objective, wander
 	if agt.objective.Type == None {
@@ -217,6 +224,10 @@ func (agt *BeeAgent) workerAction() {
 	}
 }
 
+func (agt BeeAgent) Type() envpkg.AgentType {
+	return envpkg.Bee
+}
+
 func (agt *BeeAgent) ToJsonObj() interface{} {
 	return BeeAgentJson{ID: string(agt.id),
 		Position:    agt.pos,
diff --git a/backend/simulation/agent/exagent.go b/backend/simulation/agent/exagent.go
index b7d04bd..7c896da 100644
--- a/backend/simulation/agent/exagent.go
+++ b/backend/simulation/agent/exagent.go
@@ -107,6 +107,10 @@ func (agt *ExAgent) Act() {
 	agt.orientation = agt.movement
 }
 
+func (agt *ExAgent) Type() envpkg.AgentType {
+	return envpkg.ExAgent
+}
+
 func (agt ExAgent) ToJsonObj() interface{} {
 	return ExAgentJson{ID: string(agt.id),
 		Value:       agt.value,
diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index a51dd6e..8bc574f 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -82,24 +82,24 @@ func (agt *HornetAgent) Percept() {
 func (agt *HornetAgent) Deliberate() {
 	distanceToTarget := float64(agt.env.GetMapDimension())
 	if agt.objective.Type == Bee {
-		if bee, ok := agt.objective.TargetedElem.(BeeAgent); ok {
+		if bee, ok := agt.objective.TargetedElem.(*BeeAgent); ok {
 			distanceToTarget = bee.Position().DistanceFrom(agt.Position())
 		}
 	}
 	for _, seen := range agt.seenElems {
-		if seen.Elem != nil {
-			switch seen.Elem.(type) {
-			case *BeeAgent:
-				bee := seen.Elem.(*BeeAgent)
-				distance := bee.Position().DistanceFrom(agt.Position())
-				if distance < distanceToTarget {
-					agt.objective.Type = Bee
-					agt.objective.TargetedElem = bee
-					agt.objective.Position = bee.Position().Copy()
-					distanceToTarget = distance
+		if seen.Elem != nil && seen.Elem != agt {
+			switch elem := seen.Elem.(type) {
+			case *Agent:
+				if elem.Type() == envpkg.Bee {
+					fmt.Printf("[%s] Found a close bee (%s) \n", agt.id, elem.ID())
+					distance := elem.Position().DistanceFrom(agt.Position())
+					if distance < distanceToTarget {
+						agt.objective.Type = Bee
+						agt.objective.TargetedElem = elem
+						agt.objective.Position = elem.Position().Copy()
+						distanceToTarget = distance
+					}
 				}
-			case BeeAgent:
-				fmt.Println("=================================Hornet sees a bee wtf")
 			}
 		}
 	}
@@ -119,17 +119,21 @@ func (agt *HornetAgent) Act() {
 			agt.objective.Type = None
 		}
 	case Bee:
-		fmt.Println("=====================Hornet attacking bee !")
-		bee := agt.objective.TargetedElem.(*BeeAgent)
+		bee := agt.objective.TargetedElem.(*Agent)
+		fmt.Printf("[%s] Hornet attacking %s !\n", agt.id, bee.ID())
 		agt.gotoNextStepTowards(bee.Position().Copy())
 		if agt.Position().Near(bee.pos.Copy(), 1) {
 			bee.Kill()
 			agt.objective.Type = None
-			fmt.Println("=====================Hornet killed bee !!!")
+			fmt.Printf("[%s] Hornet killed %s !!!\n", agt.id, bee.ID())
 		}
 	}
 }
 
+func (agt HornetAgent) Type() envpkg.AgentType {
+	return envpkg.Hornet
+}
+
 func (agt *HornetAgent) ToJsonObj() interface{} {
 	return HornetAgentJson{
 		ID:          string(agt.id),
diff --git a/backend/simulation/agent/vision/equilateralTriangleVision.go b/backend/simulation/agent/vision/equilateralTriangleVision.go
index f24f61e..2f500df 100644
--- a/backend/simulation/agent/vision/equilateralTriangleVision.go
+++ b/backend/simulation/agent/vision/equilateralTriangleVision.go
@@ -5,6 +5,7 @@ import (
 	"sort"
 
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
 )
 
@@ -26,7 +27,9 @@ func EquilateralTriangleVision(agt envpkg.IAgent, env *envpkg.Environment, dista
 	addElemToList := func(x, y int) {
 		if env.IsValidPosition(x, y) {
 			if pointIsInTriangle(float64(x), float64(y), topCornerX, topCornerY, leftCornerX, leftCornerY, rightCornerX, rightCornerY) {
-				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, env.GetAt(x, y)))
+				elem := env.GetAt(x, y)
+				seenElems = append(seenElems, NewSeenElem(&envpkg.Position{X: x, Y: y}, elem))
+				// fmt.Printf("Agent %s sees element %v at position (%d, %d) (%s)\n", agt.ID(), elem, x, y, reflect.TypeOf(elem))
 			}
 		}
 	}
diff --git a/backend/simulation/agent/vision/visionFunc.go b/backend/simulation/agent/vision/visionFunc.go
index 32e5fe9..0056637 100644
--- a/backend/simulation/agent/vision/visionFunc.go
+++ b/backend/simulation/agent/vision/visionFunc.go
@@ -31,7 +31,7 @@ func getTriangleCoordinates(startPt envpkg.Position, height float64, oppositeBas
 	//        of topCorner on C1
 	// Combining the two circles gives us the leftCorner and rightCorner points
 	// First circle : C1 = (x - a)² + (y - b)² = height²
-	// Second circle : C2 = (x - p)² + (y - q)² = oppositeBaseSize²
+	// Second circle : C2 = (x - p)² + (y - q)² = (oppositeBaseSize/2)²
 	// We get the tangent of the center of C2 (point on C1)
 	// The points of intersection between C2 and the tangent of C1 are the leftCorner and rightCorner
 	// This allows for maintining precision over the height of the triangle and the opposite base size
diff --git a/backend/simulation/environment/environment.go b/backend/simulation/environment/environment.go
index 6830ee8..207609a 100644
--- a/backend/simulation/environment/environment.go
+++ b/backend/simulation/environment/environment.go
@@ -34,7 +34,18 @@ func (env *Environment) GetAt(x int, y int) interface{} {
 	if x < 0 || y < 0 || x >= mapDimension || y >= mapDimension {
 		return nil
 	}
-	return env.grid[x][y]
+	elem := env.grid[x][y]
+	if elem == nil {
+		return nil
+	}
+	switch obj := elem.(type) {
+	case IAgent:
+		return obj
+	case IObject:
+		return obj
+	default:
+		return obj
+	}
 }
 
 func (env *Environment) GetMap() [][]interface{} {
@@ -49,13 +60,14 @@ func (env *Environment) IsValidPosition(x int, y int) bool {
 	return x >= 0 && y >= 0 && x < mapDimension && y < mapDimension
 }
 
+// Returns true is added on grid, false if not
 func (env *Environment) AddAgent(agt IAgent) bool {
 	pos := agt.Position()
+	env.agts = append(env.agts, agt)
 	if pos != nil && env.GetAt(pos.X, pos.Y) == nil {
-		//return false
 		env.grid[pos.X][pos.Y] = agt
+		return false
 	}
-	env.agts = append(env.agts, agt)
 	return true
 }
 
@@ -107,9 +119,10 @@ func (env *Environment) PathFinding(start *Position, end *Position, numberMoves
 	startNode := &node{position: start.Copy(), cost: 0, heuristic: start.ManhattanDistance(end)}
 	openList = append(openList, startNode)
 
-	// We allow pathfinding to last 50 iterations
+	// We allow pathfinding to last 3 times the number of moves
 	cpt := 0
-	for len(openList) > 0 && cpt < numberMoves*2 {
+	for len(openList) > 0 && cpt < numberMoves*3 {
+		//fmt.Printf("%d ", cpt)
 		currentNode := openList[0]
 		currentIndex := 0
 		for index, node := range openList {
@@ -147,6 +160,7 @@ func (env *Environment) PathFinding(start *Position, end *Position, numberMoves
 		}
 		cpt++
 	}
+	//fmt.Println("")
 
 	path := []*Position{}
 	var currentNode *node = nil
diff --git a/backend/simulation/environment/iagent.go b/backend/simulation/environment/iagent.go
index 432a60e..8c41bf4 100644
--- a/backend/simulation/environment/iagent.go
+++ b/backend/simulation/environment/iagent.go
@@ -15,6 +15,14 @@ const (
 	SouthWest Orientation = "SW"
 )
 
+type AgentType string
+
+const (
+	ExAgent AgentType = "ExAgent"
+	Bee     AgentType = "Bee"
+	Hornet  AgentType = "Hornet"
+)
+
 // IAgent is an interface representing the agent's actions
 // limits
 type IAgent interface {
@@ -27,4 +35,5 @@ type IAgent interface {
 	Percept()
 	Deliberate()
 	Act()
+	Type() AgentType
 }
diff --git a/backend/simulation/environment/position.go b/backend/simulation/environment/position.go
index 983bc6e..ed4dde2 100644
--- a/backend/simulation/environment/position.go
+++ b/backend/simulation/environment/position.go
@@ -1,7 +1,6 @@
 package environment
 
 import (
-	"fmt"
 	"math"
 
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/utils"
@@ -32,7 +31,6 @@ func (p *Position) move(grid [][]interface{}, newX int, newY int, elem interface
 	// Not a simulation
 	if grid != nil {
 		if grid[p.X][p.Y] == elem {
-			fmt.Println("Grid is not nil : ", grid[p.X][p.Y])
 			grid[p.X][p.Y], grid[newX][newY] = nil, grid[p.X][p.Y]
 		} else {
 			grid[newX][newY] = elem
diff --git a/backend/simulation/simulation.go b/backend/simulation/simulation.go
index 64fe28e..ceca97c 100644
--- a/backend/simulation/simulation.go
+++ b/backend/simulation/simulation.go
@@ -162,8 +162,10 @@ func (simu *Simulation) Run(maWs *websocket.Conn) {
 			c := agt.GetSyncChan()
 			simu.env.Lock()
 			c <- true
-			// If dead
-			if !<-c {
+			isAlive := <-c
+			// If dead, remove agent from simulation
+			if !isAlive {
+				fmt.Printf("{{SIMULATION}} - [%s] is dead\n", agt.ID())
 				simu.agts = append(simu.agts[:i], simu.agts[i+1:]...)
 			}
 			simu.env.Unlock()
diff --git a/config.yaml b/config.yaml
index f6a07c5..4fa1ffe 100644
--- a/config.yaml
+++ b/config.yaml
@@ -1,15 +1,15 @@
 # Environment 
-MapDimension: 40
+MapDimension: 25
 
 # Bees
-NumberBees: 20
+NumberBees: 10
 BeeAgentVisionRange: 4.0
 MaxNectar: 70 # in mg -> https://wildflowermeadows.com/2024/01/how-far-do-honeybees-fly/
 
 # Hornets
-NumberHornets: 2
+NumberHornets: 1
 HornetAgentVisionRange: 4.0
 
 # Flowers
-NumberFlowers: 20
+NumberFlowers: 15
 NumberFlowerPatches: 3
-- 
GitLab


From 222d8c49cb4f1bef0cc9d5a43bc40d5cde88e528 Mon Sep 17 00:00:00 2001
From: StutenEXE <alexandre.bidaux78@gmail.com>
Date: Thu, 2 Jan 2025 09:53:31 +0100
Subject: [PATCH 5/7] Fixes : agt locked on position & flowers spawn over hives
 (& other possible objects)

---
 backend/simulation/agent/agent.go          | 50 +++++++++++++++-------
 backend/simulation/agent/beeAgent.go       |  6 ++-
 backend/simulation/agent/hornetAgent.go    | 19 +++++---
 backend/simulation/environment/position.go | 13 ------
 backend/simulation/simulation.go           |  2 +-
 5 files changed, 54 insertions(+), 36 deletions(-)

diff --git a/backend/simulation/agent/agent.go b/backend/simulation/agent/agent.go
index 9feb8e3..1445545 100644
--- a/backend/simulation/agent/agent.go
+++ b/backend/simulation/agent/agent.go
@@ -168,8 +168,14 @@ func (agt *Agent) goSouthWest() bool {
 
 // https://web.archive.org/web/20171022224528/http://www.policyalmanac.org:80/games/aStarTutorial.htm
 func (agt *Agent) gotoNextStepTowards(pos *envpkg.Position) {
+	// If the position is already occupied and we are near it (by one), we don't move
+	if agt.env.GetAt(pos.X, pos.Y) != nil {
+		if agt.pos.Near(pos, 1) {
+			return
+		}
+	}
 	chain := agt.env.PathFinding(agt.pos, pos, agt.speed)
-	// We remove the first element who is the current position of the agent
+	// We remove the first element (it is the current position of the agent)
 	if len(chain) > 0 && chain[0].Equal(agt.pos) {
 		chain = chain[1:]
 	}
@@ -250,6 +256,23 @@ func (agt *Agent) wander() {
 		} else if closestBorder.Y == agt.env.GetMapDimension()-1 {
 			keepAwayFromBorderPos.GoNorth(nil, nil)
 		}
+		// If the position is already occupied by something, we find the closest available position
+		if agt.env.GetAt(keepAwayFromBorderPos.X, keepAwayFromBorderPos.Y) != nil {
+			surroundings := agt.pos.GetNeighbours(agt.speed)
+			closestPosition := agt.pos.Copy()
+			minDistance := agt.pos.DistanceFrom(keepAwayFromBorderPos)
+			for _, pos := range surroundings {
+				if agt.env.GetAt(pos.X, pos.Y) != nil {
+					continue
+				}
+				distance := pos.DistanceFrom(keepAwayFromBorderPos)
+				if distance <= minDistance {
+					closestPosition = pos.Copy()
+					minDistance = distance
+				}
+			}
+			keepAwayFromBorderPos = closestPosition.Copy()
+		}
 		agt.objective.Position = keepAwayFromBorderPos.Copy()
 		agt.objective.Type = Position
 		fmt.Printf("[%s] Too close to border (%d %d), going to (%d %d)\n", agt.id, closestBorder.X, closestBorder.Y, agt.objective.Position.X, agt.objective.Position.Y)
@@ -269,16 +292,8 @@ func (agt *Agent) wander() {
 }
 
 func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
+	// We get the accessible surroundings of the agent
 	surroundings := agt.pos.GetNeighbours(agt.speed)
-	// We remove the positions that are occupied
-	removeCpt := 0
-	for i := 0; i < len(surroundings); i++ {
-		idx := i - removeCpt
-		if agt.env.GetAt(surroundings[idx].X, surroundings[idx].Y) != nil || surroundings[idx].Equal(agt.lastPos) {
-			surroundings = append(surroundings[:idx], surroundings[idx+1:]...)
-			removeCpt++
-		}
-	}
 	nextWanderingOrientation := agt.orientation
 	// Chances : 3/4 th keeping the same orientation, 1/8th changing to the left, 1/8th changing to the right
 	chancesToChangeOrientation := rand.Intn(8)
@@ -359,10 +374,17 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 			newObjective.GoSouthWest(nil, nil)
 		}
 	}
-	// Find the closest available position in surroundings
+	// If this position is a valid one, we return it
+	if agt.env.GetAt(newObjective.X, newObjective.Y) == nil && !newObjective.Equal(agt.lastPos) {
+		return newObjective
+	}
+	// Else we find the closest available position in surroundings
 	closestPosition := agt.pos.Copy()
 	minDistance := agt.pos.DistanceFrom(newObjective)
 	for _, pos := range surroundings {
+		if agt.env.GetAt(pos.X, pos.Y) == nil && !pos.Equal(agt.lastPos) {
+			continue
+		}
 		distance := pos.DistanceFrom(newObjective)
 		if distance < minDistance {
 			closestPosition = pos.Copy()
@@ -370,10 +392,8 @@ func (agt *Agent) getNextWanderingPosition() *envpkg.Position {
 		}
 	}
 	newObjective = closestPosition
-
-	// We add the new last position to avoid cycles
-	agt.lastPos = newObjective.Copy()
-
+	// We remember the current position to avoid cycles
+	agt.lastPos = agt.pos.Copy()
 	return newObjective
 }
 
diff --git a/backend/simulation/agent/beeAgent.go b/backend/simulation/agent/beeAgent.go
index 1154495..f13788b 100644
--- a/backend/simulation/agent/beeAgent.go
+++ b/backend/simulation/agent/beeAgent.go
@@ -2,7 +2,6 @@ package agent
 
 import (
 	"fmt"
-	"reflect"
 	"time"
 
 	"math/rand"
@@ -118,7 +117,7 @@ func (agt *BeeAgent) foragerDeliberation() {
 		if seen.Elem != nil {
 			switch elem := (seen.Elem).(type) {
 			case *HornetAgent:
-				fmt.Printf("[%s] *****************Close to hornet %v\n", agt.id, reflect.TypeOf(seen.Elem))
+				fmt.Printf("[%s] Close to hornet %v\n", agt.id, elem.ID())
 				closestHornet = elem
 			case *obj.Flower:
 				if !hasAlreadySeenCloserFlower {
@@ -169,6 +168,9 @@ func (agt *BeeAgent) foragerAction() {
 		case Position:
 			if agt.pos.Equal(objf.Position) {
 				agt.objective.Type = None
+			} else if agt.env.GetAt(objf.Position.X, objf.Position.Y) != nil && agt.pos.Near(objf.Position, 1) {
+				// In some cases, the agent wants to go to a position where there is already an element (agent or object)
+				agt.objective.Type = None
 			} else {
 				agt.gotoNextStepTowards(objf.Position.Copy())
 				if agt.pos.Equal(objf.Position) {
diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index 8bc574f..bf3b053 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -109,17 +109,26 @@ func (agt *HornetAgent) Deliberate() {
 }
 
 func (agt *HornetAgent) Act() {
-	if agt.objective.Type == None {
+	objf := &agt.objective
+	if objf.Type == None {
 		return
 	}
-	switch agt.objective.Type {
+	switch objf.Type {
 	case Position:
-		agt.gotoNextStepTowards(agt.objective.Position.Copy())
-		if agt.Position().Equal(agt.objective.Position) {
+		if agt.pos.Equal(objf.Position) {
+			agt.objective.Type = None
+		} else if agt.pos.Equal(agt.lastPos) {
+			// In some cases, the agent gets stuck on an unattaignable position
+			agt.lastPos = objf.Position.Copy()
 			agt.objective.Type = None
+		} else {
+			agt.gotoNextStepTowards(objf.Position.Copy())
+			if agt.pos.Equal(objf.Position) {
+				agt.objective.Type = None
+			}
 		}
 	case Bee:
-		bee := agt.objective.TargetedElem.(*Agent)
+		bee := objf.TargetedElem.(*Agent)
 		fmt.Printf("[%s] Hornet attacking %s !\n", agt.id, bee.ID())
 		agt.gotoNextStepTowards(bee.Position().Copy())
 		if agt.Position().Near(bee.pos.Copy(), 1) {
diff --git a/backend/simulation/environment/position.go b/backend/simulation/environment/position.go
index ed4dde2..d008f7b 100644
--- a/backend/simulation/environment/position.go
+++ b/backend/simulation/environment/position.go
@@ -125,17 +125,4 @@ func (p Position) GetNeighbours(distance int) []*Position {
 		}
 	}
 	return neighbours
-	// return []*Position{
-	// 	// NW N NE
-	// 	NewPosition(p.X-1, p.Y-1, p.maxX, p.maxY),
-	// 	NewPosition(p.X, p.Y-1, p.maxX, p.maxY),
-	// 	NewPosition(p.X+1, p.Y-1, p.maxX, p.maxY),
-	// 	// W E
-	// 	NewPosition(p.X-1, p.Y, p.maxX, p.maxY),
-	// 	NewPosition(p.X+1, p.Y, p.maxX, p.maxY),
-	// 	// SW S SE
-	// 	NewPosition(p.X-1, p.Y+1, p.maxX, p.maxY),
-	// 	NewPosition(p.X, p.Y+1, p.maxX, p.maxY),
-	// 	NewPosition(p.X+1, p.Y+1, p.maxX, p.maxY),
-	// }
 }
diff --git a/backend/simulation/simulation.go b/backend/simulation/simulation.go
index ceca97c..8dda702 100644
--- a/backend/simulation/simulation.go
+++ b/backend/simulation/simulation.go
@@ -67,7 +67,7 @@ func NewSimulation(nbees int, nflowers int, nhornets int, maWs *websocket.Conn)
 		availablePositions := make([]*envpkg.Position, 0)
 		for x := centerOfPatchX - offset; x < centerOfPatchX+offset; x++ {
 			for y := centerOfPatchY - offset; y < centerOfPatchY+offset; y++ {
-				if simu.env.IsValidPosition(x, y) {
+				if simu.env.IsValidPosition(x, y) && simu.env.GetAt(x, y) == nil {
 					availablePositions = append(availablePositions, envpkg.NewPosition(x, y, mapDimension, mapDimension))
 				}
 			}
-- 
GitLab


From f3d9879e7e94bbc5141d3973ab07c5fad06f8558 Mon Sep 17 00:00:00 2001
From: Quentin Valakou <quentin.valakou@gmail.com>
Date: Fri, 3 Jan 2025 21:14:26 +0100
Subject: [PATCH 6/7] Comportement Hornet attaque Hive v0

---
 backend/simulation/agent/agent.go             |  1 -
 backend/simulation/agent/hornetAgent.go       | 57 ++++++++++++++++++-
 backend/simulation/environment/environment.go | 10 ++++
 backend/simulation/environment/iobject.go     |  7 +++
 backend/simulation/object/flower.go           |  4 ++
 backend/simulation/object/hive.go             | 23 +++++---
 backend/simulation/simulation.go              |  2 +-
 7 files changed, 93 insertions(+), 11 deletions(-)

diff --git a/backend/simulation/agent/agent.go b/backend/simulation/agent/agent.go
index 1445545..909b4af 100644
--- a/backend/simulation/agent/agent.go
+++ b/backend/simulation/agent/agent.go
@@ -401,7 +401,6 @@ func (agt *Agent) Kill() {
 	agt.env.RemoveAgent(agt)
 	agt.alive = false
 	agt.pos = nil
-
 }
 
 func (agt Agent) Type() envpkg.AgentType {
diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index bf3b053..3aef7c2 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -7,6 +7,7 @@ import (
 
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/agent/vision"
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
+	obj "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/object"
 )
 
 // HornetAgent hérite de /simulation/agent/agent.go "struct Agent"
@@ -41,6 +42,25 @@ func NewHornetAgent(id string, env *envpkg.Environment, syncChan chan bool, s in
 	return hAgent
 }
 
+// A simple way to organize target priority for the hornet.
+// If the hornet sees the Hive, and is surroundered by at least 2 of its kind, it will prioritize it.
+// false : bee
+// true : hive
+func PriorityTarget(hornet HornetAgent) bool {
+	nbHornet := 0
+	for _, seen := range hornet.seenElems {
+		switch elem := seen.Elem.(type) {
+		case *HornetAgent:
+			if elem.ID() != hornet.ID() {
+				fmt.Printf("HornetAgent %s seen by %s\n", elem.ID(), hornet.ID())
+				nbHornet++
+			}
+		}
+	}
+	fmt.Printf("nbHornet = %d\n", nbHornet)
+	return nbHornet >= 1
+}
+
 func (agt *HornetAgent) Percept() {
 	if agt.pos == nil {
 		chance := rand.Intn(2)
@@ -78,7 +98,8 @@ func (agt *HornetAgent) Percept() {
 	}
 }
 
-// The hornet agent always targets the closest bee
+// The hornet agent always targets the closest bee unless :
+// Sees a hive + is close to at least 2 of its kind
 func (agt *HornetAgent) Deliberate() {
 	distanceToTarget := float64(agt.env.GetMapDimension())
 	if agt.objective.Type == Bee {
@@ -86,11 +107,17 @@ func (agt *HornetAgent) Deliberate() {
 			distanceToTarget = bee.Position().DistanceFrom(agt.Position())
 		}
 	}
+	if agt.objective.Type == Hive {
+		if hive, ok := agt.objective.TargetedElem.(*obj.Hive); ok {
+			distanceToTarget = hive.Position().DistanceFrom(agt.Position())
+		}
+	}
 	for _, seen := range agt.seenElems {
 		if seen.Elem != nil && seen.Elem != agt {
+			priority := PriorityTarget(*agt)
 			switch elem := seen.Elem.(type) {
 			case *Agent:
-				if elem.Type() == envpkg.Bee {
+				if elem.Type() == envpkg.Bee && !priority {
 					fmt.Printf("[%s] Found a close bee (%s) \n", agt.id, elem.ID())
 					distance := elem.Position().DistanceFrom(agt.Position())
 					if distance < distanceToTarget {
@@ -100,9 +127,22 @@ func (agt *HornetAgent) Deliberate() {
 						distanceToTarget = distance
 					}
 				}
+			case *obj.Hive:
+				if elem.TypeObject() == envpkg.Hive && priority {
+					fmt.Printf("[%s] Found a close hive (%s) \n", agt.id, elem.ID())
+					distance := elem.Position().DistanceFrom(agt.Position())
+					if distance < distanceToTarget {
+						agt.objective.Type = Hive
+						agt.objective.TargetedElem = elem
+						agt.objective.Position = elem.Position().Copy()
+						distanceToTarget = distance
+
+					}
+				}
 			}
 		}
 	}
+	fmt.Printf(string(agt.objective.Type))
 	if agt.objective.Type == None {
 		agt.wander()
 	}
@@ -136,6 +176,19 @@ func (agt *HornetAgent) Act() {
 			agt.objective.Type = None
 			fmt.Printf("[%s] Hornet killed %s !!!\n", agt.id, bee.ID())
 		}
+
+	case Hive:
+		hive := objf.TargetedElem.(*obj.Hive)
+		fmt.Printf("[%s] Hornet attacking hive %s !\n", agt.id, hive.ID())
+		agt.gotoNextStepTowards(hive.Position().Copy())
+		if agt.Position().Near(hive.Pos.Copy(), 1) {
+			if hive.IsAlive() {
+				hive.Die()
+			}
+			agt.objective.Type = None
+			fmt.Printf("[%s] Hornet destructed %s !!!\n", agt.id, hive.ID())
+		}
+		fmt.Println("Hornet attacking Hive")
 	}
 }
 
diff --git a/backend/simulation/environment/environment.go b/backend/simulation/environment/environment.go
index 207609a..a7742f7 100644
--- a/backend/simulation/environment/environment.go
+++ b/backend/simulation/environment/environment.go
@@ -81,6 +81,16 @@ func (env *Environment) RemoveAgent(agt IAgent) {
 	}
 }
 
+func (env *Environment) RemoveObject(obj IObject) {
+	for i, a := range env.objs {
+		if a.ID() == obj.ID() {
+			env.objs = append(env.objs[:i], env.objs[i+1:]...)
+			env.grid[obj.Position().X][obj.Position().Y] = nil
+			break
+		}
+	}
+}
+
 func (env *Environment) AddObject(obj IObject) bool {
 	pos := obj.Position()
 	if env.GetAt(pos.X, pos.Y) != nil {
diff --git a/backend/simulation/environment/iobject.go b/backend/simulation/environment/iobject.go
index a1c8624..6f65d04 100644
--- a/backend/simulation/environment/iobject.go
+++ b/backend/simulation/environment/iobject.go
@@ -2,6 +2,13 @@ package environment
 
 type ObjectID string
 
+type ObjectType string
+
+const (
+	Flower ObjectType = "Flower"
+	Hive   ObjectType = "Hive"
+)
+
 type IObject interface {
 	ID() ObjectID
 	Position() *Position
diff --git a/backend/simulation/object/flower.go b/backend/simulation/object/flower.go
index 9de0d4c..abbf692 100644
--- a/backend/simulation/object/flower.go
+++ b/backend/simulation/object/flower.go
@@ -116,3 +116,7 @@ func (f *Flower) RetreiveNectar(nectar int) int {
 func (f *Flower) GetNectar() int {
 	return f.nectar
 }
+
+func (f Flower) ObjectType() envpkg.ObjectType {
+	return envpkg.Flower
+}
diff --git a/backend/simulation/object/hive.go b/backend/simulation/object/hive.go
index 9cee815..405ee7b 100644
--- a/backend/simulation/object/hive.go
+++ b/backend/simulation/object/hive.go
@@ -6,12 +6,13 @@ import (
 
 type Hive struct {
 	id       envpkg.ObjectID
-	pos      *envpkg.Position
+	Pos      *envpkg.Position
 	qHoney   int
 	qNectar  int
 	qPollen  int
 	queen    bool
 	minHoney int
+	env      *envpkg.Environment
 }
 
 type HiveJson struct {
@@ -24,15 +25,16 @@ type HiveJson struct {
 	MinHoney       int             `json:"min_honey"`
 }
 
-func NewHive(id string, pos *envpkg.Position, qHoney int, qNectar int, qPollen int, minHoney int) *Hive {
+func NewHive(id string, pos *envpkg.Position, qHoney int, qNectar int, qPollen int, minHoney int, environment *envpkg.Environment) *Hive {
 	return &Hive{
 		id:       envpkg.ObjectID(id),
-		pos:      pos.Copy(),
+		Pos:      pos.Copy(),
 		qHoney:   qHoney,
 		qNectar:  qNectar,
 		qPollen:  qPollen,
 		queen:    true,
 		minHoney: minHoney,
+		env:      environment,
 	}
 }
 
@@ -41,13 +43,13 @@ func (h Hive) ID() envpkg.ObjectID {
 }
 
 func (h Hive) Position() *envpkg.Position {
-	return h.pos.Copy()
+	return h.Pos.Copy()
 }
 
 func (h Hive) Copy() interface{} {
 	return &Hive{
 		id:       h.id,
-		pos:      h.pos.Copy(),
+		Pos:      h.Pos.Copy(),
 		qHoney:   h.qHoney,
 		qNectar:  h.qNectar,
 		qPollen:  h.qPollen,
@@ -65,7 +67,7 @@ func (h *Hive) Become(h_alt interface{}) {
 
 	if ok {
 		h.id = altered_hive.id
-		h.pos = altered_hive.pos.Copy()
+		h.Pos = altered_hive.Pos.Copy()
 		h.qHoney = altered_hive.qHoney
 		h.qNectar = altered_hive.qNectar
 		h.qPollen = altered_hive.qPollen
@@ -77,7 +79,7 @@ func (h *Hive) Become(h_alt interface{}) {
 func (h Hive) ToJsonObj() interface{} {
 	return HiveJson{
 		ID:             string(h.id),
-		Position:       *h.pos.Copy(),
+		Position:       *h.Pos.Copy(),
 		QuantityHoney:  h.qHoney,
 		QuantityNectar: h.qNectar,
 		QuantityPollen: h.qPollen,
@@ -99,4 +101,11 @@ func (h Hive) IsAlive() (isAlive bool) {
 
 func (h *Hive) Die() {
 	h.queen = false
+	h.env.RemoveObject(h)
+	h.IsAlive()
+	h.Pos = nil
+}
+
+func (h Hive) TypeObject() envpkg.ObjectType {
+	return envpkg.Hive
 }
diff --git a/backend/simulation/simulation.go b/backend/simulation/simulation.go
index 8dda702..3e854be 100644
--- a/backend/simulation/simulation.go
+++ b/backend/simulation/simulation.go
@@ -46,7 +46,7 @@ func NewSimulation(nbees int, nflowers int, nhornets int, maWs *websocket.Conn)
 		id := fmt.Sprintf("Hive #%d", i)
 		x, y := rand.Intn(mapDimension), rand.Intn(mapDimension)
 		pos := envpkg.NewPosition(x, y, mapDimension, mapDimension)
-		hive := obj.NewHive(id, pos, 0, 0, 0, 10)
+		hive := obj.NewHive(id, pos, 0, 0, 0, 10, env)
 		// ajout de l'objet à la simulation
 		simu.objs = append(simu.objs, hive)
 		// ajout de l'objet à l'environnement
-- 
GitLab


From 093f8a7b9f6ed76d18e949455e56a5a1eb59a53f Mon Sep 17 00:00:00 2001
From: Quentin Valakou <quentin.valakou@gmail.com>
Date: Fri, 3 Jan 2025 21:52:00 +0100
Subject: [PATCH 7/7] Ajout d'un killcount

---
 backend/simulation/agent/hornetAgent.go | 45 +++++++++++++++----------
 1 file changed, 27 insertions(+), 18 deletions(-)

diff --git a/backend/simulation/agent/hornetAgent.go b/backend/simulation/agent/hornetAgent.go
index 3aef7c2..e8d325b 100644
--- a/backend/simulation/agent/hornetAgent.go
+++ b/backend/simulation/agent/hornetAgent.go
@@ -2,12 +2,11 @@ package agent
 
 import (
 	"fmt"
-	"math/rand"
-	"time"
-
 	"gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/agent/vision"
 	envpkg "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/environment"
 	obj "gitlab.utc.fr/bidauxal/ai30_valakou_martins_chartier_bidaux/backend/simulation/object"
+	"math/rand"
+	"time"
 )
 
 // HornetAgent hérite de /simulation/agent/agent.go "struct Agent"
@@ -16,6 +15,7 @@ type HornetAgent struct {
 	Agent
 	birthDate time.Time
 	seenElems []*vision.SeenElem
+	killCount int
 }
 
 type HornetAgentJson struct {
@@ -24,6 +24,7 @@ type HornetAgentJson struct {
 	Orientation envpkg.Orientation `json:"orientation"`
 	Objective   objective          `json:"objective"`
 	SeenElems   []*vision.SeenElem `json:"seenElems"`
+	KillCount   int                `json:"killCount"`
 }
 
 func NewHornetAgent(id string, env *envpkg.Environment, syncChan chan bool, s int) *HornetAgent {
@@ -39,6 +40,7 @@ func NewHornetAgent(id string, env *envpkg.Environment, syncChan chan bool, s in
 		objective:  objective{Position: nil, Type: None},
 	}
 	hAgent.birthDate = time.Now()
+	hAgent.killCount = 0
 	return hAgent
 }
 
@@ -48,6 +50,9 @@ func NewHornetAgent(id string, env *envpkg.Environment, syncChan chan bool, s in
 // true : hive
 func PriorityTarget(hornet HornetAgent) bool {
 	nbHornet := 0
+	if hornet.killCount >= 5 {
+		return true
+	}
 	for _, seen := range hornet.seenElems {
 		switch elem := seen.Elem.(type) {
 		case *HornetAgent:
@@ -169,26 +174,30 @@ func (agt *HornetAgent) Act() {
 		}
 	case Bee:
 		bee := objf.TargetedElem.(*Agent)
-		fmt.Printf("[%s] Hornet attacking %s !\n", agt.id, bee.ID())
-		agt.gotoNextStepTowards(bee.Position().Copy())
-		if agt.Position().Near(bee.pos.Copy(), 1) {
-			bee.Kill()
-			agt.objective.Type = None
-			fmt.Printf("[%s] Hornet killed %s !!!\n", agt.id, bee.ID())
+		if bee != nil {
+			fmt.Printf("[%s] Hornet attacking %s !\n", agt.id, bee.ID())
+			agt.gotoNextStepTowards(bee.Position().Copy())
+			if agt.Position().Near(bee.pos.Copy(), 1) {
+				bee.Kill()
+				agt.killCount++
+				agt.objective.Type = None
+				fmt.Printf("[%s] killed %s !!!, Has %d kill(s) \n", agt.id, bee.ID(), agt.killCount)
+			}
 		}
 
 	case Hive:
-		hive := objf.TargetedElem.(*obj.Hive)
-		fmt.Printf("[%s] Hornet attacking hive %s !\n", agt.id, hive.ID())
-		agt.gotoNextStepTowards(hive.Position().Copy())
-		if agt.Position().Near(hive.Pos.Copy(), 1) {
-			if hive.IsAlive() {
-				hive.Die()
+		if agt.pos.Near(objf.Position, 1) {
+			if hive, ok := objf.TargetedElem.(*obj.Hive); ok {
+				if hive.IsAlive() {
+					hive.Die()
+				} else {
+					fmt.Printf("Hive is already killed \n !")
+				}
 			}
-			agt.objective.Type = None
-			fmt.Printf("[%s] Hornet destructed %s !!!\n", agt.id, hive.ID())
+		} else {
+			fmt.Printf("Hornet [%s] going to kill hive %v\n", agt.id, objf.TargetedElem.(envpkg.IObject).ID())
+			agt.gotoNextStepTowards(objf.Position.Copy())
 		}
-		fmt.Println("Hornet attacking Hive")
 	}
 }
 
-- 
GitLab