From c7fe4d8f95c50f3e48222a3071aa1d180ab36747 Mon Sep 17 00:00:00 2001 From: Matt Scott Date: Wed, 20 Sep 2023 18:09:24 +0100 Subject: [PATCH] tidied package code for final release - added comments to each function - added test files - changed function names --- main.go | 2 +- pkg/main/BaseAgent/agentInterface.go | 6 ++- pkg/main/BaseAgent/baseAgent.go | 24 +-------- pkg/main/messaging/agentMessenger.go | 1 + pkg/main/messaging/message.go | 20 +++---- pkg/main/messaging/message_test.go | 12 ++--- pkg/main/server/baseServer.go | 52 +++++++++++-------- pkg/main/server/serverInterface.go | 12 +++-- .../baseExtendedAgent/greetingMessage.go | 2 +- pkg/testing/testServer/server_test.go | 49 ++++++++++++++++- pkg/testing/testServer/testServer.go | 37 +++++++++---- 11 files changed, 134 insertions(+), 83 deletions(-) diff --git a/main.go b/main.go index 3b72409..6363641 100644 --- a/main.go +++ b/main.go @@ -18,5 +18,5 @@ func main() { iterations := 1 ts := testserver.New(m, iterations) - ts.RunGameLoop() + ts.Start() } diff --git a/pkg/main/BaseAgent/agentInterface.go b/pkg/main/BaseAgent/agentInterface.go index 8d99c84..64ad3fb 100644 --- a/pkg/main/BaseAgent/agentInterface.go +++ b/pkg/main/BaseAgent/agentInterface.go @@ -7,8 +7,10 @@ import ( ) type IAgent[T any] interface { + // composes messaging passing capabilities message.IAgentMessenger[T] - Activity() - UpdateAgent() + // allows agent to update their internal state + UpdateAgentInternalState() + // returns the unique ID of an agent GetID() uuid.UUID } diff --git a/pkg/main/BaseAgent/baseAgent.go b/pkg/main/BaseAgent/baseAgent.go index 2ccab3b..631a58d 100644 --- a/pkg/main/BaseAgent/baseAgent.go +++ b/pkg/main/BaseAgent/baseAgent.go @@ -1,8 +1,6 @@ package baseagent import ( - "fmt" - message "github.com/MattSScott/basePlatformSOMAS/pkg/main/messaging" "github.com/google/uuid" ) @@ -16,13 +14,7 @@ func (ba *BaseAgent[T]) GetID() uuid.UUID { return ba.id } -func (ba *BaseAgent[T]) UpdateAgent() { - fmt.Println("Updating BaseAgent...") -} - -func (ba *BaseAgent[T]) Activity() { - fmt.Printf("id: %s\n", ba.GetID()) - fmt.Println("__________________________") +func (ba *BaseAgent[T]) UpdateAgentInternalState() { } func NewAgent[T IAgent[T]]() *BaseAgent[T] { @@ -31,12 +23,6 @@ func NewAgent[T IAgent[T]]() *BaseAgent[T] { } } -// func GetAgent[T IAgent[T]]() *BaseAgent[T] { -// return &BaseAgent[T]{ -// id: uuid.New(), -// } -// } - func (ba *BaseAgent[T]) GetAllMessages() []message.IMessage[T] { return []message.IMessage[T]{} } @@ -45,14 +31,6 @@ func (ba *BaseAgent[T]) GetNetwork() []T { return ba.network } -func (ba *BaseAgent[T]) GetNetworkForMessaging() []message.IAgentMessenger[T] { - messengerArray := make([]message.IAgentMessenger[T], len(ba.network)) - for i := range ba.network { - messengerArray[i] = ba.network[i] - } - return messengerArray -} - func (ba *BaseAgent[T]) SetNetwork(newNetwork []T) { ba.network = newNetwork } diff --git a/pkg/main/messaging/agentMessenger.go b/pkg/main/messaging/agentMessenger.go index 7e24da8..8749212 100644 --- a/pkg/main/messaging/agentMessenger.go +++ b/pkg/main/messaging/agentMessenger.go @@ -2,5 +2,6 @@ package messaging // // base interface structure used for message passing - can be composed for more complex message structures type IAgentMessenger[T any] interface { + // produces a list of messages (of any common interface) that an agent wishes to pass GetAllMessages([]T) []IMessage[T] } diff --git a/pkg/main/messaging/message.go b/pkg/main/messaging/message.go index c54fc30..04895dc 100644 --- a/pkg/main/messaging/message.go +++ b/pkg/main/messaging/message.go @@ -1,16 +1,16 @@ package messaging -// import baseagent "github.com/MattSScott/basePlatformSOMAS/pkg/agents/BaseAgent" - -// base interface structure used for message passing - can be composed for more complex message structures - -// new message types extend this +// base interface structure used for message - can be composed for more complex message structures type IMessage[T any] interface { + // returns the sender of a message GetSender() T + // returns the list of agents that the message should be passed to GetRecipients() []T - Accept(T) + // calls the appropriate messsage handler method on the receiving agent + InvokeMessageHandler(T) } +// new message types can extend this type BaseMessage[T IAgentMessenger[T]] struct { sender T recipients []T @@ -24,12 +24,6 @@ func CreateMessage[T IAgentMessenger[T]](sender T, recipients []T) BaseMessage[T } } -// func CreateNullMessageWithSender(sender IAgentMessenger) BaseMessage { -// return BaseMessage{ -// sender: sender, -// } -// } - func (bm BaseMessage[T]) GetSender() T { return bm.sender } @@ -38,5 +32,5 @@ func (bm BaseMessage[T]) GetRecipients() []T { return bm.recipients } -func (bm BaseMessage[T]) Accept(agent T) { +func (bm BaseMessage[T]) InvokeMessageHandler(agent T) { } diff --git a/pkg/main/messaging/message_test.go b/pkg/main/messaging/message_test.go index 44f8f36..fe318cd 100644 --- a/pkg/main/messaging/message_test.go +++ b/pkg/main/messaging/message_test.go @@ -35,15 +35,15 @@ type ExtendedAgent struct { agentField int } -func (m1 Message1) Accept(agent IExtendedAgent) { +func (m1 Message1) InvokeMessageHandler(agent IExtendedAgent) { agent.HandleMessage1(m1) } -func (m2 Message2) Accept(agent IExtendedAgent) { +func (m2 Message2) InvokeMessageHandler(agent IExtendedAgent) { agent.HandleMessage2(m2) } -func (nm NullMessage) Accept(agent IExtendedAgent) { +func (nm NullMessage) InvokeMessageHandler(agent IExtendedAgent) { agent.HandleNullMessage(nm) } @@ -91,10 +91,10 @@ func TestMessageCanBeExtended(t *testing.T) { agent2 := &ExtendedAgent{agentField: 0} msgFromA1 := agent1.GetMessage1() - msgFromA1.Accept(agent2) + msgFromA1.InvokeMessageHandler(agent2) msgFromA2 := agent2.GetMessage2() - msgFromA2.Accept(agent1) + msgFromA2.InvokeMessageHandler(agent1) if agent1.agentField != 10 { t.Error("Message 2 not properly handled") @@ -114,7 +114,7 @@ func TestMessageSender(t *testing.T) { nullMsg := a1.GetNullMessage([]IExtendedAgent{a2, a3}) for _, recip := range nullMsg.GetRecipients() { - nullMsg.Accept(recip) + nullMsg.InvokeMessageHandler(recip) } if a1.GetAgentField() != a2.GetAgentField() || a3.GetAgentField() != a1.GetAgentField() { diff --git a/pkg/main/server/baseServer.go b/pkg/main/server/baseServer.go index 2e08f26..ec88b02 100644 --- a/pkg/main/server/baseServer.go +++ b/pkg/main/server/baseServer.go @@ -7,29 +7,38 @@ import ( ) type BaseServer[T baseagent.IAgent[T]] struct { - NumAgents int - NumTurns int - Agents []T + numTurns int + agents []T } -func (bs *BaseServer[T]) RunGameLoop() { - fmt.Printf("%d agents initialised: \n", bs.NumAgents) - for index, agent := range bs.Agents { - fmt.Printf("agent %d \n", index) - fmt.Printf("_____________________________________________ \n") - agent.UpdateAgent() - agent.Activity() - //TO DO: add the function for stages +func (bs *BaseServer[T]) GetAgents() []T { + return bs.agents +} - } +func (bs *BaseServer[T]) GetNumTurns() int { + return bs.numTurns +} +func (bs *BaseServer[T]) RunGameLoop() { + for _, agent := range bs.agents { + fmt.Printf("Agent %s updating state \n", agent.GetID()) + agent.UpdateAgentInternalState() + } } func (bs *BaseServer[T]) Start() { + fmt.Printf("Server initialised with %d agents \n", len(bs.agents)) + fmt.Print("\n") //LOOPS - for i := 0; i < bs.NumTurns; i++ { - fmt.Printf("Game Loop %d Running \n", i) + for i := 0; i < bs.numTurns; i++ { + fmt.Printf("Game Loop %d running... \n \n", i) + fmt.Printf("Main game loop running... \n \n") bs.RunGameLoop() + fmt.Printf("\nMain game loop finished. \n \n") + fmt.Printf("Messaging session started... \n \n") + bs.RunMessagingSession() + fmt.Printf("\nMessaging session completed \n \n") + fmt.Printf("Game Loop %d completed. \n", i) } } @@ -49,15 +58,15 @@ func MakeAgentGeneratorCountPair[T baseagent.IAgent[T]](generatorFunction AgentG } func (bs *BaseServer[T]) RunMessagingSession() { - for _, agent := range bs.Agents { - allMessages := agent.GetAllMessages(bs.Agents) + for _, agent := range bs.agents { + allMessages := agent.GetAllMessages(bs.agents) for _, msg := range allMessages { recipients := msg.GetRecipients() for _, recip := range recipients { if agent.GetID() == recip.GetID() { continue } - msg.Accept(recip) + msg.InvokeMessageHandler(recip) } } } @@ -75,8 +84,7 @@ func (bs *BaseServer[T]) initialiseAgents(m []AgentGeneratorCountPair[T]) { } } - bs.Agents = agents - bs.NumAgents = len(agents) + bs.agents = agents } func getNumAgents[T baseagent.IAgent[T]](pairs []AgentGeneratorCountPair[T]) int { @@ -90,10 +98,10 @@ func getNumAgents[T baseagent.IAgent[T]](pairs []AgentGeneratorCountPair[T]) int return numAgents } -func CreateServer[T baseagent.IAgent[T]](mapper []AgentGeneratorCountPair[T], numTurns int) *BaseServer[T] { - // generate the server and return it +// generate a server instance based on a mapping function and number of iterations +func CreateServer[T baseagent.IAgent[T]](mapper []AgentGeneratorCountPair[T], iterations int) *BaseServer[T] { serv := &BaseServer[T]{ - NumTurns: numTurns, + numTurns: iterations, } serv.initialiseAgents(mapper) return serv diff --git a/pkg/main/server/serverInterface.go b/pkg/main/server/serverInterface.go index 2b2a7a1..a276a0c 100644 --- a/pkg/main/server/serverInterface.go +++ b/pkg/main/server/serverInterface.go @@ -1,8 +1,14 @@ package server -type IServer interface { - // the set of functions defining a 'game loop' should run +import baseagent "github.com/MattSScott/basePlatformSOMAS/pkg/main/BaseAgent" + +type IServer[T baseagent.IAgent[T]] interface { + // the set of functions defining how a 'game loop' should run RunGameLoop() - // runs simulator + // begins simulator Start() + // gives access to the agents in the simulator + GetAgents() []T + // gives access to number of iteration in simulator + GetNumTurns() int } diff --git a/pkg/testing/testAgents/baseExtendedAgent/greetingMessage.go b/pkg/testing/testAgents/baseExtendedAgent/greetingMessage.go index e151227..6db71d8 100644 --- a/pkg/testing/testAgents/baseExtendedAgent/greetingMessage.go +++ b/pkg/testing/testAgents/baseExtendedAgent/greetingMessage.go @@ -18,7 +18,7 @@ func (em *GreetingMessage) GetGreeting() string { return em.greeting } -func (em GreetingMessage) Accept(agent IExtendedAgent) { +func (em GreetingMessage) InvokeMessageHandler(agent IExtendedAgent) { agent.HandleGreetingMessage(em) } diff --git a/pkg/testing/testServer/server_test.go b/pkg/testing/testServer/server_test.go index 36f3524..c11a03f 100644 --- a/pkg/testing/testServer/server_test.go +++ b/pkg/testing/testServer/server_test.go @@ -6,6 +6,7 @@ import ( helloagent "github.com/MattSScott/basePlatformSOMAS/pkg/testing/testAgents/helloAgent" worldagent "github.com/MattSScott/basePlatformSOMAS/pkg/testing/testAgents/worldAgent" testserver "github.com/MattSScott/basePlatformSOMAS/pkg/testing/testServer" + "github.com/google/uuid" "testing" ) @@ -20,7 +21,7 @@ func TestInheritedServer(t *testing.T) { floors := 3 ts := testserver.New(m, floors) - if len(ts.Agents) != 5 { + if len(ts.GetAgents()) != 5 { t.Error("Agents not properly instantiated") } @@ -31,3 +32,49 @@ func TestInheritedServer(t *testing.T) { ts.RunGameLoop() ts.Start() } + +func TestServerInterfaceComposition(t *testing.T) { + + m := make([]server.AgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent], 2) + + m[0] = server.MakeAgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent](helloagent.GetHelloAgent, 1) + m[1] = server.MakeAgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent](worldagent.GetWorldAgent, 1) + + // server can also be declared as type interface + var server testserver.IExtendedServer = testserver.New(m, 1) + + if len(server.GetAgents()) != 2 { + t.Error("Agents not properly instantiated") + } + + for _, agent := range server.GetAgents() { + if agent.GetID() == uuid.Nil { + t.Error("Agent types not correctly instantiated") + } + } + + server.Start() +} + +func TestServerMessagePassing(t *testing.T) { + m := make([]server.AgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent], 5) + + m[0] = server.MakeAgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent](helloagent.GetHelloAgent, 1) + m[1] = server.MakeAgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent](worldagent.GetWorldAgent, 1) + + floors := 3 + ts := testserver.New(m, floors) + + agents := ts.GetAgents() + + a1 := agents[0] + a2 := agents[1] + + messages := a1.GetAllMessages(agents) + + if len(messages) > 1 { + t.Error("Incorrect number of messages created") + } + + messages[0].InvokeMessageHandler(a2) +} diff --git a/pkg/testing/testServer/testServer.go b/pkg/testing/testServer/testServer.go index f8f852c..e944c0c 100644 --- a/pkg/testing/testServer/testServer.go +++ b/pkg/testing/testServer/testServer.go @@ -5,34 +5,49 @@ import ( "github.com/MattSScott/basePlatformSOMAS/pkg/testing/testAgents/baseExtendedAgent" ) -type MyServer struct { +// the base server functionality can be extended with 'environment specific' functions +type IExtendedServer interface { + baseServer.IServer[baseExtendedAgent.IExtendedAgent] + RunAdditionalPhase() +} + +// composing the 'base server' allows access to the pre-made interface implementations +// new fields can be added to the extended server data structure +type TestServer struct { *baseServer.BaseServer[baseExtendedAgent.IExtendedAgent] name string } -func New(mapper []baseServer.AgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent], iterations int) *MyServer { - return &MyServer{ +func New(mapper []baseServer.AgentGeneratorCountPair[baseExtendedAgent.IExtendedAgent], iterations int) *TestServer { + return &TestServer{ BaseServer: baseServer.CreateServer[baseExtendedAgent.IExtendedAgent](mapper, iterations), name: "TestServer", } } -func (s *MyServer) GetName() string { +func (s *TestServer) RunAdditionalPhase() { + for _, agent := range s.GetAgents() { + agent.GetID() + } +} + +func (s *TestServer) GetName() string { return s.name } -func (s *MyServer) RunGameLoop() { - for _, agent := range s.Agents { - agent.UpdateAgent() +// override to change the behaviour of the game loop +func (s *TestServer) RunGameLoop() { - } + // the superclass functions can be called + s.BaseServer.RunGameLoop() - // able to call methods from the parametrised subclass - for _, agent := range s.Agents { + // able to call methods from the parametrised subclass too + for _, agent := range s.GetAgents() { agent.GetPhrase() } - s.RunMessagingSession() + // additional functions can be run + s.RunAdditionalPhase() }