Skip to content
Snippets Groups Projects
Commit 87878b7b authored by Julien Pillis's avatar Julien Pillis
Browse files

Merge branch 'signalisation' into 'main'

Signalisation

See merge request !6
parents 47975acd 09764dba
No related branches found
No related tags found
2 merge requests!7Main,!6Signalisation
......@@ -6,7 +6,7 @@ import (
)
func main() {
s := simulation.NewSimulation(1, -1, 600*time.Second)
s := simulation.NewSimulation(20, -1, 600*time.Second)
//go simulation.StartAPI(s)
s.Run()
}
......@@ -2,12 +2,12 @@ package algorithms
import (
"container/heap"
"math/rand"
)
/*
* Utilisation de l'algorithme A* pour les déplacements
* //TODO: Peut-être gérer un passage par référence et non par copie
* //TODO: faire des points de repère
*/
type Node struct {
row, col, cost, heuristic, width, height, orientation int
......@@ -30,6 +30,10 @@ func (nd *Node) Or() int {
return nd.orientation
}
func (nd *Node) Heuristic() int {
return nd.heuristic
}
type PriorityQueue []*Node
func (pq PriorityQueue) Len() int { return len(pq) }
......@@ -55,7 +59,10 @@ func (pq *PriorityQueue) Pop() interface{} {
return item
}
func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node) []Node {
type ZoneID int
type Coord [2]int
func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node, orientation bool) []Node {
pq := make(PriorityQueue, 0)
heap.Init(&pq)
......@@ -64,7 +71,7 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node) []Node
parents := make(map[Node]Node)
closestPoint := start // Initialisation avec le point de départ
closestDistance := heuristic(start.row, start.col, end)
closestDistance := Heuristic(start.row, start.col, end)
foundPath := false
......@@ -72,7 +79,7 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node) []Node
current := heap.Pop(&pq).(*Node)
// Mise à jour du point le plus proche si le point actuel est plus proche
currentDistance := heuristic(current.row, current.col, end)
currentDistance := Heuristic(current.row, current.col, end)
if currentDistance < closestDistance {
closestPoint = *current
closestDistance = currentDistance
......@@ -90,7 +97,7 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node) []Node
visited[*current] = true
neighbors := getNeighbors(matrix, *current, end, forbidenCell)
neighbors := getNeighbors(matrix, *current, end, forbidenCell, orientation)
for _, neighbor := range neighbors {
if !visited[*neighbor] {
parents[*neighbor] = *current
......@@ -112,25 +119,40 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node) []Node
return nil // Aucun chemin trouvé
}
func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node) []*Node {
func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node, orientation bool) []*Node {
//fmt.Println("okk")
neighbors := make([]*Node, 0)
// Possible moves: up, down, left, right, rotate (clockwise)
// Possible moves: up, down, left, right
possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
for _, move := range possibleMoves {
newRow, newCol := current.row+move[0], current.col+move[1]
for orientation := 0; orientation < 4; orientation++ {
current.orientation = orientation
// fmt.Println(orientation)
// Check if the new position is valid, considering agent dimensions and rotation
if isValidMove(matrix, current, forbiddenCell, newRow, newCol) {
if orientation {
for or := 0; or < 4; or++ {
current.orientation = or
//fmt.Println(orientation)
//Check if the new position is valid, considering agent dimensions and rotation
if isValidMove(matrix, current, forbiddenCell, newRow, newCol, orientation) {
neighbors = append(neighbors, &Node{
row: newRow,
col: newCol,
cost: current.cost + 1,
heuristic: Heuristic(newRow, newCol, end),
width: current.width,
height: current.height,
orientation: current.orientation,
})
}
}
} else {
if isValidMove(matrix, current, forbiddenCell, newRow, newCol, orientation) {
neighbors = append(neighbors, &Node{
row: newRow,
col: newCol,
cost: current.cost + 1,
heuristic: heuristic(newRow, newCol, end),
heuristic: Heuristic(newRow, newCol, end),
width: current.width,
height: current.height,
orientation: current.orientation,
......@@ -143,9 +165,11 @@ func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node)
return neighbors
}
func heuristic(row, col int, end Node) int {
func Heuristic(row, col int, end Node) int {
// Heuristique simple : distance de Manhattan
return abs(row-end.row) + abs(col-end.col)
// On introduit de l'aléatoire pour ajouter de la diversité dans la construction des chemins
// On évite d'avoir tout le temps le même chemin pour un même point de départ et d'arrivé
return abs(row-end.row) + abs(col-end.col) + rand.Intn(3)
}
func abs(x int) int {
......@@ -155,7 +179,7 @@ func abs(x int) int {
return x
}
func isValidMove(matrix [20][20]string, current Node, forbiddenCell Node, newRow, newCol int) bool {
func isValidMove(matrix [20][20]string, current Node, forbiddenCell Node, newRow, newCol int, orientation bool) bool {
// Check if the new position is within the bounds of the matrix
if newRow < 0 || newRow >= len(matrix) || newCol < 0 || newCol >= len(matrix[0]) {
return false
......@@ -165,31 +189,38 @@ func isValidMove(matrix [20][20]string, current Node, forbiddenCell Node, newRow
if forbiddenCell.row == newRow && forbiddenCell.col == newCol {
return false
}
// Check if the absolute coordinates overlap with obstacles in the matrix
if matrix[newRow][newCol] == "Q" || matrix[newRow][newCol] == "X" {
return false
}
// Check if the agent fits in the new position, considering its dimensions and rotation
lRowBound, uRowBound, lColBound, uColBound := calculateBounds(newRow, newCol, current.width, current.height, current.orientation)
if orientation {
lRowBound, uRowBound, lColBound, uColBound := calculateBounds(newRow, newCol, current.width, current.height, current.orientation)
for i := lRowBound; i < uRowBound; i++ {
for j := lColBound; j < uColBound; j++ {
for i := lRowBound; i < uRowBound; i++ {
for j := lColBound; j < uColBound; j++ {
// Calculate the absolute coordinates in the matrix
absRow, absCol := i, j
// Calculate the absolute coordinates in the matrix
absRow, absCol := i, j
// Check if the absolute coordinates are within the bounds of the matrix
if absRow < 0 || absRow >= len(matrix) || absCol < 0 || absCol >= len(matrix[0]) {
return false
}
// Check if the absolute coordinates are within the bounds of the matrix
if absRow < 0 || absRow >= len(matrix) || absCol < 0 || absCol >= len(matrix[0]) {
return false
}
// Check if the absolute coordinates overlap with forbidden cells or obstacles
if forbiddenCell.row == absRow && forbiddenCell.col == absCol {
return false
}
// Check if the absolute coordinates overlap with forbidden cells or obstacles
if forbiddenCell.row == absRow && forbiddenCell.col == absCol {
return false
}
// Check if the absolute coordinates overlap with obstacles in the matrix
if matrix[absRow][absCol] == "Q" || matrix[absRow][absCol] == "X" {
return false
// Check if the absolute coordinates overlap with obstacles in the matrix
if matrix[absRow][absCol] == "Q" || matrix[absRow][absCol] == "X" {
return false
}
}
}
}
return true
......
......@@ -6,15 +6,18 @@ package simulation
* // TODO: Gérer les moments où les agents font du quasi-sur place car ils ne peuvent plus bouger
* // TODO: Il arrive encore que certains agents soient bloqués, mais c'est quand il n'y a aucun mouvement possible.
* // Il faudrait faire en sorte que les agents bougent et laisse passer les autres
*
*/
import (
//"fmt"
"log"
"math/rand"
//"math"
//"math/rand"
alg "metrosim/internal/algorithms"
"time"
"fmt"
)
type Action int64
......@@ -24,6 +27,7 @@ const (
Mark
Wait
Move
Disapear
)
type Coord [2]int
......@@ -46,6 +50,9 @@ type Agent struct {
width int
height int
orientation int
path []alg.Node
// visitedPanneaux map[alg.Node]bool
// visiting *alg.Node
}
type Behavior interface {
......@@ -57,7 +64,13 @@ type Behavior interface {
func NewAgent(id string, env *Environment, syncChan chan int, vitesse time.Duration, force int, politesse bool, behavior Behavior, departure, destination Coord, width, height int) *Agent {
isOn := make(map[Coord]string)
saveCells(&env.station, isOn, departure, width, height, 0)
return &Agent{AgentID(id), vitesse, force, politesse, departure, departure, destination, behavior, env, syncChan, Noop, isOn, false, width, height, 0}
// Enregistrement des panneaux menant à la zone
// visitedPanneaux := make(map[alg.Node]bool, len(env.panneaux[env.zones[destination]]))
// for _, panneau := range env.panneaux[env.zones[destination]] {
// visitedPanneaux[panneau] = false
// }
// visiting := alg.NewNode(destination[0], destination[1], 0, HeuristicWithObstacles(departure, destination, env), 0, 0)
return &Agent{AgentID(id), vitesse, force, politesse, departure, departure, destination, behavior, env, syncChan, Noop, isOn, false, width, height, 0, make([]alg.Node, 0)}
}
func (ag *Agent) ID() AgentID {
......@@ -75,6 +88,10 @@ func (ag *Agent) Start() {
ag.behavior.Deliberate(ag)
ag.behavior.Act(ag)
ag.syncChan <- step
if ag.decision == Disapear {
ag.env.RemoveAgent(*ag)
return
}
}
}()
}
......@@ -85,62 +102,79 @@ func (ag *Agent) Act(env *Environment) {
}
}
func IsMovementSafe(path []alg.Node, agt *Agent, env *Environment) bool {
func IsMovementSafe(path []alg.Node, agt *Agent, env *Environment) (bool, int) {
// Détermine si le movement est faisable
if len(path) <= 0 {
return false
return false, agt.orientation
}
// Calcul des bornes de position de l'agent avant mouvement
infRow, supRow, infCol, supCol := calculateBounds(agt.position, agt.width, agt.height, agt.orientation)
// Simulation du déplacement
ag := *agt
ag.position = Coord{path[0].Row(), path[0].Col()}
rotateAgent(&ag, path[0].Or())
// Calcul des bornes de position de l'agent avant mouvement
infRow, supRow, infCol, supCol := calculateBounds(agt.position, agt.width, agt.height, agt.orientation)
// Calcul des bornes de position de l'agent après mouvement
borneInfRow, borneSupRow, borneInfCol, borneSupCol := calculateBounds(ag.position, ag.width, ag.height, ag.orientation)
for i := borneInfRow; i < borneSupRow; i++ {
for j := borneInfCol; j < borneSupCol; j++ {
if !(j >= infCol && j < supCol && i >= infRow && i < supRow) && (env.station[i][j] != "B" && env.station[i][j] != "_") {
// Si on est sur un agent, en dehors de la zone qu'occupe l'agent avant déplacement, on est bloqué
return false
for or := 0; or < 4; or++ {
rotateAgent(&ag, or)
safe := true
// Calcul des bornes de position de l'agent après mouvement
borneInfRow, borneSupRow, borneInfCol, borneSupCol := calculateBounds(ag.position, ag.width, ag.height, ag.orientation)
if !(borneInfCol < 0 || borneInfRow < 0 || borneSupRow > 20 || borneSupCol > 20) {
for i := borneInfRow; i < borneSupRow; i++ {
for j := borneInfCol; j < borneSupCol; j++ {
if !(j >= infCol && j < supCol && i >= infRow && i < supRow) && (env.station[i][j] != "B" && env.station[i][j] != "_" && env.station[i][j] != "W" && env.station[i][j] != "S") {
// Si on n'est pas sur une case atteignable, en dehors de la zone qu'occupe l'agent avant déplacement, on est bloqué
safe = false
}
}
}
if safe {
return true, or
}
}
}
return true
return false, agt.orientation
}
func IsAgentBlocking(path []alg.Node, agt *Agent, env *Environment) bool {
// Détermine si un agent se trouve sur la case à visiter
// Détermine si le movement est faisable
if len(path) <= 0 {
return false
}
// Calcul des bornes de position de l'agent avant mouvement
infRow, supRow, infCol, supCol := calculateBounds(agt.position, agt.width, agt.height, agt.orientation)
// Simulation du déplacement
ag := *agt
ag.position = Coord{path[0].Row(), path[0].Col()}
rotateAgent(&ag, path[0].Or())
// Calcul des bornes de position de l'agent avant mouvement
infRow, supRow, infCol, supCol := calculateBounds(agt.position, agt.width, agt.height, agt.orientation)
// Calcul des bornes de position de l'agent après mouvement
borneInfRow, borneSupRow, borneInfCol, borneSupCol := calculateBounds(ag.position, ag.width, ag.height, ag.orientation)
for i := borneInfRow; i < borneSupRow; i++ {
for j := borneInfCol; j < borneSupCol; j++ {
if !(j >= infCol && j < supCol && i >= infRow && i < supRow) && env.station[i][j] == "A" {
// Si on est sur un agent, en dehors de la zone qu'occupe l'agent avant déplacement, on est bloqué
return true
for or := 0; or < 4; or++ {
rotateAgent(&ag, or)
blocking := false
// Calcul des bornes de position de l'agent après mouvement
borneInfRow, borneSupRow, borneInfCol, borneSupCol := calculateBounds(ag.position, ag.width, ag.height, ag.orientation)
if !(borneInfCol < 0 || borneInfRow < 0 || borneSupRow > 20 || borneSupCol > 20) {
for i := borneInfRow; i < borneSupRow; i++ {
for j := borneInfCol; j < borneSupCol; j++ {
if !(j >= infCol && j < supCol && i >= infRow && i < supRow) && env.station[i][j] == "A" {
// Si on n'est pas sur une case atteignable, en dehors de la zone qu'occupe l'agent avant déplacement, on est bloqué
blocking = true
}
}
}
if !blocking {
// Si on n'a pas trouvé d'agent bloquant pour cette nouvelle position, on retourne faux
return false
}
}
}
return false
// Le cas où dans tous les mouvements on est bloqué par un agent
return true
}
func (ag *Agent) isStuck() bool {
......@@ -175,39 +209,54 @@ func (ag *Agent) isStuck() bool {
func (ag *Agent) MoveAgent() {
// ============ Initialisation des noeuds de départ ======================
start := *alg.NewNode(ag.position[0], ag.position[1], 0, 0, ag.width, ag.height)
end := *alg.NewNode(ag.destination[0], ag.destination[1], 0, 0, ag.width, ag.height)
// ================== Tentative de calcul du chemin =======================
timea := time.Now()
path := alg.FindPath(ag.env.station, start, end, *alg.NewNode(-1, -1, 0, 0, 0, 0))
fmt.Println(time.Since(timea))
if len(ag.path) == 0 {
start, end := ag.generatePathExtremities()
// Recherche d'un chemin si inexistant
path := alg.FindPath(ag.env.station, start, end, *alg.NewNode(-1, -1, 0, 0, 0, 0), false)
ag.path = path
}
// ================== Etude de faisabilité =======================
// fmt.Println(ag.position,path[0])
if IsAgentBlocking(path, ag, ag.env) {
// Si un agent bloque notre déplacement, on attend un temps aléatoire, et reconstruit un chemin en évitant la position
if IsAgentBlocking(ag.path, ag, ag.env) {
// TODO:voir comment gérer les situations de blocage
start, end := ag.generatePathExtremities()
// Si un agent bloque notre déplacement, on attend un temps aléatoire, et reconstruit
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
path = alg.FindPath(ag.env.station, start, end, path[0])
time.Sleep(time.Second)
path := alg.FindPath(ag.env.station, start, end, *alg.NewNode(-1, -1, 0, 0, 0, 0), false)
ag.path = path
return
}
if IsMovementSafe(path, ag, ag.env) {
removeAgent(&ag.env.station, ag)
rotateAgent(ag, path[0].Or())
// ================== Déplacement si aucun problème =======================
safe, or := IsMovementSafe(ag.path, ag, ag.env)
if safe {
RemoveAgent(&ag.env.station, ag)
rotateAgent(ag, or)
//ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = ag.isOn
ag.position[0] = path[0].Row()
ag.position[1] = path[0].Col()
ag.position[0] = ag.path[0].Row()
ag.position[1] = ag.path[0].Col()
if len(ag.path) > 1 {
ag.path = ag.path[1:]
} else {
ag.path = nil
}
saveCells(&ag.env.station, ag.isOn, ag.position, ag.width, ag.height, ag.orientation)
//ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = "A"
writeAgent(&ag.env.station, ag)
// ============ Prise en compte de la vitesse de déplacement ======================
time.Sleep(ag.vitesse * time.Millisecond)
//fmt.Println(path[0])
//fmt.Println(ag.position)
}
}
func (ag *Agent) generatePathExtremities() (alg.Node, alg.Node) {
// Génère les points extrêmes du chemin de l'agent
start := *alg.NewNode(ag.position[0], ag.position[1], 0, 0, ag.width, ag.height)
destination := ag.destination
end := *alg.NewNode(destination[0], destination[1], 0, 0, ag.width, ag.height)
return start, end
}
func removeAgent(matrix *[20][20]string, agt *Agent) {
func RemoveAgent(matrix *[20][20]string, agt *Agent) {
// Supprime l'agent de la matrice
// Calcul des bornes de position de l'agent
......@@ -249,17 +298,24 @@ func saveCells(matrix *[20][20]string, savedCells map[Coord]string, position Coo
func removeCoord(to_remove Coord, mapping map[Coord]string) {
// Suppression d'une clé dans une map
for coord, _ := range mapping {
if coord[0] == to_remove[0] && coord[1] == to_remove[1] {
if equalCoord(&coord, &to_remove) {
delete(mapping, coord)
}
}
}
func equalCoord(coord1, coord2 *Coord) bool {
// Vérifie l'égalité de 2 objets Coord
return coord1[0] == coord2[0] && coord1[1] == coord2[1]
}
// Fonction utilitaire de rotation
func rotateAgent(agt *Agent, orientation int) {
agt.orientation = orientation
}
func calculateBounds(position Coord, width, height, orientation int) (infRow, supRow, infCol, supCol int) {
// Fonction de génération des frontières d'un objet ayant une largeur et une hauteur, en focntion de son orientation
borneInfRow := 0
borneSupRow := 0
borneInfCol := 0
......@@ -268,21 +324,25 @@ func calculateBounds(position Coord, width, height, orientation int) (infRow, su
// Calcul des bornes de position de l'agent après mouvement
switch orientation {
case 0:
// Orienté vers le haut
borneInfRow = position[0] - width + 1
borneSupRow = position[0] + 1
borneInfCol = position[1]
borneSupCol = position[1] + height
case 1:
// Orienté vers la droite
borneInfRow = position[0]
borneSupRow = position[0] + height
borneInfCol = position[1]
borneSupCol = position[1] + width
case 2:
// Orienté vers le bas
borneInfRow = position[0]
borneSupRow = position[0] + width
borneInfCol = position[1]
borneSupCol = position[1] + height
case 3:
// Orienté vers la gauche
borneInfRow = position[0]
borneSupRow = position[0] + height
borneInfCol = position[1] - width + 1
......
......@@ -11,8 +11,12 @@ type Environment struct {
agentCount int
station [20][20]string
agentsChan map[AgentID]chan AgentID
// zones map[Coord]ZoneID // Zones de la station
// panneaux map[ZoneID][]alg.Node // Les panneaux de la station, permettant d'aller vers la zone
}
type ZoneID int
func NewEnvironment(ags []Agent, carte [20][20]string, agentsCh map[AgentID]chan AgentID) (env *Environment) {
return &Environment{ags: ags, agentCount: len(ags), station: carte, agentsChan: agentsCh}
}
......@@ -22,6 +26,18 @@ func (env *Environment) AddAgent(agt Agent) {
env.agentCount++
}
func (env *Environment) RemoveAgent(agt Agent) {
for i := 0; i < len(env.station); i++ {
if env.ags[i].id == agt.id{
// Utiliser la syntaxe de découpage pour supprimer l'élément
env.ags = append(env.ags[:i], env.ags[i+1:]...)
// Sortir de la boucle après avoir trouvé et supprimé l'élément
break
}
}
env.agentCount--
}
func (env *Environment) Do(a Action, c Coord) (err error) {
env.Lock()
defer env.Unlock()
......
......@@ -3,8 +3,6 @@ package simulation
import (
"fmt"
"log"
//"math/rand"
"sync"
"time"
)
......@@ -42,6 +40,7 @@ var carte [20][20]string = [20][20]string{
{"X", "X", "X", "X", "S", "S", "X", "X", "X", "X", "X", "X", "E", "E", "X", "X", "X", "X", "X", "X"},
}
var playground [20][20]string = [20][20]string{
{"_", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"},
{"_", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"},
......@@ -97,8 +96,15 @@ func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu
syncChan := make(chan int)
//ag := NewAgent(id, &simu.env, syncChan, time.Duration(time.Second), 0, true, Coord{0, 8 + i%2}, Coord{0, 8 + i%2}, &UsagerLambda{}, Coord{0, 8 + i%2}, Coord{12 - 4*(i%2), 18 - 15*(i%2)})
ag := NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{2, 8}, Coord{13, 15}, 2, 1)
//ag := NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{5, 8}, Coord{0, 0}, 2, 1)
//ag := NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{3, 4}, Coord{18, 12}, 2, 1)
ag := &Agent{}
if i%2==0{
ag = NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{18, 4}, Coord{0, 8}, 2, 1)
}else{
ag = NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{1, 8}, Coord{8, 5}, 1, 1)
}
//ag := NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{1, 17}, Coord{0, 0}, 2, 1)
// ajout de l'agent à la simulation
simu.agents = append(simu.agents, *ag)
......
package simulation
import (
"fmt"
"math/rand"
"time"
)
......@@ -17,7 +18,10 @@ func (ul *UsagerLambda) Percept(ag *Agent) {
}
func (ul *UsagerLambda) Deliberate(ag *Agent) {
if ag.stuck {
if ag.position == ag.destination && (ag.isOn[ag.position] == "W" || ag.isOn[ag.position] == "S") {
fmt.Println(ag.id, "disapear")
ag.decision = Disapear
} else if ag.stuck {
ag.decision = Wait
} else {
ag.decision = Move
......@@ -30,6 +34,8 @@ func (ul *UsagerLambda) Act(ag *Agent) {
} else if ag.decision == Wait {
n := rand.Intn(2) // temps d'attente aléatoire
time.Sleep(time.Duration(n) * time.Second)
} else if ag.decision == Disapear {
RemoveAgent(&ag.env.station, ag)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment