diff --git a/agent.go b/agent.go index 5a7d3269d8c03848cf17e690f24fa50ec5720d99..1222a62af26fcf5ef9d5c48693ee013dd7d53e8d 100644 --- a/agent.go +++ b/agent.go @@ -1,8 +1,11 @@ package simulation import ( + //"container/heap" + "fmt" "log" "math/rand" + "time" ) type Action int64 @@ -10,39 +13,81 @@ type Action int64 const ( Noop = iota Mark + Wait + Move ) -type Coord [2]float64 +type Coord [2]int +type AgentID string -type Rect [2]Coord +type Agent struct { + id AgentID + vitesse int + force int + politesse bool + coordHautOccupation Coord + coordBasOccupation Coord + departure Coord + destination Coord + behavior Behavior + env *Environment + syncChan chan int + decision int + isOn string // Contenu de la case sur laquelle il se trouve +} -type Agent interface { - Start() - Percept(*Environment) - Deliberate() - Act(*Environment) - ID() AgentID +type Behavior interface { + // TODO: Comment faire pour ne pas passer l'agent en param ? C'est possible ? + Percept(*Agent, *Environment) + Deliberate(*Agent) + Act(*Agent, *Environment) } -type AgentID string +type UsagerLambda struct{} + +func (ul *UsagerLambda) Percept(ag *Agent, env *Environment) { +} + +func (ul *UsagerLambda) Deliberate(ag *Agent) { + if ag.env.station[ag.departure[0]][ag.departure[1]] == "A" { + ag.decision = Wait + } else { + ag.decision = Move + } +} + +func (ul *UsagerLambda) Act(ag *Agent, env *Environment) { + // TODO: Je crois que la construction d'un chemin s'arrête s'il y a déjà un agent sur destination. Il faudrait donc faire en sorte de s'approcher le plus possible + if ag.decision == Move { + start := ag.coordBasOccupation + end := ag.destination + + path := findPathBFS(ag.env.station, start, end) + if len(path) > 0 && env.station[path[0][0]][path[0][1]] != "A" { + fmt.Println(ag.id, path) + ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = ag.isOn + ag.isOn = ag.env.station[path[0][0]][path[0][1]] + ag.coordBasOccupation[0] = path[0][0] + ag.coordBasOccupation[1] = path[0][1] + ag.env.station[ag.coordBasOccupation[0]][ag.coordBasOccupation[1]] = "A" + } + vitesseInSeconds := int(ag.vitesse) + // Multiply the vitesse by time.Second + sleepDuration := time.Duration(vitesseInSeconds) * time.Second + time.Sleep(sleepDuration) + } -type AgentPI struct { - id AgentID - rect Rect - decision Action - env *Environment - syncChan chan int } -func NewAgentPI(id string, env *Environment, syncChan chan int) *AgentPI { - return &AgentPI{AgentID(id), Rect{Coord{0, 0}, Coord{1, 1}}, Noop, env, syncChan} +func NewAgent(id string, env *Environment, syncChan chan int, vitesse int, force int, politesse bool, UpCoord Coord, DownCoord Coord, behavior Behavior, departure, destination Coord) *Agent { + return &Agent{AgentID(id), vitesse, force, politesse, UpCoord, DownCoord, departure, destination, behavior, env, syncChan, Noop, env.station[UpCoord[0]][UpCoord[1]]} } -func (ag *AgentPI) ID() AgentID { +func (ag *Agent) ID() AgentID { return ag.id } -func (ag *AgentPI) Start() { +func (ag *Agent) Start() { log.Printf("%s starting...\n", ag.id) go func() { @@ -50,21 +95,19 @@ func (ag *AgentPI) Start() { var step int for { step = <-ag.syncChan - - ag.Percept(env) - ag.Deliberate() - ag.Act(env) - + ag.behavior.Percept(ag, env) + ag.behavior.Deliberate(ag) + ag.behavior.Act(ag, env) ag.syncChan <- step } }() } -func (ag *AgentPI) Percept(env *Environment) { - ag.rect = env.Rect() +func (ag *Agent) Percept(env *Environment) { + //ag.rect = env.Rect() } -func (ag *AgentPI) Deliberate() { +func (ag *Agent) Deliberate() { if rand.Float64() < 0.1 { ag.decision = Noop } else { @@ -72,12 +115,161 @@ func (ag *AgentPI) Deliberate() { } } -func (ag *AgentPI) Act(env *Environment) { +func (ag *Agent) Act(env *Environment) { if ag.decision == Noop { env.Do(Noop, Coord{}) - } else { - x := rand.Float64()*(ag.rect[1][0]-ag.rect[0][0]) + ag.rect[0][0] - y := rand.Float64()*(ag.rect[1][1]-ag.rect[0][1]) + ag.rect[0][1] - env.Do(Mark, Coord{x, y}) } } + +// ================================================================================ + +/* + * Utilisation de l'algorithme BFS pour les déplacements. Beaucoup plus rapide que A* + * + * + */ +func findPathBFS(matrix [20][20]string, start, end Coord) []Coord { + queue := []Coord{start} + visited := make(map[Coord]bool) + parents := make(map[Coord]Coord) + + for len(queue) > 0 { + current := queue[0] + queue = queue[1:] + + if current == end { + // Construire le chemin à partir des parents + path := []Coord{current} + for parent, ok := parents[current]; ok; parent, ok = parents[parent] { + path = append([]Coord{parent}, path...) + } + return path[1:] + } + + visited[current] = true + + neighbors := getNeighborsBFS(matrix, current) + for _, neighbor := range neighbors { + if !visited[neighbor] { + parents[neighbor] = current + queue = append(queue, neighbor) + } + } + } + + return nil // Aucun chemin trouvé +} + +func getNeighborsBFS(matrix [20][20]string, current Coord) []Coord { + neighbors := make([]Coord, 0) + + // Déplacements possibles : haut, bas, gauche, droite + possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} + + for _, move := range possibleMoves { + newRow, newCol := current[0]+move[0], current[1]+move[1] + + // Vérifier si la nouvelle position est valide et non visitée + if newRow >= 0 && newRow < len(matrix) && newCol >= 0 && newCol < len(matrix[0]) && (matrix[newRow][newCol] == "_" || matrix[newRow][newCol] == "B") { + neighbors = append(neighbors, Coord{newRow, newCol}) + } + } + + return neighbors +} + +/* +type Node struct { + row, col, cost, heuristic int +} + +type PriorityQueue []*Node + +func (pq PriorityQueue) Len() int { return len(pq) } + +func (pq PriorityQueue) Less(i, j int) bool { + return (pq[i].cost + pq[i].heuristic) < (pq[j].cost + pq[j].heuristic) +} + +func (pq PriorityQueue) Swap(i, j int) { + pq[i], pq[j] = pq[j], pq[i] +} + +func (pq *PriorityQueue) Push(x interface{}) { + item := x.(*Node) + *pq = append(*pq, item) +} + +func (pq *PriorityQueue) Pop() interface{} { + old := *pq + n := len(old) + item := old[n-1] + *pq = old[0 : n-1] + return item +} + +func findPath(matrix [20][20]string, start, end Node) []Node { + pq := make(PriorityQueue, 0) + heap.Init(&pq) + + heap.Push(&pq, &start) + visited := make(map[Node]bool) + parents := make(map[Node]Node) + + for pq.Len() > 0 { + current := heap.Pop(&pq).(*Node) + + if current.row == end.row && current.col == end.col { + // Construire le chemin à partir des parents + path := []Node{*current} + for parent, ok := parents[*current]; ok; parent, ok = parents[parent] { + path = append([]Node{parent}, path...) + } + + return path[1:] + } + + visited[*current] = true + + neighbors := getNeighbors(matrix, *current) + for _, neighbor := range neighbors { + if !visited[*neighbor] { + parents[*neighbor] = *current + heap.Push(&pq, neighbor) + } + } + } + + return nil // Aucun chemin trouvé +} + +func getNeighbors(matrix [20][20]string, current Node) []*Node { + neighbors := make([]*Node, 0) + + // Déplacements possibles : haut, bas, gauche, droite + possibleMoves := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} + + for _, move := range possibleMoves { + newRow, newCol := current.row+move[0], current.col+move[1] + + // Vérifier si la nouvelle position est valide et non visitée + if newRow >= 0 && newRow < len(matrix) && newCol >= 0 && newCol < len(matrix[0]) && (matrix[newRow][newCol] == "_" || matrix[newRow][newCol] == "B") { + neighbors = append(neighbors, &Node{newRow, newCol, current.cost + 1, heuristic(newRow, newCol, current, current)}) + } + } + + return neighbors +} + +func heuristic(row, col int, current, end Node) int { + // Heuristique simple : distance de Manhattan + return abs(row-end.row) + abs(col-end.col) +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} +*/ diff --git a/cmd/simu/main.go b/cmd/simu/main.go index 1fd983b2d04a4f1c253b2b63b22f67602fbb597a..bac00185ea04403b2c5d9c54aaab9934eb55e5da 100644 --- a/cmd/simu/main.go +++ b/cmd/simu/main.go @@ -6,7 +6,7 @@ import ( ) func main() { - s := simulation.NewSimulation(100, -1, 600*time.Second) + s := simulation.NewSimulation(3, -1, 600*time.Second) go simulation.StartAPI(s) s.Run() } diff --git a/env.go b/env.go index 55950a82a2745b095a874336a5a4319d7b2bfad2..05fe4b1a5565ed980838b285385a1891b0cbdbba 100644 --- a/env.go +++ b/env.go @@ -5,17 +5,16 @@ import ( "sync" ) + type Environment struct { sync.RWMutex ags []Agent agentCount int - in uint64 - out uint64 - noopCount uint64 + station [20][20]string } -func NewEnvironment(ags []Agent) (env *Environment) { - return &Environment{ags: ags, agentCount: len(ags)} +func NewEnvironment(ags []Agent, carte [20][20]string) (env *Environment) { + return &Environment{ags: ags, agentCount: len(ags),station : carte} } func (env *Environment) AddAgent(agt Agent) { @@ -33,15 +32,9 @@ func (env *Environment) Do(a Action, c Coord) (err error) { return fmt.Errorf("bad coordinates (%f,%f)", c[0], c[1]) } - if c[0]*c[0]+c[1]*c[1] <= 1 { - env.in++ - } else { - env.out++ - } return nil case Noop: - env.noopCount++ return nil } @@ -52,9 +45,9 @@ func (env *Environment) PI() float64 { env.RLock() defer env.RUnlock() - return 4 * float64(env.in) / (float64(env.out) + float64(env.in)) + return 4 } -func (env *Environment) Rect() Rect { - return Rect{Coord{0, 0}, Coord{1, 1}} +func (env *Environment) Rect() Coord { + return Coord{0, 0} } diff --git a/simu.go b/simu.go index d197e93e8c52163e38c24f5a8814a4979aefb839..6a4597576c15217fb1ff5f6962f41df8eba3249b 100644 --- a/simu.go +++ b/simu.go @@ -7,8 +7,40 @@ import ( "time" ) +// Déclaration de la matrice +/* + * X : Mur, zone inatteignable + * E : Entrée + * S : Sortie + * W : Entrée et Sortie + * Q : Voie + * _ : Couloir, zon + * B: Bridge/Pont, zone accessible + */ +var carte [20][20]string = [20][20]string{ + {"X", "X", "X", "X", "X", "X", "X", "X", "W", "W", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}, + {"X", "X", "X", "X", "X", "X", "X", "X", "_", "_", "X", "X", "X", "X", "X", "X", "_", "_", "_", "X"}, + {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, + {"_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_"}, + {"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"}, +} + type Simulation struct { - env Environment // pb de copie avec les locks... + env Environment agents []Agent maxStep int maxDuration time.Duration @@ -23,23 +55,23 @@ func NewSimulation(agentCount int, maxStep int, maxDuration time.Duration) (simu simu.maxStep = maxStep simu.maxDuration = maxDuration - simu.env = *NewEnvironment([]Agent{}) + simu.env = *NewEnvironment([]Agent{}, carte) // création des agents et des channels for i := 0; i < agentCount; i++ { // création de l'agent id := fmt.Sprintf("Agent #%d", i) syncChan := make(chan int) - ag := NewAgentPI(id, &simu.env, syncChan) - + ag := NewAgent(id, &simu.env, syncChan, 1, 0, true, Coord{0, 8+i%2}, Coord{0, 8+i%2}, &UsagerLambda{},Coord{0, 8+i%2}, Coord{12-4*(i%2),18-15*(i%2)}) + fmt.Println(ag.id,ag.departure,ag.destination) // ajout de l'agent à la simulation - simu.agents = append(simu.agents, ag) + simu.agents = append(simu.agents, *ag) // ajout du channel de synchro simu.syncChans.Store(ag.ID(), syncChan) // ajout de l'agent à l'environnement - ag.env.AddAgent(ag) + ag.env.AddAgent(*ag) } return simu @@ -54,13 +86,19 @@ func (simu *Simulation) Run() { go simu.Print() // Démarrage des agents + // Démarrage des agents + var wg sync.WaitGroup for _, agt := range simu.agents { - agt.Start() - } + wg.Add(1) + go func(agent Agent) { + defer wg.Done() + agent.Start() + }(agt) +} // On sauvegarde la date du début de la simulation simu.start = time.Now() - + // Lancement de l'orchestration de tous les agents // simu.step += 1 // plus de sens for _, agt := range simu.agents { @@ -78,13 +116,17 @@ func (simu *Simulation) Run() { time.Sleep(simu.maxDuration) - log.Printf("Fin de la simulation [step: %d, in: %d, out: %d, π: %f]", simu.step, simu.env.in, simu.env.out, simu.env.PI()) + log.Printf("Fin de la simulation [step: %d, in: %d, out: %d, π: %f]", simu.step, simu.env.PI()) } func (simu *Simulation) Print() { for { - fmt.Printf("\rπ = %.30f", simu.env.PI()) - time.Sleep(time.Second / 60) // 60 fps ! + for i := 0; i < 20; i++ { + fmt.Println(simu.env.station[i]) + } + //time.Sleep(time.Second / 60) // 60 fps ! + time.Sleep(time.Second) // 1 fps ! + //fmt.Print("\033[H\033[2J") // effacement du terminal } }