diff --git a/cmd/simu/main.go b/cmd/simu/main.go index f963e03be40af2a537924923ad7f76bad953b94a..c298cf2bfb099acbc2cec11fb165e1ad79a66d61 100644 --- a/cmd/simu/main.go +++ b/cmd/simu/main.go @@ -6,7 +6,7 @@ import ( ) func main() { - s := simulation.NewSimulation(10, -1, 600*time.Second) + s := simulation.NewSimulation(2, -1, 600*time.Second) //go simulation.StartAPI(s) s.Run() } diff --git a/cmd/test/main.go b/cmd/test/main.go deleted file mode 100644 index f5e5e2d9ece35c061fc43785bb778f2d36bb978f..0000000000000000000000000000000000000000 --- a/cmd/test/main.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "fmt" - "regexp" -) - -func main() { - faceCase := "Agent1000" // Remplacez ceci par votre variable - - // Créer l'expression régulière - regexPattern := `^Agent\d+$` // \d+ correspond à un ou plusieurs chiffres - matched, err := regexp.MatchString(regexPattern, faceCase) - - if err != nil { - fmt.Println("Erreur lors de l'analyse de la regex :", err) - return - } - - // Vérifiez si la chaîne ne correspond pas au motif - if !matched { - fmt.Println("La chaîne ne correspond pas au motif 'Agentx'") - } else { - fmt.Println("La chaîne correspond au motif 'Agentx'") - } -} diff --git a/internal/algorithms/astar.go b/internal/algorithms/astar.go index d159d9db222d884b42ada32d48bc8aa92ba299e0..c7f400e4ea9a029e0443849a8ea729757d34c0e3 100644 --- a/internal/algorithms/astar.go +++ b/internal/algorithms/astar.go @@ -64,7 +64,7 @@ func (pq *PriorityQueue) Pop() interface{} { type ZoneID int type Coord [2]int -func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node, orientation bool, timeout time.Duration) []Node { +func FindPath(matrix [50][50]string, start, end Node, forbidenCell Node, orientation bool, timeout time.Duration) []Node { // Création d'un context avec timeout, pour limiter le calcul ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() @@ -77,7 +77,7 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node, orienta parents := make(map[Node]Node) closestPoint := start // Initialisation avec le point de départ - closestDistance := Heuristic(start.row, start.col, end) + closestDistance := Heuristic(matrix, start.row, start.col, end) foundPath := false @@ -92,7 +92,7 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node, orienta 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(matrix, current.row, current.col, end) if currentDistance < closestDistance { closestPoint = *current closestDistance = currentDistance @@ -132,7 +132,7 @@ func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node, orienta return nil // Aucun chemin trouvé } -func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node, orientation bool) []*Node { +func getNeighbors(matrix [50][50]string, current, end Node, forbiddenCell Node, orientation bool) []*Node { neighbors := make([]*Node, 0) // Déplacements possibles: up, down, left, right @@ -149,7 +149,7 @@ func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node, row: newRow, col: newCol, cost: current.cost + 1, - heuristic: Heuristic(newRow, newCol, end), + heuristic: Heuristic(matrix, newRow, newCol, end), width: current.width, height: current.height, orientation: current.orientation, @@ -163,7 +163,7 @@ func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node, row: newRow, col: newCol, cost: current.cost + 1, - heuristic: Heuristic(newRow, newCol, end), + heuristic: Heuristic(matrix, newRow, newCol, end), width: current.width, height: current.height, orientation: current.orientation, @@ -176,12 +176,16 @@ func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node, return neighbors } -func Heuristic(row, col int, end Node) int { +func Heuristic(matrix [50][50]string, row, col int, end Node) int { // Heuristique simple : distance de Manhattan // 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) - return Abs(row-end.row) + Abs(col-end.col) + rand.Intn(10) + malus := 0 + if len(matrix[row][col]) > 1 { + malus += 10 + } + return Abs(row-end.row) + Abs(col-end.col) + rand.Intn(10) + malus } func Abs(x int) int { @@ -191,7 +195,7 @@ func Abs(x int) int { return x } -func isValidMove(matrix [20][20]string, current Node, forbiddenCell Node, newRow, newCol int, orientation bool) bool { +func isValidMove(matrix [50][50]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 @@ -199,10 +203,11 @@ func isValidMove(matrix [20][20]string, current Node, forbiddenCell Node, newRow // Check if the new position overlaps with forbidden cells or obstacles if forbiddenCell.row == newRow && forbiddenCell.col == newCol { - return false + //return false + current.heuristic = current.heuristic + 100 } // Check if the absolute coordinates overlap with obstacles in the matrix - if matrix[newRow][newCol] == "Q" || matrix[newRow][newCol] == "X" { + if matrix[newRow][newCol] == "Q" || matrix[newRow][newCol] == "X" || matrix[newRow][newCol] == "M" { return false } @@ -223,11 +228,12 @@ func isValidMove(matrix [20][20]string, current Node, forbiddenCell Node, newRow // Check if the absolute coordinates overlap with forbidden cells or obstacles if forbiddenCell.row == absRow && forbiddenCell.col == absCol { - return false + //return false + current.heuristic = current.heuristic + 100 } // Check if the absolute coordinates overlap with obstacles in the matrix - if matrix[absRow][absCol] == "Q" || matrix[absRow][absCol] == "X" { + if matrix[absRow][absCol] == "Q" || matrix[absRow][absCol] == "X" || matrix[absRow][absCol] == "M" { return false } } @@ -286,3 +292,22 @@ func calculateBounds(row, col, width, height, orientation int) (infRow, supRow, } return borneInfRow, borneSupRow, borneInfCol, borneSupCol } + +func FindNearestExit(matrix [50][50]string, row, col int) (dest_row, dest_col int) { + // Recherche de la sortie la plus proche + min := 1000000 + n := len(matrix[0]) + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + if matrix[i][j] == "S" || matrix[i][j] == "W" { + dist := Abs(row-i) + Abs(col-j) + if dist < min { + min = dist + dest_row = i + dest_col = j + } + } + } + } + return dest_row, dest_col +} diff --git a/internal/simulation/agent.go b/internal/simulation/agent.go index 09a45169216bc3ef3a216a4d1b8dc217f0d22f63..a72707b8b66816e3f4e49df351082e8571d85e97 100644 --- a/internal/simulation/agent.go +++ b/internal/simulation/agent.go @@ -2,14 +2,10 @@ package simulation /* * Classe et méthodes principales de la structure Agent - * à faire : - * // 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" + "fmt" "log" //"fmt" @@ -17,19 +13,18 @@ import ( "math/rand" alg "metrosim/internal/algorithms" "time" + "sort" ) type Action int64 const ( - Noop = iota - Mark - Wait - Move - Disappear - Expel // virer l'agent - Stop // arreter l'agent - GiveInfos + Noop = iota + Wait // Attente + Move // Déplacement de l'agent + Disappear // Disparition de l'agent dans la simulatiin + Expel // virer l'agent + Stop // arreter l'agent ) type Coord [2]int @@ -51,14 +46,15 @@ type Agent struct { stuck bool width int height int - orientation int //0 : vers le haut, 1 : vers la droite, 2 : vers le bas, 3 : vers la gauche (sens de deuxieme case occupé par l'agent) + orientation int //0 : vers le haut, 1 : vers la droite, 2 : vers le bas, 3 : vers la gauche (sens de construction de l'agent) path []alg.Node request *Request direction int //0 : vers le haut, 1 : vers la droite, 2 : vers le bas, 3 : vers la gauche (sens de son deplacement) + } type Request struct { - demandeur chan Request + demandeur chan Request //channel de l'émetteur de la demande decision int } @@ -90,6 +86,9 @@ func (ag *Agent) Start() { if (ag.id[0] == 'C') { ag.behavior.(*Controleur).startTimer() } + if (ag.id[0] == 'M') { + ag.behavior.(*MobiliteReduite).setUpPath(ag) + } go func() { var step int @@ -99,6 +98,7 @@ func (ag *Agent) Start() { ag.behavior.Deliberate(ag) ag.behavior.Act(ag) ag.syncChan <- step + //fmt.Println(ag.id, ag.path) if ag.decision == Disappear { ag.env.RemoveAgent(*ag) return @@ -117,6 +117,7 @@ func IsMovementSafe(path []alg.Node, agt *Agent, env *Environment) (bool, int) { // Détermine si le movement est faisable if len(path) <= 0 { + fmt.Println("[isMovementSafe] path vide : ", agt.id) return false, agt.orientation } // Calcul des bornes de position de l'agent avant mouvement @@ -124,6 +125,7 @@ func IsMovementSafe(path []alg.Node, agt *Agent, env *Environment) (bool, int) { // Si pas encore sur la map, mais agent déja sur la position, on ne peut pas encore apparaître if len(agt.isOn) == 0 && len(env.station[agt.path[0].Row()][agt.path[0].Col()]) > 1 { + //fmt.Println("[isMovementSafe] case occupée : ",agt.id) return false, agt.orientation } // Simulation du déplacement @@ -139,8 +141,9 @@ func IsMovementSafe(path []alg.Node, agt *Agent, env *Environment) (bool, int) { if !(borneInfCol < 0 || borneInfRow < 0 || borneSupRow > len(env.station[0]) || borneSupCol > len(env.station[1])) { 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") { + 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" && env.station[i][j] != "O") { // Si on n'est pas sur une case atteignable, en dehors de la zone qu'occupe l'agent avant déplacement, on est bloqué + //fmt.Println("[IsMovementSafe]case inaccessible :",agt.id) safe = false } } @@ -152,7 +155,6 @@ func IsMovementSafe(path []alg.Node, agt *Agent, env *Environment) (bool, int) { } return false, agt.orientation - } func IsAgentBlocking(path []alg.Node, agt *Agent, env *Environment) bool { @@ -220,27 +222,88 @@ func (ag *Agent) isStuck() bool { return not_acc == count } +func (ag *Agent) WhichAgent() string { + if ag.direction == 0 { // vers le haut + return ag.env.station[ag.position[0]-1][ag.position[1]] + } else if ag.direction == 1 { // vers la droite + return ag.env.station[ag.position[0]][ag.position[1]+1] + } else if ag.direction == 2 { // vers le bas + return ag.env.station[ag.position[0]+1][ag.position[1]] + } else { // vers la gauche + return ag.env.station[ag.position[0]][ag.position[1]-1] + } +} + +func (env *Environment) FindAgentByID(agtId AgentID) *Agent { + for i := range env.ags { + if env.ags[i].id == agtId { + return &env.ags[i] + } + } + return nil +} + func (ag *Agent) MoveAgent() { //fmt.Println("[Agent, MoveAgent] destination ", ag.destination) // ================== Tentative de calcul du chemin ======================= - if len(ag.path) == 0 { + if len(ag.path) == 0 || ag.isGoingToExitPath() { 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, 2*time.Second) - ag.path = path + ag.path = alg.FindPath(ag.env.station, start, end, *alg.NewNode(-1, -1, 0, 0, 0, 0), false, 2*time.Second) + } // ================== Etude de faisabilité ======================= 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, ag.path[0], true, 2*time.Second) - ag.path = path + if ag.politesse { + 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, *alg.NewNode(-1, -1, 0, 0, 0, 0), false, 2*time.Second) + path := alg.FindPath(ag.env.station, start, end, ag.path[0], false, 2*time.Second) + ag.path = path + return + } else { + //Si individu impoli, demande à l'agent devant de bouger + //On récupère le id de la personne devant + blockingAgentID := AgentID(ag.WhichAgent()) + //blockingAgent := ag.env.FindAgentByID(blockingAgentID) + var reqToBlockingAgent *Request + //var reqToImpoliteAgent *Request + i := 0 + accept := false + for !accept && i < 3 { + //Demande à l'agent qui bloque de se pousser (réitère trois fois s'il lui dit pas possible) + i += 1 + reqToBlockingAgent = NewRequest(ag.env.agentsChan[ag.id], 3) //Création "Hello, je suis ag.id, move." + ag.env.agentsChan[blockingAgentID] <- *reqToBlockingAgent //Envoi requête + + /* + 1. Faire le moment ou blocking agent recoit qqchose sur son canal + 2. + + + */ + /* + //BlockingAgent cherche si autour de lui c'est vide + possible, or := IsMovementSafe(blockingAgent.path, blockingAgent, blockingAgent.env) + + if !possible { + reqToImpoliteAgent = NewRequest(ag.id, 0) + ag.env.agentsChan[ag.id] <- *reqToImpoliteAgent + } else { + //Bouge sur la case possible + accept = true + coordBlockingAgent := blockingAgent.position + //Gérer le déplacement de Ag et de BlockingAgent + déplacement en fonction de la force !!!!!!!!!!!!!!!!!!!!!!!!!!!!! + } + */ + } + } } - // ================== Déplacement si aucun problème ======================= + + // ================== Déplacement si aucun problème ou si blockingAgent se pousse ======================= safe, or := IsMovementSafe(ag.path, ag, ag.env) if safe { if len(ag.isOn) > 0 { @@ -275,7 +338,7 @@ func (ag *Agent) generatePathExtremities() (alg.Node, alg.Node) { return start, end } -func RemoveAgent(matrix *[20][20]string, agt *Agent) { +func RemoveAgent(matrix *[50][50]string, agt *Agent) { // Supprime l'agent de la matrice // Calcul des bornes de position de l'agent @@ -289,7 +352,7 @@ func RemoveAgent(matrix *[20][20]string, agt *Agent) { } } -func writeAgent(matrix *[20][20]string, agt *Agent) { +func writeAgent(matrix *[50][50]string, agt *Agent) { // Ecris l'agent dans la matrice // Calcul des bornes de position de l'agent @@ -303,7 +366,7 @@ func writeAgent(matrix *[20][20]string, agt *Agent) { } -func saveCells(matrix *[20][20]string, savedCells map[Coord]string, position Coord, width, height, orientation int) { +func saveCells(matrix *[50][50]string, savedCells map[Coord]string, position Coord, width, height, orientation int) { // Enregistrement des valeurs des cellules de la matrice borneInfRow, borneSupRow, borneInfCol, borneSupCol := calculateBounds(position, width, height, orientation) @@ -384,20 +447,51 @@ func (ag *Agent) listenForRequests() { } } -func (ag *Agent) findNearestGate(gates [] Coord) (Coord) { - // Recherche de la porte la plus proche - nearest := Coord{0, 0} - min := 1000000 +func (ag *Agent) isGoingToExitPath() bool { + if len(ag.path) > 0 { + for _, metro := range ag.env.metros { + for gate_index, gate := range metro.way.gates { + if equalCoord(&ag.destination, &gate) { + // Si la destination est une porte de métro, on va essayer de libérer le chemin des agents sortants + exit_path := metro.way.pathsToExit[gate_index] + for _, cell := range exit_path { + if equalCoord(&Coord{cell.Row(), cell.Col()}, &ag.position) { + return true + } + } + } + } + } + } + return false + +} + +// Structure pour associer une Coord et sa distance par rapport au position d'un agent +type GateDistance struct { + Gate Coord + Distance int +} + +func (ag *Agent) findNearestGates(gates []Coord) []GateDistance { + var gateDistances []GateDistance + + // Calcul de la distance pour chaque porte for _, gate := range gates { dist := alg.Abs(ag.position[0]-gate[0]) + alg.Abs(ag.position[1]-gate[1]) - if dist < min { - min = dist - nearest = gate - } + gateDistances = append(gateDistances, GateDistance{Gate: gate, Distance: dist}) } - return nearest + + // Tri des Coords par distance + sort.Slice(gateDistances, func(i, j int) bool { + return gateDistances[i].Distance < gateDistances[j].Distance + }) + + return gateDistances } + + func (ag *Agent) findNearestExit() (Coord){ // Recherche de la sortie la plus proche nearest := Coord{0, 0} @@ -488,4 +582,20 @@ func verifyDirection( n int ,depart Coord, dimensionCarte int) bool{ } } return false -} \ No newline at end of file +} + + +func (ag *Agent) bestGate(gates[] Coord) (Coord) { + mapNb := make(map[Coord]int) + for _, gate := range gates { + nb :=ag.env.getNbAgentsAround(gate) + mapNb[gate] = nb + } + + listDist := ag.findNearestGates(gates) //Type []GateDistance + + for _, couple := range listDist { + mapNb[couple.Gate] = mapNb[couple.Gate] / couple.Distance // il faut trouver le gate ayant une valeur le plus proche à 1 + } + return mapNb +} diff --git a/internal/simulation/controleur.go b/internal/simulation/controleur.go index 49143c9f40c890d3b6a30e32ce17769f420365aa..4935d023dcacbb4bb6f45d7e10a8615d08896096 100644 --- a/internal/simulation/controleur.go +++ b/internal/simulation/controleur.go @@ -53,15 +53,15 @@ func (c *Controleur) Deliberate(ag *Agent) { matchedFraud, err2 := regexp.MatchString(regexFraudeur, c.faceCase) //fmt.Println("faceCase : ", c.faceCase) //fmt.Println("matchedAgt : ", matchedAgt) - + if err1 != nil || err2 != nil { fmt.Println("Erreur lors de l'analyse de la regex :", err1, err2) return } else { - if matchedAgt && ag.env.controlledAgents[AgentID(c.faceCase)] == false { // si l'agent devant le controleur est un agent et qu'il n'a pas encore été controlé + if matchedAgt && ag.env.controlledAgents[AgentID(c.faceCase)] == false { // si l'agent devant le controleur est un agent et qu'il n'a pas encore été controlé //fmt.Println("L'agent ", c.face, " a été détecté par le controleur") ag.decision = Stop // arreter l'agent devant lui - } else if matchedFraud && !ag.env.controlledAgents[AgentID(c.faceCase)]{ + } else if matchedFraud && !ag.env.controlledAgents[AgentID(c.faceCase)] { ag.decision = Expel // virer l'agent devant lui //sinon comportement par défaut (comportement de l'usager lambda) }else if ag.position == ag.destination && (ag.isOn[ag.position] == "W" || ag.isOn[ag.position] == "S") { // si l'agent est arrivé à sa destination et qu'il est sur une sortie diff --git a/internal/simulation/env.go b/internal/simulation/env.go index cf3aaf76417a44cb9ccc1a7a8b1b5927f5b394b0..ab0e46f9fec5f684af1d7ebb08fb4f05efba52a6 100644 --- a/internal/simulation/env.go +++ b/internal/simulation/env.go @@ -7,43 +7,52 @@ import ( type Environment struct { sync.RWMutex - ags []Agent - agentCount int - station [20][20]string - agentsChan map[AgentID]chan Request + ags []Agent + agentCount int + station [50][50]string + agentsChan map[AgentID]chan Request controlledAgents map[AgentID]bool - metros []Metro // Liste des métros de la station , utilisé pour le choix du métro par l'agent à mobilité réduite (A voir si on peut trouver une autre manière de faire) - // zones map[Coord]ZoneID // Zones de la station - // panneaux map[ZoneID][]alg.Node // Les panneaux de la station, permettant d'aller vers la zone + newAgentChan chan Agent + metros []Metro } type ZoneID int -func NewEnvironment(ags []Agent, carte [20][20]string, agentsCh map[AgentID]chan Request) (env *Environment) { - mapControlle := make(map[AgentID]bool) + +func NewEnvironment(ags []Agent, carte [50][50]string, newAgtCh chan Agent, agtCount int) (env *Environment) { + agentsCh := make(map[AgentID]chan Request) + mapControlled := make(map[AgentID]bool) for _, ag := range ags { - mapControlle[ag.id] = false + mapControlled[ag.id] = false + } - return &Environment{ags: ags, agentCount: len(ags), station: carte, agentsChan: agentsCh, controlledAgents: mapControlle} + return &Environment{ags: ags, agentCount: agtCount, station: carte, agentsChan: agentsCh, controlledAgents: mapControlled, newAgentChan: newAgtCh} } func (env *Environment) AddAgent(agt Agent) { + env.Lock() + defer env.Unlock() env.ags = append(env.ags, agt) + env.controlledAgents[agt.id] = false + // ajout du channel de l'agent à l'environnement + env.agentsChan[agt.id] = make(chan Request, 5) env.agentCount++ + env.newAgentChan <- agt } func (env *Environment) RemoveAgent(agt Agent) { + // TODO:gérer la suppression dans simu 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:]...) - delete(env.agentsChan,agt.id) + delete(env.agentsChan, agt.id) // Sortir de la boucle après avoir trouvé et supprimé l'élément break } } - env.agentCount-- + //env.agentCount-- } func (env *Environment) Do(a Action, c Coord) (err error) { @@ -51,12 +60,12 @@ func (env *Environment) Do(a Action, c Coord) (err error) { defer env.Unlock() switch a { - case Mark: - if c[0] < 0 || c[0] > 1 || c[1] < 0 || c[1] > 1 { - return fmt.Errorf("bad coordinates (%f,%f)", c[0], c[1]) - } + // case Mark: + // if c[0] < 0 || c[0] > 1 || c[1] < 0 || c[1] > 1 { + // return fmt.Errorf("bad coordinates (%f,%f)", c[0], c[1]) + // } - return nil + // return nil case Noop: return nil @@ -85,15 +94,15 @@ func (env *Environment) verifyEmptyCase(c Coord) bool { } func existAgent(c string) bool { - return c != "X" && c != "E" && c != "S" && c != "W" && c!= "Q" && c!= "_" && c!= "B" + return c != "X" && c != "E" && c != "S" && c != "W" && c != "Q" && c != "_" && c != "B" } func calculDirection(depart Coord, arrive Coord) int { if depart[0] == arrive[0] { if depart[1] > arrive[1] { - return 3 //Gauche + return 3 //Gauche } else { - return 1 //droite + return 1 //droite } } else { if depart[0] > arrive[0] { @@ -102,4 +111,18 @@ func calculDirection(depart Coord, arrive Coord) int { return 2 //bas } } -} \ No newline at end of file +} + +func (env *Environment) getNbAgentsAround(pos Coord) int { + nb := 0 + for i := 0; i < 4; i++ { + for j := 0; j < 4; j++ { + c := env.station[pos[0]+i][pos[1]+j] + if existAgent(c) { + nb++ + } + } + } + return nb +} + diff --git a/internal/simulation/metro.go b/internal/simulation/metro.go index a31708b76854f47807cac797f2f2fbfc72c3365f..dbd6b8793110c895e9b5ccf8b1d746ca3cce4c89 100644 --- a/internal/simulation/metro.go +++ b/internal/simulation/metro.go @@ -7,21 +7,28 @@ import ( "time" ) +/* + * //TODO:Ajouter la capacité max + * // Apparition des agents sortant + */ + +var metro_speed int = 5 // Nombre de seconde de l'entrée en gare + type Metro struct { - frequency time.Duration - stopTime time.Duration + frequency time.Duration // fréquence d'arrivée du métro + stopTime time.Duration // temps d'arrêt du métro en gare + capacity int freeSpace int // nombre de cases disponibles dans le métro - env *Environment comChannel chan Request way *Way } -func NewMetro(freq time.Duration, stopT time.Duration, freeS int, env *Environment, way *Way) *Metro { +func NewMetro(freq time.Duration, stopT time.Duration, capacity, freeS int, way *Way) *Metro { return &Metro{ frequency: freq, stopTime: stopT, + capacity: capacity, freeSpace: freeS, - env: env, comChannel: make(chan Request), way: way, } @@ -34,11 +41,16 @@ func (metro *Metro) Start() { //var step int for { //step = <-metro.syncChan + if refTime.Add(metro.frequency).Sub(time.Now()) <= time.Duration(metro_speed)*time.Second { + metro.printMetro() + } if refTime.Add(metro.frequency).Before(time.Now()) { - go metro.pickUpUsers() + //metro.dropUsers() + metro.way.openGates() + metro.pickUpUsers() + metro.way.closeGates() + metro.removeMetro() metro.freeSpace = rand.Intn(10) - fmt.Println(metro.way.id, metro.freeSpace) - //go metro.dropUsers() refTime = time.Now() } //metro.syncChan <- step @@ -61,22 +73,146 @@ func (metro *Metro) pickUpUsers() { func (metro *Metro) pickUpGate(gate *Coord) { // Récupérer les usagers à une porte spécifique - gate_cell := metro.env.station[gate[0]][gate[1]] + gate_cell := metro.way.env.station[gate[0]][gate[1]] if len(gate_cell) > 1 { agent := metro.findAgent(AgentID(gate_cell)) if agent != nil && agent.width*agent.height <= metro.freeSpace && agent.destination == *gate { - metro.env.agentsChan[agent.id] <- *NewRequest(metro.comChannel, Disappear) - metro.freeSpace-- + metro.way.env.agentsChan[agent.id] <- *NewRequest(metro.comChannel, Disappear) + fmt.Println("[pickUpGate] requete envoyée à l'agent ", agent.id) + metro.freeSpace = metro.freeSpace - agent.width*agent.height } } } func (metro *Metro) findAgent(agent AgentID) *Agent { // Trouver l'adresse de l'agent - for _, agt := range metro.env.ags { + for _, agt := range metro.way.env.ags { if agt.id == agent { return &agt } } return nil } + +func (metro *Metro) dropUsers() { + nb := rand.Intn(metro.capacity - metro.freeSpace) // Nombre de cases à vider du métro + for nb > 0 { + gate_nb := rand.Intn(len(metro.way.gates)) // Sélection d'une porte aléatoirement + width := 1 //+ rand.Intn(2) + height := 1 //+ rand.Intn(2) + metro.freeSpace = metro.freeSpace + width*height + nb = nb - width*height + id := fmt.Sprintf("Agent%d", metro.way.env.agentCount) + path := metro.way.pathsToExit[gate_nb] + ag := NewAgent(id, metro.way.env, make(chan int), 200, 0, true, &UsagerLambda{}, metro.way.gates[gate_nb], metro.way.nearestExit[gate_nb], width, height) + ag.path = path + writeAgent(&ag.env.station,ag) + metro.way.env.AddAgent(*ag) + //log.Println(metro.way.id, nb, metro.way.env.agentCount) + //fmt.Println("agent leaving metro", ag.id, ag.departure, ag.destination, width, height) + //time.Sleep(500 * time.Millisecond) + } + +} + +func (metro *Metro) printMetro() { + + if metro.way.horizontal { + waiting_time := time.Duration((metro_speed * 1000) / (metro.way.downRightCoord[1] - metro.way.upLeftCoord[1])) + if metro.way.goToLeft { + for y := metro.way.downRightCoord[1]; y >= metro.way.upLeftCoord[1]; y-- { + for x := metro.way.upLeftCoord[0]; x <= metro.way.downRightCoord[0]; x++ { + if metro.way.env.station[x][y] == "Q" { + metro.way.env.station[x][y] = "M" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } else { + for y := metro.way.upLeftCoord[1]; y <= metro.way.downRightCoord[1]; y++ { + for x := metro.way.upLeftCoord[0]; x <= metro.way.downRightCoord[0]; x++ { + if metro.way.env.station[x][y] == "Q" { + metro.way.env.station[x][y] = "M" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } + + } else { + waiting_time := time.Duration((metro_speed * 1000) / (metro.way.downRightCoord[0] - metro.way.upLeftCoord[0])) + if metro.way.goToLeft { + // de bas en haut + for x := metro.way.downRightCoord[0]; x >= metro.way.upLeftCoord[0]; x-- { + for y := metro.way.upLeftCoord[1]; y <= metro.way.downRightCoord[1]; y++ { + if metro.way.env.station[x][y] == "Q" { + metro.way.env.station[x][y] = "M" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } else { + for x := metro.way.upLeftCoord[0]; x <= metro.way.downRightCoord[0]; x++ { + for y := metro.way.upLeftCoord[1]; y <= metro.way.downRightCoord[1]; y++ { + if metro.way.env.station[x][y] == "Q" { + metro.way.env.station[x][y] = "M" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } + + } + +} + +func (metro *Metro) removeMetro() { + + if metro.way.horizontal { + waiting_time := time.Duration((metro_speed * 1000) / (metro.way.downRightCoord[1] - metro.way.upLeftCoord[1])) + + if metro.way.goToLeft { + for y := metro.way.downRightCoord[1]; y >= metro.way.upLeftCoord[1]; y-- { + for x := metro.way.upLeftCoord[0]; x <= metro.way.downRightCoord[0]; x++ { + if metro.way.env.station[x][y] == "M" { + metro.way.env.station[x][y] = "Q" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } else { + for y := metro.way.upLeftCoord[1]; y <= metro.way.downRightCoord[1]; y++ { + for x := metro.way.upLeftCoord[0]; x <= metro.way.downRightCoord[0]; x++ { + if metro.way.env.station[x][y] == "M" { + metro.way.env.station[x][y] = "Q" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } + + } else { + waiting_time := time.Duration((metro_speed * 1000) / (metro.way.downRightCoord[0] - metro.way.upLeftCoord[0])) + if metro.way.goToLeft { + // de bas en haut + for x := metro.way.downRightCoord[0]; x >= metro.way.upLeftCoord[0]; x-- { + for y := metro.way.upLeftCoord[1]; y <= metro.way.downRightCoord[1]; y++ { + if metro.way.env.station[x][y] == "M" { + metro.way.env.station[x][y] = "Q" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } else { + for x := metro.way.upLeftCoord[0]; x <= metro.way.downRightCoord[0]; x++ { + for y := metro.way.upLeftCoord[1]; y <= metro.way.downRightCoord[1]; y++ { + if metro.way.env.station[x][y] == "M" { + metro.way.env.station[x][y] = "Q" + } + } + time.Sleep(waiting_time * time.Millisecond) + } + } + + } +} diff --git a/internal/simulation/mobiliteReduite.go b/internal/simulation/mobiliteReduite.go index d0a9753038ddb1981b33e375f720551118b39653..1a3b43473ba8c8bc596032fdd0bf3628c878b183 100644 --- a/internal/simulation/mobiliteReduite.go +++ b/internal/simulation/mobiliteReduite.go @@ -8,33 +8,38 @@ import ( alg "metrosim/internal/algorithms" ) -var once sync.Once + type MobiliteReduite struct { - faceCase string req *Request + once sync.Once } + /* - Calcule le chemin de l'agent à mobilité réduite vers la porte la plus proche +* Fonction qui permet de définir la destination d'un agent à mobilité réduite */ -func (mr * MobiliteReduite) setUpPath(ag *Agent) { +func (mr *MobiliteReduite)setUpDestination(ag *Agent){ choix_voie := rand.Intn(2) // choix de la voie de départ aléatoire - fmt.Println("[MobiliteReduite, setUpPath] choix_voie = ",choix_voie) - dest_porte := ag.findNearestGate(ag.env.metros[choix_voie].way.gates) + //fmt.Println("[MobiliteReduite, setUpPath] choix_voie = ",choix_voie) + dest_porte := (ag.findNearestGates(ag.env.metros[choix_voie].way.gates))[0].Gate fmt.Println("[MobiliteReduite, setUpPath] dest_porte = ",dest_porte) ag.destination = dest_porte + mr.setUpPath(ag) +} +/* + Calcule le chemin de l'agent à mobilité réduite vers la porte la plus proche +*/ +func (mr * MobiliteReduite) setUpPath(ag *Agent) { 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, 2*time.Second) ag.path = path + fmt.Println("[MobiliteReduite, setUpPath] path = ",ag.path) ag.direction = calculDirection(ag.position, Coord{ag.path[0].Row(), ag.path[0].Col()}) } func (mr *MobiliteReduite) Percept(ag *Agent) { - once.Do(func(){mr.setUpPath(ag)}) // la fonction setUp est executé à la premiere appel de la fonction Percept() - - mr.faceCase = ag.getFaceCase() // mettre à jour la case en face de l'agent en fonction de son attribut direction de deplacement - + mr.once.Do(func(){mr.setUpDestination(ag)}) // la fonction setUp est executé à la premiere appel de la fonction Percept() switch { case ag.request != nil: //verifier si l'agent est communiqué par un autre agent, par exemple un controleur lui a demandé de s'arreter //print("Requete recue par l'agent lambda : ", ag.request.decision, "\n") @@ -53,11 +58,13 @@ func (mr *MobiliteReduite) Deliberate(ag *Agent) { if mr.req.decision == Stop{ ag.decision = Wait mr.req = nil //demande traitée - } else { // sinon alors la requete est de type "Viré" cette condition est inutile car l'usager lambda ne peut pas etre expulsé , elle est nécessaire pour les agents fraudeurs + } else if mr.req.decision == Expel { // sinon alors la requete est de type "Viré" cette condition est inutile car l'usager lambda ne peut pas etre expulsé , elle est nécessaire pour les agents fraudeurs //fmt.Println("[AgentLambda, Deliberate] Expel") ag.decision = Expel mr.req = nil //demande traitée - } + }else if mr.req.decision == Disappear { + ag.decision = Disappear + } }else if ag.position == ag.destination && (ag.isOn[ag.position] == "W" || ag.isOn[ag.position] == "S") { // si l'agent est arrivé à sa destination et qu'il est sur une sortie //fmt.Println(ag.id, "disappear") ag.decision = Disappear @@ -93,6 +100,9 @@ func (mr *MobiliteReduite) Act(ag *Agent) { */ func (mr *MobiliteReduite) MoveMR(ag *Agent) { // ================== Déplacement si aucun problème ======================= + if(len(ag.path) <= 0){ + mr.setUpPath(ag) + } safe, or := IsMovementSafe(ag.path, ag, ag.env) if safe { if len(ag.isOn) > 0 { diff --git a/internal/simulation/simu.go b/internal/simulation/simu.go index 4d0041bc9287d0560de6b1c7e99df1560f51c925..aba913e5ca18b41d4c39a6dcb9e51acf5d389e3b 100644 --- a/internal/simulation/simu.go +++ b/internal/simulation/simu.go @@ -7,41 +7,78 @@ import ( "time" ) -// Déclaration de la matrice /* - * X : Mur, zone inatteignable - * E : Entrée - * S : Sortie - * W : Entrée et Sortie - * Q : Voie - * _ : Couloir, case libre - * B: Bridge/Pont, zone accessible - * valeur de AgentID : Agent + * //TODO: Mettre en place un débit d'apparition des agents */ -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", "_"}, - {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B"}, - {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B"}, - {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B"}, - {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B"}, - {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, - {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, - {"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", "S", "S", "X", "X", "X", "X", "X", "X", "E", "E", "X", "X", "X", "X", "X", "X"}, + +// Déclaration de la matrice +/* + - X : Mur, zone inatteignable + - E : Entrée + - S : Sortie + - W : Entrée et Sortie + - Q : Voie + - _ : Couloir, case libre + - B: Bridge/Pont, zone accessible + - G: gate/porte de métro + - O : Porte ouverte + - M : rame de métro + - valeur de AgentID : Agent +*/ +var carte [50][50]string = [50][50]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", "W", "W", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "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", "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", "W", "W"}, + {"X", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X", "X", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X", "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", "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", "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", "W", "W"}, + {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "Q", "B", "B", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, + {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "W", "W"}, + {"X", "X", "X", "X", "S", "S", "X", "X", "X", "X", "X", "X", "E", "E", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "E", "E", "X", "X", "X", "X", "X", "X", "S", "S", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "W", "W"}, } -var playground [20][20]string = [20][20]string{ +var playground [50][50]string = [50][50]string{ {"_", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, {"_", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, {"_", "X", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, @@ -65,37 +102,36 @@ var playground [20][20]string = [20][20]string{ } type Simulation struct { - env Environment - agents []Agent - maxStep int - maxDuration time.Duration - step int // Stats - start time.Time - syncChans sync.Map - metros []Metro + env Environment + maxStep int + maxDuration time.Duration + step int // Stats + start time.Time + syncChans sync.Map + newAgentChan chan Agent // permet la création d'agent en cours de simulation (canal de com avec env) } +// TODO:voir si agents est mis à jour lors de suppression d'agent + func (sim *Simulation) Env() *Environment { return &sim.env } func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu *Simulation) { simu = &Simulation{} - simu.agents = make([]Agent, 0, agentCount) simu.maxStep = maxStep simu.maxDuration = maxDuration - // Communication entre agents - mapChan := make(map[AgentID]chan Request) + simu.newAgentChan = make(chan Agent, 200) // channel avec buffer pour gérer les sorties simultanées // Création de l'environement - simu.env = *NewEnvironment([]Agent{}, carte, mapChan) + simu.env = *NewEnvironment([]Agent{}, carte, simu.newAgentChan, agentCount) //simu.env = *NewEnvironment([]Agent{}, playground, mapChan) // Création du métro - metro1 := *NewMetro(10*time.Second, 5*time.Second, 2, &simu.env, NewWay(1, []Coord{{8, 5}})) - metro2 := *NewMetro(10*time.Second, 5*time.Second, 2, &simu.env, NewWay(2, []Coord{{13, 4}})) - simu.metros = []Metro{metro1, metro2} + //NewWay(wayId WayID, upLeftCoord, downRightCoord Coord, goToLeft bool, gates []Coord, env *Environment) + metro1 := *NewMetro(10*time.Second, 5*time.Second, 20, 2, NewWay(1, Coord{9, 0}, Coord{10, 39}, true, []Coord{{8, 5}, {8, 34}}, &simu.env)) + metro2 := *NewMetro(10*time.Second, 5*time.Second, 20, 2, NewWay(2, Coord{11, 0}, Coord{12, 39}, false, []Coord{{13, 5}, {13, 34}}, &simu.env)) simu.env.metros = []Metro{metro1, metro2} // création des agents et des channels @@ -108,39 +144,40 @@ func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu ag := &Agent{} + + + id := fmt.Sprintf("MR%d", i) + ag = NewAgent(id, &simu.env, syncChan, 400, 0, true, &MobiliteReduite{}, Coord{0, 28}, Coord{8, 5}, 1, 1) + /* if i%2 == 0 { //Type Agent id := fmt.Sprintf("MR%d", i) //NewAgent(id string, env *Environment, syncChan chan int, vitesse time.Duration, force int, politesse bool, behavior Behavior, departure, destination Coord, width, height int) - ag = NewAgent(id, &simu.env, syncChan, 200, 0, true, &MobiliteReduite{}, Coord{18, 4}, Coord{13, 4}, 1, 1) + ag = NewAgent(id, &simu.env, syncChan, 200, 0, true, &UsagerLambda{}, Coord{49, 32}, Coord{0, 9}, 2, 1) } else { // Type Controleur //id := fmt.Sprintf("Controleur%d", i) - id := fmt.Sprintf("Controleur%d", i) - //ag = NewAgent(id, &simu.env, syncChan, 500, 0, true, &Controleur{}, Coord{1, 8}, Coord{8, 5}, 1, 1) - ag = NewAgent(id, &simu.env, syncChan, 1000, 0, true, &Controleur{}, Coord{18, 12}, Coord{18, 4}, 1, 1) + id := fmt.Sprintf("Agent%d", i) + ag = NewAgent(id, &simu.env, syncChan, 200, 0, true, &UsagerLambda{}, Coord{0, 8}, Coord{8, 5}, 1, 1) + //ag = NewAgent(id, &simu.env, syncChan, 1000, 0, true, &Controleur{}, Coord{18, 12}, Coord{18, 4}, 1, 1) } + */ //ag := NewAgent(id, &simu.env, syncChan, 1000, 0, true, &UsagerLambda{}, Coord{19, 12}, Coord{0, 8}, 2, 1) // ajout de l'agent à la simulation - simu.agents = append(simu.agents, *ag) + simu.env.ags = append(simu.env.ags, *ag) + + simu.env.agentsChan[ag.id] = make(chan Request, 5) // ajout du channel de synchro simu.syncChans.Store(ag.ID(), syncChan) - // ajout de l'agent à l'environnement - ag.env.AddAgent(*ag) - ag.env.controlledAgents[ag.id] = false - - // ajout du channel de l'agent à l'environnement - simu.env.agentsChan[ag.id] = make(chan Request) } return simu } func (simu *Simulation) Run() { - // A REVOIR si nécessaire de faire appeler simu.env.pi() - log.Printf("Démarrage de la simulation [step: %d, π: %f]", simu.step, simu.env.PI()) + log.Printf("Démarrage de la simulation ") // Démarrage du micro-service de Log go simu.Log() // Démarrage du micro-service d'affichage @@ -149,7 +186,7 @@ func (simu *Simulation) Run() { // Démarrage des agents var wg sync.WaitGroup - for _, agt := range simu.agents { + for _, agt := range simu.env.ags { wg.Add(1) go func(agent Agent) { defer wg.Done() @@ -161,7 +198,7 @@ func (simu *Simulation) Run() { simu.start = time.Now() // Lancement des métros - for _, metro := range simu.metros { + for _, metro := range simu.env.metros { wg.Add(1) go func(metro Metro) { defer wg.Done() @@ -170,8 +207,8 @@ func (simu *Simulation) Run() { } // Lancement de l'orchestration de tous les agents - // simu.step += 1 // plus de sens - for _, agt := range simu.agents { + + for _, agt := range simu.env.ags { go func(agt Agent) { step := 0 for { @@ -184,45 +221,59 @@ func (simu *Simulation) Run() { }(agt) } + go simu.listenNewAgentChan() + time.Sleep(simu.maxDuration) - log.Printf("Fin de la simulation [step: %d, in: %d, out: %d, π: %f]", simu.step, simu.env.PI()) } +func (simu *Simulation) listenNewAgentChan() { + for { + select { + case newAgent := <-simu.newAgentChan: + //simu.env.ags = append(simu.env.ags, newAgent) + simu.syncChans.Store(newAgent.ID(), newAgent.syncChan) + go func(agent Agent) { + agent.Start() + }(newAgent) + go func(agent Agent) { + step := 0 + for { + step++ + c, _ := simu.syncChans.Load(agent.ID()) // communiquer les steps aux agents + c.(chan int) <- step // /!\ utilisation d'un "Type Assertion" + time.Sleep(1 * time.Millisecond) // "cool down" + <-c.(chan int) + } + }(newAgent) + + // Add the new agent to simu.agents + + } + } +} + -func (simu *Simulation) Print_v0() { +func (simu *Simulation) Print() { for { for i := 0; i < 20; i++ { - fmt.Println(simu.env.station[i]) + for j := 0; j < 50; j++ { + element := simu.env.station[i][j] + if len(element) > 1 { + fmt.Print(element[len(element)-1:] + " ") // Afficher le premier caractère si la longueur est supérieure à 1 + } else { + fmt.Print(element + " ") + } + } + fmt.Println() } - //fmt.Println("=================================================================================") fmt.Println() fmt.Println() + //fmt.Println("============================================================") //time.Sleep(time.Second / 4) // 60 fps ! - time.Sleep(500 * time.Millisecond) // 1 fps ! - //fmt.Print("\033[H\033[2J") // effacement du terminal + time.Sleep(200 * time.Millisecond) // 1 fps ! } } -func (simu *Simulation) Print() { - for { - for i := 0; i < len(simu.env.station[0]); i++ { - for j := 0; j < len(simu.env.station[0]); j++ { - element := simu.env.station[i][j] - if len(element) > 1 { - fmt.Print(string(element[0]) + " ") // Afficher le premier caractère si la longueur est supérieure à 1 - } else { - fmt.Print(element+" ") - } - } - fmt.Println() - } - fmt.Println() - fmt.Println() - //fmt.Println("============================================================") - //time.Sleep(time.Second / 4) // 60 fps ! - time.Sleep(500 * time.Millisecond) // 1 fps ! - //fmt.Print("\033[H\033[2J") // effacement du terminal - } -} + func (simu *Simulation) Log() { // Not implemented diff --git a/internal/simulation/usagerLambda.go b/internal/simulation/usagerLambda.go index b50fdef505118330d8833d0f5f47db0b17b17e47..7ec0bd21d7075f0cfbcb8d3122a5a3cab8a60e56 100644 --- a/internal/simulation/usagerLambda.go +++ b/internal/simulation/usagerLambda.go @@ -2,9 +2,10 @@ package simulation import ( //"fmt" + "math/rand" - "time" alg "metrosim/internal/algorithms" + "time" ) type UsagerLambda struct { @@ -23,7 +24,6 @@ func (ul *UsagerLambda) Percept(ag *Agent) { } } - } func (ul *UsagerLambda) Deliberate(ag *Agent) { @@ -32,12 +32,19 @@ func (ul *UsagerLambda) Deliberate(ag *Agent) { if ul.req.decision == Stop{ ag.decision = Wait ul.req = nil //demande traitée - } else { // sinon alors la requete est de type "Viré" cette condition est inutile car l'usager lambda ne peut pas etre expulsé , elle est nécessaire pour les agents fraudeurs + return + } else if ul.req.decision == Expel { // cette condition est inutile car l'usager lambda ne peut pas etre expulsé , elle est nécessaire pour les agents fraudeurs //fmt.Println("[AgentLambda, Deliberate] Expel") ag.decision = Expel ul.req = nil //demande traitée + return + }else if ul.req.decision == Disappear { + ag.decision = Disappear + return + }else if ul.req.decision == Wait { + ag.decision = Wait } - }else if ag.position == ag.destination && (ag.isOn[ag.position] == "W" || ag.isOn[ag.position] == "S") { // si l'agent est arrivé à sa destination et qu'il est sur une sortie + }else if (ag.position != ag.departure && ag.position == ag.destination) && (ag.isOn[ag.position] == "W" || ag.isOn[ag.position] == "S") { // si l'agent est arrivé à sa destination et qu'il est sur une sortie //fmt.Println(ag.id, "disappear") ag.decision = Disappear } else if ag.stuck{ // si l'agent est bloqué @@ -56,12 +63,14 @@ func (ul *UsagerLambda) Act(ag *Agent) { time.Sleep(time.Duration(n) * time.Second) } else if ag.decision == Disappear { RemoveAgent(&ag.env.station, ag) - } else { //age.decision == Expel + } else if ag.decision == Expel { //fmt.Println("[AgentLambda, Act] Expel") ag.destination = ag.findNearestExit() //fmt.Println("[AgentLambda, Act] destination = ",ag.destination) ag.env.controlledAgents[ag.id] = true - ag.path = make([]alg.Node,0) + ag.path = make([]alg.Node, 0) ag.MoveAgent() + } else { + // nothing to wait } } diff --git a/internal/simulation/way.go b/internal/simulation/way.go index 87212b517ae3fa6b73a3df679c04dbc661960854..141e50d1d17da44c1d996379108c7192e8f12498 100644 --- a/internal/simulation/way.go +++ b/internal/simulation/way.go @@ -1,14 +1,72 @@ package simulation +/* + * Classe et méthodes principales de la structure Way (porte de métro) + */ + +import ( + alg "metrosim/internal/algorithms" + "time" +) + type Way struct { - id WayID - gates []Coord //listes des portes associée à la voie + id WayID + upLeftCoord Coord // inclus + downRightCoord Coord // inclus + goToLeft bool // si vrai, le métro se déplace de droite à gauche, si faux de gauche à droite + horizontal bool + gates []Coord //listes des portes associée à la voie + nearestExit []Coord // Chemin vers la sortie la plus proche pour chaque porte (index vers pathsToExit) + pathsToExit [][]alg.Node + env *Environment } type WayID int -func NewWay(wayId WayID, gates []Coord) *Way { +func NewWay(wayId WayID, upLeftCoord, downRightCoord Coord, goToLeft bool, gates []Coord, env *Environment) *Way { + /* Affichage des portes */ + for _, gate := range gates { + if !(gate[0] < 0 || gate[1] > 49) && env.station[gate[0]][gate[1]] != "X" && env.station[gate[0]][gate[1]] != "Q" { + env.station[gate[0]][gate[1]] = "G" + } + + } + /* Sens de la voie */ + horizontal := true + if alg.Abs(upLeftCoord[0]-downRightCoord[0]) > alg.Abs(upLeftCoord[1]-downRightCoord[1]) { + horizontal = false + } + nearestExit := make([]Coord, len(gates)) + pathsToExit := make([][]alg.Node, len(gates)) + for index, gate := range gates { + row, col := alg.FindNearestExit(env.station, gate[0], gate[1]) + nearestExit[index] = Coord{row, col} + pathsToExit[index] = alg.FindPath(env.station, *alg.NewNode(gate[0], gate[1], 0, 0, 1, 1), *alg.NewNode(row, col, 0, 0, 0, 0), *alg.NewNode(-1, -1, 0, 0, 0, 0), false, 5*time.Second) + index++ + } + return &Way{ - id: wayId, - gates: gates} + id: wayId, + upLeftCoord: upLeftCoord, + downRightCoord: downRightCoord, + goToLeft: goToLeft, + horizontal: horizontal, + gates: gates, + nearestExit: nearestExit, + pathsToExit: pathsToExit, + env: env} +} + +func (way *Way) openGates() { + // Début d'autorisation d'entrer dans le métro + for _, gate := range way.gates { + way.env.station[gate[0]][gate[1]] = "O" + } +} + +func (way *Way) closeGates() { + // Fin d'autorisation d'entrer dans le métro + for _, gate := range way.gates { + way.env.station[gate[0]][gate[1]] = "G" + } }