Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pillisju/metrosim
1 result
Show changes
Commits on Source (77)
Showing
with 2042 additions and 517 deletions
# MetroSim
Work in progress.. :construction_worker: :hammer: :warning:
// 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
*/
\ No newline at end of file
- 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
*/
package api
import (
"bytes"
"encoding/json"
_ "encoding/json"
"fmt"
"log"
req "metrosim/internal/request"
sim "metrosim/internal/simulation"
"net/http"
"time"
sim "metrosim/internal/simulation"
)
func StartAPI(sim *sim.Simulation) {
mux := http.NewServeMux()
var simulation *sim.Simulation = nil
pi := func(w http.ResponseWriter, r *http.Request) {
msg, _ := json.Marshal(sim.Env().PI())
fmt.Fprintf(w, "%s", msg)
}
func StartAPI() {
mux := http.NewServeMux()
port := "12000"
mux.HandleFunc("/pi", pi)
mux.HandleFunc("/configure", simHandler("configure"))
mux.HandleFunc("/launch", simHandler("launch"))
//mux.HandleFunc("/stop", simHandler("stop"))
s := &http.Server{
Addr: ":12000",
Addr: ":" + "12000",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20}
log.Println("Listening on localhost:8080")
log.Println(fmt.Sprintf("Listening on localhost:%s", port))
log.Fatal(s.ListenAndServe())
}
func decodeConf(r *http.Request) (conf req.Cfg, err error) {
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
err = json.Unmarshal(buf.Bytes(), &conf)
fmt.Println(err)
return
}
func checkMethod(method string, r *http.Request) bool {
return r.Method == method
}
func simHandler(action string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS,PUT")
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")
switch action {
case "configure":
if simulation != nil && simulation.IsRunning() {
log.Println("[API] Must stop current sim to configure")
} else {
configuration(w, r)
}
case "launch":
log.Println("[API] Launch request")
if simulation != nil {
log.Println("[API] running")
if !simulation.IsRunning() {
log.Println("[API] running")
go simulation.Run()
}
msg, _ := json.Marshal(simulation.Print())
fmt.Fprintf(w, "%s", msg)
}
case "stop":
// TODO
}
}
}
func configuration(w http.ResponseWriter, r *http.Request) {
// vérification de la méthode de la requête
if !checkMethod("POST", r) {
return
}
conf, err := decodeConf(r)
if err != nil {
log.Println("[API] Not able to read configuration data")
simulation = nil
return
}
log.Println("[API] Received configuration data : ", conf)
size := len(conf.LeftTopCorners)
if len(conf.RightBottomCorners) != size || len(conf.Gates) != size || len(conf.GoToLeft) != size || len(conf.Frequency) != size || len(conf.StopTime) != size || len(conf.Capacity) != size {
simulation = nil
return
}
log.Println("[API] Simulation initialized")
simulation = sim.NewSimulation(conf)
}
package main
import (
simulation "metrosim/internal/simulation"
"time"
"fmt"
"metrosim/api"
)
func main() {
s := simulation.NewSimulation(2, -1, 600*time.Second)
//go simulation.StartAPI(s)
s.Run()
api.StartAPI()
fmt.Scanln()
}
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'")
}
}
......@@ -2,7 +2,9 @@ package algorithms
import (
"container/heap"
"context"
"math/rand"
"time"
)
/*
......@@ -59,10 +61,11 @@ func (pq *PriorityQueue) Pop() interface{} {
return item
}
type ZoneID int
type Coord [2]int
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()
func FindPath(matrix [20][20]string, start, end Node, forbidenCell Node, orientation bool) []Node {
pq := make(PriorityQueue, 0)
heap.Init(&pq)
......@@ -71,15 +74,22 @@ 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
for pq.Len() > 0 {
select {
case <-ctx.Done():
// Timeout reached, return an error or handle accordingly
return nil
default:
// Continue with the algorithm
}
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
......@@ -119,11 +129,10 @@ 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 {
//fmt.Println("okk")
func getNeighbors(matrix [50][50]string, current, end Node, forbiddenCell Node, orientation bool) []*Node {
neighbors := make([]*Node, 0)
// Possible moves: up, down, left, right
// Déplacements possibles: up, down, left, right
possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
for _, move := range possibleMoves {
......@@ -131,14 +140,13 @@ func getNeighbors(matrix [20][20]string, current, end Node, forbiddenCell Node,
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
//Vérifie que le déplacement soit valide
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(matrix, newRow, newCol, end),
width: current.width,
height: current.height,
orientation: current.orientation,
......@@ -152,7 +160,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,
......@@ -165,21 +173,26 @@ 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)
// On évite d'avoir tout le temps le même chemin pour un même point de départ et d'arrivée
//return abs(row-end.row) + abs(col-end.col) + rand.Intn(3)
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 {
func Abs(x int) int {
if x < 0 {
return -x
}
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
......@@ -187,22 +200,23 @@ 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
}
// Check if the agent fits in the new position, considering its dimensions and rotation
if orientation {
lRowBound, uRowBound, lColBound, uColBound := calculateBounds(newRow, newCol, current.width, current.height, current.orientation)
lRowBound, uRowBound, lColBound, uColBound := CalculateBounds(Coord{newRow, newCol}, current.width, current.height, current.orientation)
for i := lRowBound; i < uRowBound; i++ {
for j := lColBound; j < uColBound; j++ {
// Calculate the absolute coordinates in the matrix
absRow, absCol := i, j
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]) {
......@@ -211,11 +225,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
}
}
......@@ -242,35 +257,17 @@ func rotateCoordinates(i, j, orientation int) (rotatedI, rotatedJ int) {
return rotatedI, rotatedJ
}
func calculateBounds(row, col, width, height, orientation int) (infRow, supRow, infCol, supCol int) {
borneInfRow := 0
borneSupRow := 0
borneInfCol := 0
borneSupCol := 0
// Calcul des bornes de position de l'agent après mouvement
switch orientation {
case 0:
borneInfRow = row - width + 1
borneSupRow = row + 1
borneInfCol = col
borneSupCol = col + height
case 1:
borneInfRow = row
borneSupRow = row + height
borneInfCol = col
borneSupCol = col + width
case 2:
borneInfRow = row
borneSupRow = row + width
borneInfCol = col
borneSupCol = col + height
case 3:
borneInfRow = row
borneSupRow = row + height
borneInfCol = col - width + 1
borneSupCol = col + 1
func FindNearestExit(exits *[]Coord, row, col int) (dest_row, dest_col int) {
// Recherche de la sortie la plus proche
min := 1000000
for _, exit := range *exits {
dist := Abs(row-exit[0]) + Abs(col-exit[1])
if dist < min {
min = dist
dest_row = exit[0]
dest_col = exit[1]
}
}
return borneInfRow, borneSupRow, borneInfCol, borneSupCol
return dest_row, dest_col
}
package algorithms
type Coord [2]int
func EqualCoord(coord1, coord2 *Coord) bool {
// Vérifie l'égalité de 2 objets Coord
return coord1[0] == coord2[0] && coord1[1] == coord2[1]
}
func RemoveCoord(to_remove Coord, mapping map[Coord]string) {
// Suppression d'une clé dans une map
for coord, _ := range mapping {
if EqualCoord(&coord, &to_remove) {
delete(mapping, coord)
}
}
}
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
borneSupCol := 0
// 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
borneSupCol = position[1] + 1
}
return borneInfRow, borneSupRow, borneInfCol, borneSupCol
}
\ No newline at end of file
package request
import (
alg "metrosim/internal/algorithms"
)
type Cfg struct {
// Section des paramètres généraux
Station [50][50]string `json:"map"`
MaxDuration int `json:"maxDuration"` // durée maximale de la simulation (en secondes)
Flow int `json:"flow"` // débit de création d'agents/seconde, 1 agent créé toutes les flow MILLIsecondes
Controleurs bool `json:"controleurs"` // présence de controleurs
Fraudeurs bool `json:"fraudeurs"` // présence de fraudeurs
Impolis bool `json:"impolis"` // présence d'impolis
Mob_reduite bool `json:"mob_reduite"` // présence de personnes à mobilité réduite
Patients bool `json:"patients"` // présence d'usagers patients
// Section des paramètres des voies
// (un indice est associé à une voie, ex: goToLeft[0]
// concerne la voie 0, goToLeft[1] concerne la voie 1, etc...)
LeftTopCorners []alg.Coord `json:"leftTopCorners"` // coins haut-gauche des voies
RightBottomCorners []alg.Coord `json:"rightDownCorners"` // coins bas-droit des voies
GoToLeft []bool `json:"goToLeft"` // sens de circulation des voies
Gates [][]alg.Coord `json:"gates"` // coordonnées des portes (gates[i] correspond aux portes de la voie i)
// Section des paramètres des trains (un indice est associé à un train, et un train à le même indice que sa voie ex : capacity[0] correspond à la capacité du train de la voie 0])
Frequency []int `json:"frequency"` // fréquence de passage des trains en secondes
StopTime []int `json:"stopTime"` // temps d'arrêt des trains en secondes
Capacity []int `json:"capacity"` // capacité des trains (nombre de cases occupées par un train)
}
package request
type Request struct {
demandeur chan Request //channel de l'émetteur de la demande
decision int
}
func NewRequest(demandeur chan Request, decision int) (req *Request) {
return &Request{demandeur, decision}
}
func (req *Request) Demandeur() (demandeur chan Request) {
return req.demandeur
}
func (req *Request) Decision() (decision int) {
return req.decision
}
func (req *Request) SetDemandeur(demandeur chan Request) {
req.demandeur = demandeur
}
func (req *Request) SetDecision(decision int) {
req.decision = decision
}
This diff is collapsed.
package simulation
/*
Le controleur se déplace aléatoirement dans la station pendant un certain temps
et controle les agents qui se trouvent devant lui si ils ne sont pas déjà controllés par un autre controleur,
si l'agent est un fraudeur alors il est expulsé, sinon il est arreté pendant un certain temps
*/
import (
"fmt"
"math/rand"
"time"
alg "metrosim/internal/algorithms"
req "metrosim/internal/request"
"regexp"
"fmt"
"time"
)
/*
Je suppose que l'id du controleur est de format "Cont + un chiffre"
Exemple : "Cont1"
et l'id du l'agent est de format "Agent + un chiffre"
Exemple : "Agent1"
*/
type Controleur struct{
faceCase string // chaine de caractère qui contient l'id de l'agent qui se trouve devant le controleur, exemple : "Agent1", "Fraudeur1", "X" ,etc.
type Controleur struct {
req *req.Request // requete reçue par le controleur
faceCase string // chaine de caractère qui contient l'id de l'agent qui se trouve devant le controleur, exemple : "Agent1", "Fraudeur1", "X" ,etc.
timer *time.Timer // timer qui permet de définir la durée de vie du controleur
isExpired bool // true si le controleur est expiré, false sinon
}
func (c *Controleur) Percept(ag *Agent) {
env := ag.env
//initialiser le faceCase en fonction de la direction de l'agent
c.faceCase = ag.getFaceCase()
switch {
// comportement par défaut (comportement agent Lambda)
case ag.request != nil: //verifier si l'agent est communiqué par un autre agent
//print("Requete recue par l'agent lambda : ", ag.request.decision, "\n")
c.req = ag.request
default:
ag.stuck = ag.isStuck()
if ag.stuck {
return
if ag.orientation == 0 { // vers le haut
c.faceCase = env.station[ag.position[0]-1][ag.position[1]]
} else if ag.orientation == 1 { // vers la droite
c.faceCase = env.station[ag.position[0]][ag.position[1]+1]
} else if ag.orientation == 2 { // vers le bas
c.faceCase = env.station[ag.position[0]+1][ag.position[1]]
} else { // vers la gauche
c.faceCase = env.station[ag.position[0]][ag.position[1]-1]
}
}
}
func (c *Controleur) Deliberate(ag *Agent) {
// Verifier si la case devant lui contient un agent ou un fraudeur
// Créer l'expression régulière
regexAgent:= `^Agent\d+$` // \d+ correspond à un ou plusieurs chiffres
regexCont := `^Cont\d+$` // \d+ correspond à un ou plusieurs chiffres
regexFraudeur := `^Fraudeur\d+$`
// Vérifier si la valeur de faceCase ne correspond pas au motif
matchedAgt, err1 := regexp.MatchString(regexAgent, c.faceCase)
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)
matchedCont, err1 := regexp.MatchString(regexCont, c.faceCase)
matchedFraud, err := regexp.MatchString(regexFraudeur, c.faceCase)
//fmt.Println("faceCase : ", c.faceCase)
//fmt.Println("matchedAgt : ", matchedAgt)
if err != nil {
fmt.Println("Erreur lors de l'analyse de la regex :", err)
return
}
if err1 != nil {
fmt.Println("Erreur lors de l'analyse de la regex :", err1)
return
} else {
if matchedAgt {
fmt.Println("L'agent ", c.faceCase, " a été arrêté")
ag.decision = Expel // arreter l'agent devant lui
} else if matchedFraud {
if matchedCont {
// si l'agent devant le controleur est un autre controleur alors il faut attendre qu'il se déplace
ag.decision = Move
return
}
if matchedFraud && !ag.env.controlledAgents[AgentID(c.faceCase)] {
ag.decision = Expel // virer l'agent devant lui
} else{
// Comportement de l'usager lambda (comportement par defaut)
if ag.stuck {
ag.decision = Wait
} else {
ag.decision = Move
}
return
} else if ag.position == ag.destination && (ag.isOn[ag.position] == "W" || ag.isOn[ag.position] == "S") { // si le controleur est arrivé à sa destination et qu'il est sur une sortie
//fmt.Println(ag.id, "disappear")
ag.decision = Disappear
return
} else if ag.stuck { // si le controleur est bloqué
ag.decision = Wait
return
} else {
ag.decision = Move
return
}
}
}
func (c *Controleur) Act(ag *Agent) {
if ag.decision == Move {
switch ag.decision {
case Move:
if !c.isExpired {
//fmt.Printf("[Controleur, Act, non expiré] Le controleur %s est en mouvement \n", ag.id)
c.SetUpDestination(ag)
//fmt.Printf("[Controleur, Act] destination s = %s : %d \n",ag.id,ag.destination)
} else {
//fmt.Printf("[Controleur, Act] Le controleur %s est expiré \n",ag.id)
ag.destination = ag.findNearestExit()
//fmt.Printf("[Controleur, Act, Expire] destination de %s = %d \n",ag.id,ag.destination)
}
ag.MoveAgent()
} else if ag.decision == Wait {
case Wait:
n := rand.Intn(2) // temps d'attente aléatoire
time.Sleep(time.Duration(n) * time.Second)
} else {
case Disappear:
ag.env.RemoveAgent(ag)
case Expel: //Expel
agt_face_id := AgentID(c.faceCase) //id de l'agent qui se trouve devant le controleur
fmt.Print("L'agent ", agt_face_id, " a été expulsé\n")
ag.env.agentsChan[agt_face_id] <- *NewRequest(ag.id, ag.decision) // envoie la decision du controleur à l'agent qui se trouve devant lui
fmt.Print("L'agent ", agt_face_id, " a été expulsé \n")
ag.env.controlledAgents[agt_face_id] = true // l'agent qui se trouve devant le controleur est controlé
ag.env.agentsChan[agt_face_id] <- *req.NewRequest(ag.env.agentsChan[ag.id], ag.decision) // envoie la decision du controleur à l'agent qui se trouve devant lui
//time.Sleep(500 * time.Millisecond)
}
ag.request = nil
}
func (c *Controleur) SetUpDestination(ag *Agent) {
rand.Seed(time.Now().UnixNano()) // le générateur de nombres aléatoires
randomRow := rand.Intn(len(ag.env.station[0])) // Génère un entier aléatoire entre 0 et 49
randomCol := rand.Intn(len(ag.env.station[1])) // Génère un entier aléatoire entre 0 et 49
for ag.env.station[randomRow][randomCol] != "_" {
randomRow = rand.Intn(len(ag.env.station[0])) // Génère un entier aléatoire entre 0 et 49
randomCol = rand.Intn(len(ag.env.station[1])) // Génère un entier aléatoire entre 0 et 49
}
ag.destination = alg.Coord{randomRow, randomCol}
}
func (c *Controleur) startTimer() {
//rand.Seed(time.Now().UnixNano()) // le générateur de nombres aléatoires
//randomSeconds := rand.Intn(9) + 2 // Génère un entier aléatoire entre 2 et 10
randomSeconds := 500
lifetime := time.Duration(randomSeconds) * time.Second
c.timer = time.NewTimer(lifetime)
//fmt.Println("[Controleur , startTimer] Le controleur est créé avec une durée de vie de ", lifetime)
go func() {
<-c.timer.C // attend que le timer expire
c.isExpired = true
}()
}
......@@ -2,79 +2,270 @@ package simulation
import (
"fmt"
alg "metrosim/internal/algorithms"
req "metrosim/internal/request"
"sync"
)
//TODO:rajouter les entrées et sorties
type Environment struct {
sync.RWMutex
ags []Agent
agentCount int
station [20][20]string
agentsChan map[AgentID]chan Request
// zones map[Coord]ZoneID // Zones de la station
// panneaux map[ZoneID][]alg.Node // Les panneaux de la station, permettant d'aller vers la zone
ags []Agent
agentCount int
station [50][50]string
agentsChan map[AgentID]chan req.Request
controlledAgents map[AgentID]bool
newAgentChan chan Agent
metros []Metro
exits []alg.Coord
entries []alg.Coord
gates []alg.Coord
}
type ZoneID int
func NewEnvironment(ags []Agent, carte [50][50]string, metros []Metro, newAgtCh chan Agent, agtCount int, sim *Simulation) (env *Environment) {
agentsCh := make(map[AgentID]chan req.Request)
mapControlled := make(map[AgentID]bool)
// Récupération des entrées et sorties
entries := make([]alg.Coord, 0)
exits := make([]alg.Coord, 0)
for i := 0; i < 50; i++ {
for j := 0; j < 50; j++ {
switch carte[i][j] {
case "E":
entries = append(entries, alg.Coord{i, j})
case "S":
exits = append(exits, alg.Coord{i, j})
case "W":
entries = append(entries, alg.Coord{i, j})
exits = append(exits, alg.Coord{i, j})
}
}
}
// Récupération des portes
gates := make([]alg.Coord, 0)
for _, metro := range metros {
fmt.Println(metro.way.gates)
for _, gate := range metro.way.gates {
gates = append(gates, gate)
}
}
// Initialisation de la vérification du contrôle agents (faux au départ)
for _, ag := range ags {
mapControlled[ag.id] = false
}
func NewEnvironment(ags []Agent, carte [20][20]string, agentsCh map[AgentID]chan Request) (env *Environment) {
return &Environment{ags: ags, agentCount: len(ags), station: carte, agentsChan: agentsCh}
return &Environment{
ags: ags,
agentCount: agtCount,
station: carte,
agentsChan: agentsCh,
controlledAgents: mapControlled,
newAgentChan: newAgtCh,
exits: exits,
entries: entries,
gates: gates,
metros: metros,
}
}
func (env *Environment) AddAgent(agt Agent) {
// Ajout d'un agent à l'environnement
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 req.Request, 5)
env.agentCount++
env.newAgentChan <- agt
}
func (env *Environment) RemoveAgent(agt Agent) {
func (env *Environment) DeleteAgent(agt Agent) {
// Suppression d'un agent de l'environnement
env.Lock()
defer env.Unlock()
for i := 0; i < len(env.station); i++ {
if env.ags[i].id == agt.id{
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)
// Sortir de la boucle après avoir trouvé et supprimé l'élément
break
}
}
env.agentCount--
}
func (env *Environment) GetAgentChan(agt_id AgentID) chan req.Request {
// Récupère le channel de communication d'un agent
return env.agentsChan[agt_id]
}
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 (env *Environment) verifyEmptyCase(c alg.Coord) bool {
return env.station[c[0]][c[1]] == "_" //|| env.station[c[0]][c[1]] == "E" || env.station[c[0]][c[1]] == "S" || env.station[c[0]][c[1]] == "W"
}
func existAgent(c string) bool {
// Vérifie si c'est un agent
return c != "X" && c != "E" && c != "S" && c != "W" && c != "Q" && c != "_" && c != "B"
}
func calculDirection(depart alg.Coord, arrive alg.Coord) int {
// Calcul de la direction d'un agent
if depart[0] == arrive[0] {
if depart[1] > arrive[1] {
return 3 //Gauche
} else {
return 1 //droite
}
} else {
if depart[0] > arrive[0] {
return 0 //haut
} else {
return 2 //bas
}
}
}
func (env *Environment) RemoveAgent(agt *Agent) {
// Supprime l'agent de la matrice
// Calcul des bornes de position de l'agent
borneInfRow, borneSupRow, borneInfCol, borneSupCol := alg.CalculateBounds(agt.position, agt.width, agt.height, agt.orientation)
for i := borneInfRow; i < borneSupRow; i++ {
for j := borneInfCol; j < borneSupCol; j++ {
if len(env.station[i][j])>1{
env.station[i][j] = agt.isOn[alg.Coord{i, j}]
}
alg.RemoveCoord(alg.Coord{i, j}, agt.isOn)
}
}
}
func (env *Environment) Do(a Action, c Coord) (err error) {
func (env *Environment) writeAgent(agt *Agent) {
// Ecris l'agent dans la matrice
env.Lock()
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])
// Calcul des bornes de position de l'agent
borneInfRow, borneSupRow, borneInfCol, borneSupCol := alg.CalculateBounds(agt.position, agt.width, agt.height, agt.orientation)
for i := borneInfRow; i < borneSupRow; i++ {
for j := borneInfCol; j < borneSupCol; j++ {
env.station[i][j] = string(agt.id)
}
}
}
return nil
case Noop:
return nil
func (env *Environment) getNbAgentsAround(pos alg.Coord) int {
//pos est la position de la porte
way := env.getWay(pos)
upleft := way.upLeftCoord
//downright := way.downRightCoord
upmetro := false
if pos[0] == upleft[0]-1 { //si la porte est en haut du métro
upmetro = true
}
return fmt.Errorf("bad action number %d", a)
nb := 0
if upmetro {
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
a := pos[0] - i
b := pos[1] + j
if a >= 0 && b >= 0 && a < len(env.station[0]) && b < len(env.station[1]) {
c := env.station[a][b]
if existAgent(c) {
nb++
}
}
b = pos[1] - j
if a >= 0 && b >= 0 && a < len(env.station[0]) && b < len(env.station[1]) {
c := env.station[a][b]
if existAgent(c) {
nb++
}
}
}
}
} else { //si la porte est en bas du métro
for i := 0; i < 4; i++ {
for j := 0; j < 4; j++ {
a := pos[0] + i
b := pos[1] + j
if a >= 0 && b >= 0 && a < len(env.station[0]) && b < len(env.station[1]) {
c := env.station[a][b]
if existAgent(c) {
nb++
}
}
b = pos[1] - j
if a >= 0 && b >= 0 && a < len(env.station[0]) && b < len(env.station[1]) {
c := env.station[a][b]
if existAgent(c) {
nb++
}
}
}
}
}
return nb
}
func (env *Environment) getWay(pos alg.Coord) *Way {
for _, metro := range env.metros {
for _, gate := range metro.way.gates {
if gate[0] == pos[0] && gate[1] == pos[1] {
return metro.way
}
}
}
return nil
}
func (env *Environment) PI() float64 {
func (env *Environment) Station() [50][50]string {
// Récupère la matrice de l'environnement
env.RLock()
defer env.RUnlock()
return 4
return env.station
}
func (env *Environment) Rect() Coord {
return Coord{0, 0}
}
func (env *Environment) getNearGateFromGate(gate Gate) []alg.Coord {
/*
* Renvoie les portes proches de la porte passée en paramètre
*/
gates := env.gates
nearGates := make([]alg.Coord, 0)
for _, g := range gates {
if alg.Abs(g[0]-gate.Position[0]) <= 1 && alg.Abs(g[1]-gate.Position[1]) <= 1 {
nearGates = append(nearGates, g)
}
}
nearGates = append(nearGates, gate.Position)
return nearGates
}
func (env *Environment) GetAgentChan(agt_id AgentID) chan Request {
return env.agentsChan[agt_id]
}
\ No newline at end of file
package simulation
import (
"fmt"
"log"
"math/rand"
alg "metrosim/internal/algorithms"
req "metrosim/internal/request"
"sync"
"time"
)
/*
* // Apparition des agents sortant
*/
var metro_speed int = 5 // Nombre de seconde de l'entrée en gare
type Metro struct {
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
comChannel chan req.Request
way *Way
}
func NewMetro(freq time.Duration, stopT time.Duration, capacity, freeS int, way *Way) *Metro {
return &Metro{
frequency: freq,
stopTime: stopT,
capacity: capacity,
freeSpace: freeS,
comChannel: make(chan req.Request),
way: way,
}
}
func (metro *Metro) Start() {
// Début de la simulation du métro
log.Printf("Metro starting...\n")
refTime := time.Now()
//var step int
// affichage des portes au départ
metro.closeGates()
for {
if refTime.Add(metro.frequency).Sub(time.Now()) <= time.Duration(metro_speed)*time.Second {
metro.printMetro()
}
if refTime.Add(metro.frequency).Before(time.Now()) {
metro.dropUsers()
metro.openGates()
metro.pickUpUsers()
metro.closeGates()
metro.removeMetro()
metro.freeSpace = rand.Intn(metro.capacity)
refTime = time.Now()
}
}
}
func (metro *Metro) pickUpUsers() {
// Récupérer les usagers à toutes les portes
var wg sync.WaitGroup
for _, gate := range metro.way.gates {
wg.Add(1)
go func(gate alg.Coord) {
defer wg.Done()
metro.pickUpGate(&gate, time.Now().Add(metro.stopTime), false)
}(gate)
}
wg.Wait()
}
func (metro *Metro) pickUpGate(gate *alg.Coord, endTime time.Time, force bool) {
// Récupérer les usagers à une porte spécifique
for {
if !time.Now().Before(endTime) {
return
} else {
gate_cell := metro.way.env.station[gate[0]][gate[1]]
if len(gate_cell) > 1 {
agent := metro.findAgent(AgentID(gate_cell))
if agent != nil && (((!force && agent.width*agent.height <= metro.freeSpace) && alg.EqualCoord(&agent.destination, gate)) || force) {
fmt.Println("agent entering metro : ", agent.id, "at gate ", gate)
metro.way.env.agentsChan[agent.id] <- *req.NewRequest(metro.comChannel, EnterMetro)
select {
case <-metro.comChannel:
metro.freeSpace = metro.freeSpace - agent.width*agent.height
fmt.Println("agent entered metro : ", agent.id, "at gate ", gate)
case <-time.After(2 * time.Second):
// Si l'agent prend trop de temps à répondre, on le supprime "manuellement"
if metro.findAgent(agent.id) != nil {
agent.env.RemoveAgent(agent)
agent.env.DeleteAgent(*agent)
}
if !force {
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.way.env.ags {
if agt.id == agent {
//fmt.Println("found agent", agt.id)
return &agt
}
}
return nil
}
func (metro *Metro) dropUsers() {
// Déposer les usagers dans un métro, à une porte aléatoire
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]
// Attribution d'une sortie aléatoire en destination
ag := NewAgent(id, metro.way.env, make(chan int), 200, true, &UsagerLambda{}, metro.way.gates[gate_nb], metro.way.env.exits[rand.Intn(len(metro.way.env.exits))], width, height)
//ag.path = path
metro.way.env.AddAgent(*ag)
ag.env.writeAgent(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() {
// Afficher le métro sur la carte
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() {
// Supprimer le métro de la carte
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)
}
}
}
}
func (metro *Metro) openGates() {
// Début d'autorisation d'entrer dans le métro
for _, gate := range metro.way.gates {
metro.way.env.station[gate[0]][gate[1]] = "O"
}
metro.way.gatesClosed = false
}
func (metro *Metro) closeGates() {
// Fin d'autorisation d'entrer dans le métro
metro.way.gatesClosed = true
for _, gate := range metro.way.gates {
if len(metro.way.env.station[gate[0]][gate[1]]) > 1 {
// On autorise les agents déjà sur la case à rentrer dans le métro
metro.pickUpGate(&gate, time.Now().Add(time.Duration(1*time.Second)), true)
}
metro.way.env.station[gate[0]][gate[1]] = "G"
}
}
package simulation
/*
L'agent à Mobilité Reduite cherche la porte du metro la plus proche de lui
*/
import (
"fmt"
"math/rand"
alg "metrosim/internal/algorithms"
req "metrosim/internal/request"
"sort"
"time"
)
type MobiliteReduite struct {
req *req.Request
//once sync.Once
}
func (mr *MobiliteReduite) Percept(ag *Agent) {
//mr.once.Do(func(){mr.setUpDestination(ag)}) // on initialise la destination la plus proche, la fonction setUp est executé à la premiere appel à 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
//fmt.Printf("Requete recue par l'agent mR : %d \n", ag.request.Decision())
mr.req = ag.request
default:
ag.stuck = ag.isStuck()
if ag.stuck {
return
}
}
}
func (mr *MobiliteReduite) Deliberate(ag *Agent) {
//fmt.Println("[AgentLambda Deliberate] decision :", ul.req.decision)
if mr.req != nil {
switch mr.req.Decision() {
case Expel: // sinon alors la requete est de type "Viré" cette condition est inutile car MR ne peut pas etre expulsé , elle est nécessaire pour les agents fraudeurs
//fmt.Println("[AgentLambda, Deliberate] Expel")
ag.decision = Expel
return
case Disappear:
fmt.Println("[Deliberate]", ag.id, "Disappear cond 1 (requete)")
ag.decision = Disappear
return
case Wait:
ag.decision = Wait
return
case EnterMetro:
fmt.Println("[MobiliteReduite, Deliberate] EnterMetro")
ag.decision = EnterMetro
return
case YouHaveToMove:
//fmt.Println("J'essaye de bouger")
movement := ag.MoveAgent()
//fmt.Printf("Je suis agent %s Resultat du mouvement de la personne %t \n", ag.id, movement)
if movement {
ag.decision = Done
} else {
ag.decision = Noop
}
return
default:
ag.decision = Move
return
}
} 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("[Deliberate]",ag.id, "Disappear cond 2")
ag.decision = Disappear
/*}else if (ag.position != ag.departure && ag.position == ag.destination){
// si l'agent est arrivé à la porte mais n'a pas reçu une requete du metro pour entrer, il attend
ag.decision = Wait [A REVOIR]
}*/
} else if ag.stuck { // si l'agent est bloqué
ag.decision = Wait
} else {
ag.decision = Move
}
}
func (mr *MobiliteReduite) Act(ag *Agent) {
//fmt.Println("[AgentLambda Act] decision :",ag.decision)
switch ag.decision {
case Move:
//mr.MoveMR(ag)
ag.MoveAgent()
case Wait:
n := rand.Intn(2) // temps d'attente aléatoire
time.Sleep(time.Duration(n) * time.Second)
case Disappear:
ag.env.RemoveAgent(ag)
case 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)
//mr.MoveMR(ag)
ag.MoveAgent()
case EnterMetro:
fmt.Printf("[MobiliteReduite, Act %s] EnterMetro \n", ag.id)
ag.env.RemoveAgent(ag)
mr.req.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], ACK)
}
ag.request = nil
}
/*
* Fonction qui permet de définir la destination d'un agent à mobilité réduite
*/
func (mr *MobiliteReduite) SetUpDestination(ag *Agent) {
choix_voie := rand.Intn(len(ag.env.metros)) // choix de la voie de métro aléatoire
dest_porte := (mr.findNearestGates(ag, ag.env.metros[choix_voie].way.gates))
//fmt.Println("[MobiliteReduite, setUpDestination] dest_porte = ",dest_porte)
ag.destination = dest_porte[0].Position
}
func (mr *MobiliteReduite) findNearestGates(ag *Agent, gates []alg.Coord) []Gate {
var gateDistances []Gate
// 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])
gateDistances = append(gateDistances, Gate{Position: gate, Distance: float64(dist)})
}
// Tri des Coords par distance
sort.Slice(gateDistances, func(i, j int) bool {
return gateDistances[i].Distance < gateDistances[j].Distance
})
return gateDistances
}
This diff is collapsed.
......@@ -3,55 +3,122 @@ package simulation
import (
"fmt"
"math/rand"
alg "metrosim/internal/algorithms"
req "metrosim/internal/request"
"time"
)
type UsagerLambda struct{
req Request
type UsagerLambda struct {
requete *req.Request
// once sync.Once
}
func (ul *UsagerLambda) Percept(ag *Agent) {
// récupérer le channel de l'agent lambda
chan_agt := ag.env.GetAgentChan(ag.id)
select {
case req := <-chan_agt : //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 : ", req.decision, "\n")
ul.req = req
case <- time.After(time.Second):
//ul.once.Do(func() { ul.setUpAleaDestination(ag) }) // la fonction setUp est executé à la premiere appel de la fonction Percept()
/*fmt.Println("[UsagerLambda, Percept] ", ag.id)
if ( ag.env.controlledAgents[ag.id]){
fmt.Println("[UsagerLambda, Percept] ", ag.id, " est controlé")
}
*/
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
fmt.Printf("Requete recue par l'agent lambda %s : %d \n ", ag.id, ag.request.Decision())
ul.requete = ag.request
default:
ag.stuck = ag.isStuck()
if ag.stuck {
return
}
}
}
func (ul *UsagerLambda) Deliberate(ag *Agent) {
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 ul.req.decision == Wait{
//fmt.Println("[AgentLambda Deliberate] decision :", ul.req.decision)
if ul.requete != nil {
switch ul.requete.Decision() {
case Expel:
ag.decision = Expel
return
case Disappear:
ag.decision = Disappear
return
case EnterMetro:
fmt.Println("[UsagerLambda, Deliberate] EnterMetro %s", ag.id)
ag.decision = EnterMetro
return
case Wait:
ag.decision = Wait
return
case Move:
ag.decision = Move
return
case YouHaveToMove:
ag.decision = TryToMove
return
default:
ag.decision = Move
return
}
} 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é
ag.decision = Wait
} else if ul.req.decision == Expel{ // cette condition est inutile car l'usager lambda ne peut pas etre expulsé
ag.decision = Expel
}else {
} else {
ag.decision = Move
}
}
func (ul *UsagerLambda) Act(ag *Agent) {
if ag.decision == Move {
//fmt.Println("[AgentLambda Act] decision :",ag.decision)
switch ag.decision {
case Move:
ag.MoveAgent()
} else if ag.decision == Wait {
n := rand.Intn(2) // temps d'attente aléatoire
case Wait: // temps d'attente aléatoire
n := rand.Intn(2)
time.Sleep(time.Duration(n) * time.Second)
} else if ag.decision == Disapear {
RemoveAgent(&ag.env.station, ag)
} else { //age.decision == Expel
ag.destination = ag.departure
case Disappear:
//fmt.Printf("[UsagerLambda, Act] agent %s est disparu \n",ag.id)
ag.env.RemoveAgent(ag)
case EnterMetro:
//fmt.Printf("[UsagerLambda, Act] agent %s entre dans le Metro \n",ag.id)
ag.env.RemoveAgent(ag)
//fmt.Printf("Demandeur d'entrer le metro : %s \n",ul.requete.Demandeur())
ul.requete.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], ACK)
case Expel:
//fmt.Println("[AgentLambda, Act] Expel")
ag.destination = ag.findNearestExit()
fmt.Printf("[UsagerLambda, Act] position de l'agent %s = (%d , %d) \n", ag.id, ag.position[0], ag.position[1])
fmt.Printf("[UsagerLambda, Act] destination de l'agent %s = (%d , %d) \n", ag.id, ag.destination[0], ag.destination[1])
ag.env.controlledAgents[ag.id] = true
ag.path = make([]alg.Node, 0)
ag.MoveAgent()
fmt.Printf("[UsagerLambda, Act] J'ai bougé %s , ma position = (%d , %d)\n", ag.id, ag.position[0], ag.position[1])
case TryToMove:
//fmt.Printf("Je suis %s est-ce que j'ai bougé? %t \n", ag.id, movement)
if ag.ShiftAgent() {
ul.requete.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], Done)
} else {
ul.requete.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], Noop)
}
}
//ag.request = nil // la requete est traitée
if ag.request != nil && ag.decision == ag.request.Decision() {
ag.request = nil
ul.requete = nil
} // la requete est traitée
}
func (ul *UsagerLambda) SetUpDestination(ag *Agent) {
//fmt.Println("[UsagerLambda, setUpAleaDestination] setUpAleaDestination")
choix_voie := rand.Intn(len(ag.env.metros)) // choix de la voie de métro aléatoire
dest_porte := rand.Intn(len(ag.env.metros[choix_voie].way.gates)) // choix de la porte de métro aléatoire
ag.destination = ag.env.metros[choix_voie].way.gates[dest_porte]
}
func isControlledAgt(ag *Agent) bool {
return ag.env.controlledAgents[ag.id]
}
package simulation
/*
Agent qui se dirige vers la porte la plus proche sans trop de monde (bon rapport monde/proximité )
On normalise les valeurs de proximité et de monde pour avoir un score entre 0 et 1, on choisit la porte ayant le moins du score (le score est la somme des deux valeurs normalisées)
Si plusieurs portes ont le même score, on choisit celle ayant le moins de monde
*/
import (
"fmt"
"math"
"math/rand"
alg "metrosim/internal/algorithms"
req "metrosim/internal/request"
"sort"
"time"
//"sync"
)
type UsagerNormal struct {
req *req.Request // req recue par l'agent lambda
//once sync.Once
}
func (un *UsagerNormal) Percept(ag *Agent) {
//un.once.Do(func() { un.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("req recue par l'agent lambda : ", ag.request.decision, "\n")
un.req = ag.request
default:
ag.stuck = ag.isStuck()
if ag.stuck {
return
}
}
}
func (un *UsagerNormal) Deliberate(ag *Agent) {
//fmt.Println("[AgentLambda Deliberate] decision :", un.req.decision)
if un.req != nil {
switch un.req.Decision() {
case Expel: // cette condition est inutile car l'usager lambda ne peut pas etre expunsé , elle est nécessaire pour les agents fraudeurs
//fmt.Println("[AgentLambda, Deliberate] Expel")
ag.decision = Expel
return
case Disappear:
ag.decision = Disappear
return
case EnterMetro:
ag.decision = EnterMetro
return
case Wait:
ag.decision = Wait
return
case Move:
ag.decision = Move
return
case YouHaveToMove:
fmt.Println("[AgentNormal, Deliberate] J'essaye de bouger ", ag.id)
movement := ag.MoveAgent()
//fmt.Printf("Je suis agent %s Resultat du mouvement de la personne %t \n", ag.id, movement)
if movement {
fmt.Println("[AgentNormal, Deliberate] J'ai bougé ", ag.id)
ag.decision = Done
} else {
ag.decision = Noop
}
return
}
} 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é
ag.decision = Wait
} else {
ag.decision = Move
//un.setUpDestination(ag)
}
}
func (un *UsagerNormal) Act(ag *Agent) {
//fmt.Println("[AgentLambda Act] decision :",ag.decision)
switch ag.decision {
case Move:
ag.MoveAgent()
case Wait: // temps d'attente aléatoire
n := rand.Intn(2)
time.Sleep(time.Duration(n) * time.Second)
case Disappear:
//fmt.Printf("[UsagerLambda, Act] agent %s est disparu \n",ag.id)
ag.env.RemoveAgent(ag)
case EnterMetro:
fmt.Printf("[UsagerNormal, Act] agent %s entre dans le Metro \n", ag.id)
ag.env.RemoveAgent(ag)
//fmt.Printf("Demandeur d'entrer le metro : %s \n",un.req.Demandeur())
un.req.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], ACK)
case Expel:
//fmt.Println("[AgentLambda, Act] Expel")
ag.destination = ag.findNearestExit()
fmt.Printf("[UsagerNormal, Act] destination de l'agent %s = %s \n", ag.id, ag.destination)
ag.env.controlledAgents[ag.id] = true
ag.path = make([]alg.Node, 0)
ag.MoveAgent()
case Noop:
//Cas ou un usager impoli demande a un usager de bouger et il refuse
un.req.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], Noop)
// nothing to do
case Done:
//Cas ou un usager impoli demande a un usager de bouger et il le fait
un.req.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], Done)
case TryToMove:
movement := ag.MoveAgent()
fmt.Printf("Je suis %s est-ce que j'ai bougé? %t \n", ag.id, movement)
if movement {
un.req.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], Done)
} else {
un.req.Demandeur() <- *req.NewRequest(ag.env.agentsChan[ag.id], Noop)
}
}
ag.request = nil
}
func (un *UsagerNormal) SetUpDestination(ag *Agent) {
//t := rand.Intn(10) +1
//time.Sleep(time.Duration(t) * time.Second) // "cool down"
//fmt.Println("[UsagerNormal, setUpDestination] setUpDestination")
choix_voie := rand.Intn(len(ag.env.metros)) // choix de la voie de métro aléatoire
dest_porte := (un.findBestGate(ag, ag.env.metros[choix_voie].way.gates))
ag.destination = dest_porte
fmt.Println("[UsagerNormal, setUpDestination] destination de l'agent ", ag.id, " = ", ag.destination, " son position = ", ag.position)
}
func (un *UsagerNormal) findBestGate(ag *Agent, gates []alg.Coord) alg.Coord {
uniquegates := make([]alg.Coord, 0)
for i, gate := range gates {
if i+1 < len(gates) && twocloseGate(gate, gates[i+1]) {
// Si la porte est trop proche d'une autre, on l'ignore, on considère que c'est la même
continue
}
uniquegates = append(uniquegates, gate)
//gatesDistances[i] = Gate{Position: gate, Distance: float64(dist), NbAgents: nbAgents}
}
gatesDistances := make([]Gate, len(uniquegates))
for i, gate := range uniquegates {
dist := alg.Abs(ag.position[0]-gate[0]) + alg.Abs(ag.position[1]-gate[1]) // Distance de Manhattan entre l'agent et la porte
nbAgents := float64(ag.env.getNbAgentsAround(gate))
gatesDistances[i] = Gate{Position: gate, Distance: float64(dist), NbAgents: nbAgents}
}
//fmt.Println("[findBestGate] agent : ",ag.id)
//fmt.Println("[findBestGate] agent Position : ",ag.position)
//fmt.Println("[findBestGate] gates non normalisé : ",gatesDistances)
normalizedGates, _, _ := normalizeGates(gatesDistances)
fmt.Println("[findBestGate, %s] gates normalisé : ",ag.id ,normalizedGates)
bestGates := gates_with_lowest_score(normalizedGates)
bestGate := bestGates[0]
if len(bestGates) > 1 {
//on choisit la porte ayant le moins de monde
for _, gate := range bestGates {
if gate.NbAgents < bestGate.NbAgents {
bestGate = gate
}
}
}
//fmt.Println("[findBestGate] bestGate : ",bestGate)
//choix de la porte aléatoire parmi les portes adjacentes
nearGates := ag.env.getNearGateFromGate(bestGate)
var bestGatePos alg.Coord
if len(nearGates) > 1 {
bestGatePos = nearGates[rand.Intn(len(nearGates))]
} else {
bestGatePos = nearGates[0]
}
return bestGatePos
}
func twocloseGate(gate1 alg.Coord, gate2 alg.Coord) bool {
if (gate1[0] == gate2[0]) && (gate1[1] == gate2[1]+1 || gate1[1] == gate2[1]-1) {
return true
}
if (gate1[1] == gate2[1]) && (gate1[0] == gate2[0]+1 || gate1[0] == gate2[0]-1) {
return true
}
return false
}
// Normalise les valeurs d'un ensemble de portes
func normalizeGates(gates []Gate) ([]Gate, float64, float64) {
var minAgents, maxAgents float64 = math.MaxFloat64, 0
var minDistance, maxDistance float64 = math.MaxFloat64, 0
// Trouver les valeurs max et min pour la normalisation
for _, gate := range gates {
if gate.NbAgents > maxAgents {
maxAgents = gate.NbAgents
}
if gate.NbAgents < minAgents {
minAgents = gate.NbAgents
}
if gate.Distance > maxDistance {
maxDistance = gate.Distance
}
if gate.Distance < minDistance {
minDistance = gate.Distance
}
}
// Normaliser les valeurs
d_agt := (maxAgents - minAgents)
if d_agt == 0 {
d_agt = 1.0
}
d_dist := (maxDistance - minDistance)
if d_dist == 0 {
d_dist = 1.0
}
//fmt.Println("[normalizeGates] d_dist : ",d_dist)
for i := range gates {
gates[i].NbAgents = (gates[i].NbAgents - minAgents) / d_agt
//fmt.Println("[normalizeGates] gates[i].Distance : ",gates[i].Distance)
//fmt.Println("[normalizeGates] minDistance : ",minDistance)
//fmt.Println("[normalizeGates] d_dist : ",d_dist)
gates[i].Distance = (gates[i].Distance - minDistance) / d_dist
}
return gates, float64(maxAgents - minAgents), maxDistance - minDistance
}
// Calcul du score d'un Gate
func (g Gate) Score() float64 {
return g.Distance + g.NbAgents
}
// sort_by_score trie une tranche de Gates par ordre croissant de leur score
func sort_by_score(gates []Gate) {
sort.Slice(gates, func(i, j int) bool {
return gates[i].Score() < gates[j].Score() // Ordre croissant
})
}
// gates_with_highest_score renvoie une tranche de Gates ayant le score le moins élevé
func gates_with_lowest_score(gates []Gate) []Gate {
if len(gates) == 0 {
return nil
}
sort_by_score(gates) // D'abord, on trie les gates
lowestScore := gates[0].Score() // Le premier gate a le score le moins élevé après le tri
var lowestScoreGates []Gate
for _, gate := range gates {
if gate.Score() == lowestScore {
lowestScoreGates = append(lowestScoreGates, gate)
} else {
break // Puisque les gates sont triés, pas besoin de vérifier plus loin
}
}
return lowestScoreGates
}
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
upLeftCoord alg.Coord // inclus
downRightCoord alg.Coord // inclus
goToLeft bool // si vrai, le métro se déplace de droite à gauche, si faux de gauche à droite
horizontal bool
gates []alg.Coord //listes des portes associée à la voie
nearestExit []alg.Coord // Chemin vers la sortie la plus proche pour chaque porte (index vers pathsToExit)
pathsToExit [][]alg.Node
gatesClosed bool
env *Environment
}
type WayID int
func NewWay(wayId WayID, upLeftCoord, downRightCoord alg.Coord, goToLeft bool, gates []alg.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([]alg.Coord, len(gates))
pathsToExit := make([][]alg.Node, len(gates))
for index, gate := range gates {
row, col := alg.FindNearestExit(&env.exits, gate[0], gate[1])
nearestExit[index] = alg.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,
upLeftCoord: upLeftCoord,
downRightCoord: downRightCoord,
goToLeft: goToLeft,
horizontal: horizontal,
gates: gates,
nearestExit: nearestExit,
pathsToExit: pathsToExit,
env: env}
}