diff --git a/agent.go b/agent.go index 0ab76b86977f304c6c8a28760fdc2f2f404cb39f..9feb3c09fad78bb48ff62f5d69c893fb57d269a6 100644 --- a/agent.go +++ b/agent.go @@ -1,10 +1,8 @@ package simulation import ( - //"container/heap" - "fmt" + //"fmt" "log" - "math" "math/rand" "time" ) @@ -23,7 +21,7 @@ type AgentID string type Agent struct { id AgentID - vitesse int + vitesse time.Duration force int politesse bool coordHautOccupation Coord @@ -39,7 +37,6 @@ type Agent struct { } type Behavior interface { - // TODO: Comment faire pour ne pas passer l'agent en param ? C'est possible ? Percept(*Agent, *Environment) Deliberate(*Agent) Act(*Agent, *Environment) @@ -48,32 +45,7 @@ type Behavior interface { type UsagerLambda struct{} func (ul *UsagerLambda) Percept(ag *Agent, env *Environment) { - // TODO: Essayer un nouveau chemin quand l'agent est bloqué - - // Perception des éléments autour de l'agent pour déterminer si bloqué - s := 0 // nombre de cases indisponibles autour de l'agent - for i := 0; i < 3; i++ { - for j := 0; j < 3; j++ { - ord := (ag.coordBasOccupation[0] - 1) + i - abs := (ag.coordBasOccupation[1] - 1) + j - - if(ord!=ag.coordBasOccupation[0] && abs !=ag.coordBasOccupation[1]){ - if ord < 0 || abs < 0 || ord > 19 || abs > 19 { - s++ - } else if env.station[ord][abs] == "X" || env.station[ord][abs] == "Q" || env.station[ord][abs] == "A" { - s++ - } - } - } - } - // Si pas de case disponible autour de lui, il est bloqué - // (ag.env.station[ag.departure[0]][ag.departure[1]] == "A" && ag.coordBasOccupation[0] == ag.departure[0] && ag.coordBasOccupation[1] == ag.departure[1]) - if s == 8 { - ag.stuck = true - fmt.Println(ag.id, ag.stuck, ag.coordBasOccupation, s) - } else { - ag.stuck = false - } + ag.stuck = ag.isStuck() } func (ul *UsagerLambda) Deliberate(ag *Agent) { @@ -87,21 +59,7 @@ func (ul *UsagerLambda) Deliberate(ag *Agent) { func (ul *UsagerLambda) Act(ag *Agent, env *Environment) { if ag.decision == Move { - - start := ag.coordBasOccupation - end := ag.destination - _, path := findClosestPointBFS(ag.env.station, start, end) - - if len(path) > 0 && env.station[path[0][0]][path[0][1]] != "A" { // TODO: Pas ouf les conditions je trouve - ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = ag.isOn - ag.isOn = ag.env.station[path[0][0]][path[0][1]] - ag.coordBasOccupation[0] = path[0][0] - ag.coordBasOccupation[1] = path[0][1] - ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = "A" - } - //vitesseInSeconds := int(ag.vitesse) - //sleepDuration := time.Duration(vitesseInSeconds) * time.Second - time.Sleep(200 * time.Millisecond) + ag.MoveAgent() } if ag.decision == Wait { n := rand.Intn(2) // temps d'attente aléatoire @@ -110,7 +68,7 @@ func (ul *UsagerLambda) Act(ag *Agent, env *Environment) { } -func NewAgent(id string, env *Environment, syncChan chan int, vitesse int, force int, politesse bool, UpCoord Coord, DownCoord Coord, behavior Behavior, departure, destination Coord) *Agent { +func NewAgent(id string, env *Environment, syncChan chan int, vitesse time.Duration, force int, politesse bool, UpCoord Coord, DownCoord Coord, behavior Behavior, departure, destination Coord) *Agent { return &Agent{AgentID(id), vitesse, force, politesse, UpCoord, DownCoord, departure, destination, behavior, env, syncChan, Noop, env.station[UpCoord[0]][UpCoord[1]], false} } @@ -134,238 +92,73 @@ func (ag *Agent) Start() { }() } -func (ag *Agent) Percept(env *Environment) { - //ag.rect = env.Rect() -} - -func (ag *Agent) Deliberate() { - if rand.Float64() < 0.1 { - ag.decision = Noop - } else { - ag.decision = Mark - } -} - func (ag *Agent) Act(env *Environment) { if ag.decision == Noop { env.Do(Noop, Coord{}) } } -// ================================================================================ - -/* - * Utilisation de l'algorithme BFS pour les déplacements. Beaucoup plus rapide que A* - * - * - */ -/* -func findPathBFS(matrix [20][20]string, start, end Coord) []Coord { - queue := []Coord{start} - visited := make(map[Coord]bool) - parents := make(map[Coord]Coord) - - for len(queue) > 0 { - current := queue[0] - queue = queue[1:] - - if current == end { - // Construire le chemin à partir des parents - path := []Coord{current} - for parent, ok := parents[current]; ok; parent, ok = parents[parent] { - path = append([]Coord{parent}, path...) - } - return path[1:] - } - - visited[current] = true - - neighbors := getNeighborsBFS(matrix, current) - for _, neighbor := range neighbors { - if !visited[neighbor] { - parents[neighbor] = current - queue = append(queue, neighbor) - } - } - } - - return nil // Aucun chemin trouvé +func IsMovementSafe(path []Node, env *Environment) bool { + // Détermine si le movement est faisable + return len(path) > 0 && (env.station[path[0].row][path[0].col] == "B" || env.station[path[0].row][path[0].col] == "_") } -*/ - -func distance(coord1, coord2 Coord) float64 { - dx := float64(coord1[0] - coord2[0]) - dy := float64(coord1[1] - coord2[1]) - return math.Sqrt(dx*dx + dy*dy) -} - -func findClosestPointBFS(matrix [20][20]string, start, end Coord) (Coord, []Coord) { - queue := []Coord{start} - visited := make(map[Coord]bool) - parents := make(map[Coord]Coord) - closestPoint := start // Initialisez avec le point de départ - closestDistance := distance(start, end) - foundPath := false - - for len(queue) > 0 { - current := queue[0] - queue = queue[1:] - - // Mettez à jour le point le plus proche si le point actuel est plus proche - currentDistance := distance(current, end) - if currentDistance < closestDistance { - closestPoint = current - closestDistance = currentDistance - } - - if current == end { - // Construire le chemin du point le plus proche à la destination - path := []Coord{closestPoint} - for parent, ok := parents[closestPoint]; ok; parent, ok = parents[parent] { - path = append([]Coord{parent}, path...) - } - return closestPoint, path[1:] - } - - visited[current] = true - - neighbors := getNeighborsBFS(matrix, current, end) - for _, neighbor := range neighbors { - if !visited[neighbor] { - parents[neighbor] = current - queue = append(queue, neighbor) - } - } - foundPath = true - } - - if foundPath { - // Retourner le chemin le plus proche même si la destination n'a pas été atteinte - path := []Coord{closestPoint} - for parent, ok := parents[closestPoint]; ok; parent, ok = parents[parent] { - path = append([]Coord{parent}, path...) - } - return closestPoint, path[1:] - } - - return closestPoint, nil // Aucun chemin trouvé +func IsAgentBlocking(path []Node, env *Environment) bool { + // Détermine si un agent se trouve sur la case à visiter + return len(path) > 0 && env.station[path[0].row][path[0].col] == "A" } -func getNeighborsBFS(matrix [20][20]string, current Coord, end Coord) []Coord { - neighbors := make([]Coord, 0) - - // Déplacements possibles : haut, bas, gauche, droite - possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} - - for _, move := range possibleMoves { - newRow, newCol := current[0]+move[0], current[1]+move[1] +func (ag *Agent) isStuck() bool { + // Perception des éléments autour de l'agent pour déterminer si bloqué + s := 0 // nombre de cases indisponibles autour de l'agent + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + ord := (ag.coordBasOccupation[0] - 1) + i + abs := (ag.coordBasOccupation[1] - 1) + j - // Vérifier si la nouvelle position est valide et non visitée - if newRow >= 0 && newRow < len(matrix) && newCol >= 0 && newCol < len(matrix[0]) && (matrix[newRow][newCol] != "Q" && matrix[newRow][newCol] != "X") { - if !(matrix[newRow][newCol] == "A" && newRow != end[0] && newCol != end[1]) { - // Si la case du chemin ne comporte pas d'agent et que ce n'est pas la case d'arrivée, on peut l'ajouter - neighbors = append(neighbors, Coord{newRow, newCol}) + if ord != ag.coordBasOccupation[0] && abs != ag.coordBasOccupation[1] { + if ord < 0 || abs < 0 || ord > 19 || abs > 19 { + s++ + } else if ag.env.station[ord][abs] == "X" || ag.env.station[ord][abs] == "Q" || ag.env.station[ord][abs] == "A" { + s++ + } } - } } - - return neighbors + // Si aucune case disponible autour de lui, il est bloqué + return s == 8 } -/* -type Node struct { - row, col, cost, heuristic int -} +func (ag *Agent) MoveAgent() { -type PriorityQueue []*Node + // ============ Initialisation des noeuds de départ ====================== + start := Node{ag.coordBasOccupation[0], ag.coordBasOccupation[1], 0, 0} + end := Node{ag.destination[0], ag.destination[1], 0, 0} -func (pq PriorityQueue) Len() int { return len(pq) } + // ================== 1ère tentative de calcul du chemin ======================= + path := findPath(ag.env.station, start, end, Node{}) -func (pq PriorityQueue) Less(i, j int) bool { - return (pq[i].cost + pq[i].heuristic) < (pq[j].cost + pq[j].heuristic) -} - -func (pq PriorityQueue) Swap(i, j int) { - pq[i], pq[j] = pq[j], pq[i] -} + // ================== Etude de faisabilité ======================= + if IsMovementSafe(path, ag.env) { + ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = ag.isOn + ag.isOn = ag.env.station[path[0].row][path[0].col] + ag.coordBasOccupation[0] = path[0].row + ag.coordBasOccupation[1] = path[0].col + ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = "A" -func (pq *PriorityQueue) Push(x interface{}) { - item := x.(*Node) - *pq = append(*pq, item) -} - -func (pq *PriorityQueue) Pop() interface{} { - old := *pq - n := len(old) - item := old[n-1] - *pq = old[0 : n-1] - return item -} + } else if IsAgentBlocking(path, ag.env) { + // Si un agent bloque notre déplacement, on attend un temps aléatoire, et reconstruit un chemin en évitant la position + time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) + path := findPath(ag.env.station, start, end, path[0]) -func findPath(matrix [20][20]string, start, end Node) []Node { - pq := make(PriorityQueue, 0) - heap.Init(&pq) - - heap.Push(&pq, &start) - visited := make(map[Node]bool) - parents := make(map[Node]Node) - - for pq.Len() > 0 { - current := heap.Pop(&pq).(*Node) - - if current.row == end.row && current.col == end.col { - // Construire le chemin à partir des parents - path := []Node{*current} - for parent, ok := parents[*current]; ok; parent, ok = parents[parent] { - path = append([]Node{parent}, path...) - } - - return path[1:] - } - - visited[*current] = true - - neighbors := getNeighbors(matrix, *current) - for _, neighbor := range neighbors { - if !visited[*neighbor] { - parents[*neighbor] = *current - heap.Push(&pq, neighbor) - } - } - } - - return nil // Aucun chemin trouvé -} - -func getNeighbors(matrix [20][20]string, current Node) []*Node { - neighbors := make([]*Node, 0) - - // Déplacements possibles : haut, bas, gauche, droite - possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} - - for _, move := range possibleMoves { - newRow, newCol := current.row+move[0], current.col+move[1] - - // Vérifier si la nouvelle position est valide et non visitée - if newRow >= 0 && newRow < len(matrix) && newCol >= 0 && newCol < len(matrix[0]) && (matrix[newRow][newCol] == "_" || matrix[newRow][newCol] == "B") { - neighbors = append(neighbors, &Node{newRow, newCol, current.cost + 1, heuristic(newRow, newCol, current, current)}) + if len(path) > 0 && (ag.env.station[path[0].row][path[0].col] == "B" || ag.env.station[path[0].row][path[0].col] == "_") { + ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = ag.isOn + ag.isOn = ag.env.station[path[0].row][path[0].col] + ag.coordBasOccupation[0] = path[0].row + ag.coordBasOccupation[1] = path[0].col + ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = "A" } } - - return neighbors -} - -func heuristic(row, col int, current, end Node) int { - // Heuristique simple : distance de Manhattan - return abs(row-end.row) + abs(col-end.col) -} - -func abs(x int) int { - if x < 0 { - return -x - } - return x + // ============ Prise en compte de la vitesse de déplacement ====================== + time.Sleep(ag.vitesse) } -*/ diff --git a/astar.go b/astar.go new file mode 100644 index 0000000000000000000000000000000000000000..459b998a44992783fada047d3a5c0046809e0d2b --- /dev/null +++ b/astar.go @@ -0,0 +1,126 @@ +package simulation + +import ( + "container/heap" +) + +/* + * Utilisation de l'algorithme A* pour les déplacements + * + * + */ +type Node struct { + row, col, cost, heuristic int +} + +type PriorityQueue []*Node + +func (pq PriorityQueue) Len() int { return len(pq) } + +func (pq PriorityQueue) Less(i, j int) bool { + return (pq[i].cost + pq[i].heuristic) < (pq[j].cost + pq[j].heuristic) +} + +func (pq PriorityQueue) Swap(i, j int) { + pq[i], pq[j] = pq[j], pq[i] +} + +func (pq *PriorityQueue) Push(x interface{}) { + item := x.(*Node) + *pq = append(*pq, item) +} + +func (pq *PriorityQueue) Pop() interface{} { + old := *pq + n := len(old) + item := old[n-1] + *pq = old[0 : n-1] + return item +} + +func findPath(matrix [20][20]string, start, end Node, forbidenCell Node) []Node { + pq := make(PriorityQueue, 0) + heap.Init(&pq) + + heap.Push(&pq, &start) + visited := make(map[Node]bool) + parents := make(map[Node]Node) + + closestPoint := start // Initialisation avec le point de départ + closestDistance := heuristic(start.row, start.col, end) + + foundPath := false + + for pq.Len() > 0 { + 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) + if currentDistance < closestDistance { + closestPoint = *current + closestDistance = currentDistance + } + + if current.row == end.row && current.col == end.col { + // Construire le chemin à partir des parents + path := []Node{closestPoint} + for parent, ok := parents[closestPoint]; ok; parent, ok = parents[parent] { + path = append([]Node{parent}, path...) + } + + return path[1:] + } + + visited[*current] = true + + neighbors := getNeighbors(matrix, *current, end, forbidenCell) + for _, neighbor := range neighbors { + if !visited[*neighbor] { + parents[*neighbor] = *current + heap.Push(&pq, neighbor) + } + } + foundPath = true + } + + if foundPath { + // Retourner le chemin le plus proche même si la destination n'a pas été atteinte + path := []Node{closestPoint} + for parent, ok := parents[closestPoint]; ok; parent, ok = parents[parent] { + path = append([]Node{parent}, path...) + } + return path[1:] + } + + return nil // Aucun chemin trouvé +} + +func getNeighbors(matrix [20][20]string, current, end Node, forbidenCell Node) []*Node { + neighbors := make([]*Node, 0) + + // Déplacements possibles : haut, bas, gauche, droite + possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} + + for _, move := range possibleMoves { + newRow, newCol := current.row+move[0], current.col+move[1] + + // Vérifier si la nouvelle position est valide + if (forbidenCell.row != newRow || forbidenCell.col != newCol) && newRow >= 0 && newRow < len(matrix) && newCol >= 0 && newCol < len(matrix[0]) && (matrix[newRow][newCol] != "Q" && matrix[newRow][newCol] != "X") { + neighbors = append(neighbors, &Node{newRow, newCol, current.cost + 1, heuristic(newRow, newCol, end)}) + } + } + + return neighbors +} + +func heuristic(row, col int, end Node) int { + // Heuristique simple : distance de Manhattan + return abs(row-end.row) + abs(col-end.col) +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} diff --git a/cmd/simu/main.go b/cmd/simu/main.go index c432c7df3e08caff206939acd0ddb327044da181..93deaced0df1ec6e0bb057368e113d3b181228b7 100644 --- a/cmd/simu/main.go +++ b/cmd/simu/main.go @@ -6,7 +6,7 @@ import ( ) func main() { - s := simulation.NewSimulation(20, -1, 600*time.Second) + s := simulation.NewSimulation(40, -1, 600*time.Second) //go simulation.StartAPI(s) s.Run() } diff --git a/env.go b/env.go index 05fe4b1a5565ed980838b285385a1891b0cbdbba..107b44e75fd80e8d4fb2d8c8880ba34a6b58b5c6 100644 --- a/env.go +++ b/env.go @@ -5,16 +5,16 @@ import ( "sync" ) - type Environment struct { sync.RWMutex ags []Agent agentCount int - station [20][20]string + station [20][20]string + agentsChan map[AgentID]chan AgentID } -func NewEnvironment(ags []Agent, carte [20][20]string) (env *Environment) { - return &Environment{ags: ags, agentCount: len(ags),station : carte} +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} } func (env *Environment) AddAgent(agt Agent) { @@ -45,7 +45,7 @@ func (env *Environment) PI() float64 { env.RLock() defer env.RUnlock() - return 4 + return 4 } func (env *Environment) Rect() Coord { diff --git a/simu.go b/simu.go index d3b8cac1857c2a8da55871f46b73cd6c79d43cbf..5ca0e61615ecb179bd81fd3a534978191868ff5e 100644 --- a/simu.go +++ b/simu.go @@ -3,6 +3,7 @@ package simulation import ( "fmt" "log" + //"math/rand" "sync" "time" @@ -15,16 +16,16 @@ import ( * S : Sortie * W : Entrée et Sortie * Q : Voie - * _ : Couloir, zon + * _ : Couloir, case libre * B: Bridge/Pont, zone accessible */ var carte [20][20]string = [20][20]string{ {"X", "X", "X", "X", "X", "X", "X", "X", "W", "W", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, - {"X", "X", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, - {"X", "X", "_", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, - {"X", "X", "_", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X"}, + {"X", "X", "_", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "_", "X", "X"}, + {"X", "X", "_", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "_", "X", "X"}, {"X", "X", "_", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "_", "_", "_", "X"}, {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "_"}, @@ -80,7 +81,9 @@ func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu simu.maxStep = maxStep simu.maxDuration = maxDuration - simu.env = *NewEnvironment([]Agent{}, carte) + // Communication entre agents + mapChan := make(map[AgentID]chan AgentID) + simu.env = *NewEnvironment([]Agent{}, carte, mapChan) //simu.env = *NewEnvironment([]Agent{}, playground) // création des agents et des channels @@ -88,7 +91,7 @@ func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu // création de l'agent id := fmt.Sprintf("Agent #%d", i) syncChan := make(chan int) - ag := NewAgent(id, &simu.env, syncChan, 1, 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, time.Duration(200*time.Millisecond), 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, 1, 0, true, Coord{4,10}, Coord{4,10}, &UsagerLambda{}, Coord{4,10}, Coord{0, 0}) @@ -100,6 +103,9 @@ func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu // ajout de l'agent à l'environnement ag.env.AddAgent(*ag) + + // ajout + simu.env.agentsChan[ag.id] = make(chan AgentID) } return simu @@ -114,7 +120,7 @@ func (simu *Simulation) Run() { go simu.Print() // Démarrage des agents - + var wg sync.WaitGroup for _, agt := range simu.agents { wg.Add(1) @@ -123,7 +129,7 @@ func (simu *Simulation) Run() { agent.Start() }(agt) } - + // On sauvegarde la date du début de la simulation simu.start = time.Now() @@ -153,7 +159,7 @@ func (simu *Simulation) Print() { fmt.Println(simu.env.station[i]) } //time.Sleep(time.Second / 4) // 60 fps ! - time.Sleep(time.Second) // 1 fps ! + time.Sleep(time.Second) // 1 fps ! //fmt.Print("\033[H\033[2J") // effacement du terminal } }