Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Refactoring to allow multiple groups of strategies that can work toge…

…ther to build a final list of outcomes
  • Loading branch information...
commit 58e3235e3ced8e62c7dcbcc52dca571214fde767 1 parent a4e28e9
Chris Turner authored
12 src/main/scala/scalatron/botwar/botPlugin/configuration/BotConfig.scala
... ... @@ -1,9 +1,17 @@
1 1 package scalatron.botwar.botPlugin.configuration
2 2
3   -import com.typesafe.config.{ConfigValue, Config}
  3 +import scala.collection.JavaConverters._
  4 +import com.typesafe.config.{ConfigList, ConfigValue, Config}
  5 +import java.util.Map.Entry
4 6
5 7 case class BotConfig(apocalypse: Int, round: Int, config: Config) {
6   - def strategyNames = (strategyConfigValues map (_.unwrapped().toString)).toList
  8 + def strategyGroups: Map[String, List[String]] = {
  9 + def strategies = config.getConfig("bot.strategies").entrySet().asScala
  10 + def values(entry: Entry[String, ConfigValue]) = entry.getValue.asInstanceOf[ConfigList].toArray(Array[ConfigValue]())
  11 + def stringValues(entry: Entry[String, ConfigValue]) = (values(entry) map (_.unwrapped().toString)).toList
  12 +
  13 + (strategies map (entry => (entry.getKey, stringValues(entry)))).toMap
  14 + }
7 15
8 16 private def strategyConfigValues = config.getList("bot.strategies").toArray(Array[ConfigValue]())
9 17 }
5 src/main/scala/scalatron/botwar/botPlugin/domain/Outcome.scala
@@ -54,11 +54,12 @@ case class OutcomeResult(name: String,
54 54 }
55 55
56 56 object Outcome {
57   - def asResult(name: String, sequenceGenerator: Stream[Int], outcomes: Set[Outcome]): OutcomeResult = {
  57 + def asResult(name: String, sequenceGenerator: Stream[Int], outcomes: Set[Outcome]): Option[OutcomeResult] = {
58 58 def encodeOutcome(result: OutcomeResult, outcome: Outcome) = outcome.encode(result)
59 59 def compatibility(forMaster: Boolean)(outcome: Outcome) =
60 60 if ( forMaster ) outcome.compatibleWithMaster else outcome.compatibleWithMiniBot
61 61
62   - (outcomes filter compatibility(name == "Master")).foldLeft(OutcomeResult(name, sequenceGenerator))(encodeOutcome)
  62 + if ( outcomes.isEmpty ) None
  63 + else Some((outcomes filter compatibility(name == "Master")).foldLeft(OutcomeResult(name, sequenceGenerator))(encodeOutcome))
63 64 }
64 65 }
21 src/main/scala/scalatron/botwar/botPlugin/responders/BotControlResponder.scala
... ... @@ -1,8 +1,8 @@
1 1 package scalatron.botwar.botPlugin.responders
2 2
3 3 import scalatron.botwar.botPlugin.protocol._
4   -import scalatron.botwar.botPlugin.domain.{Outcome, Request, DeltaOffset}
5 4 import scalatron.botwar.botPlugin.strategies.StrategyChain
  5 +import scalatron.botwar.botPlugin.domain.{OutcomeResult, Outcome, Request, DeltaOffset}
6 6
7 7
8 8 case class BotControlResponder(environment: BotEnvironment,
@@ -25,14 +25,14 @@ case class BotControlResponder(environment: BotEnvironment,
25 25 offset: Option[DeltaOffset] = None): BotResponder = {
26 26 def lift[T](f: => T): Option[T] = Some(f)
27 27
28   - val result = for (
29   - req <- lift(Request(name, time, energy, view, offset, environment.trackedState));
30   - strategy <- forRequest(environment.strategies, req);
31   - outcomes <- lift(strategy.apply(req));
32   - result <- lift(Outcome.asResult(req.context.name, environment.sequenceGenerator, outcomes))
33   - ) yield result
  28 + val req = Request(name, time, energy, view, offset, environment.trackedState)
  29 + val allOutcomes = (for (
  30 + strategyGroup <- environment.strategies;
  31 + strategy <- forRequest(strategyGroup, req);
  32 + outcomes <- lift(strategy.apply(req))
  33 + ) yield outcomes).flatten
34 34
35   - result map { r =>
  35 + Outcome.asResult(req.context.name, environment.sequenceGenerator, allOutcomes) map { r =>
36 36 val nextEnv = (r.newMiniBots.foldLeft(environment) { (env, bot) =>
37 37 env.updateTrackedState(bot._1, bot._2)
38 38 }) updateTrackedState (r.name, r.trackedState) replace r.sequenceGenerator
@@ -40,4 +40,9 @@ case class BotControlResponder(environment: BotEnvironment,
40 40 BotControlResponder(nextEnv, r.actions.toIndexedSeq)
41 41 } getOrElse this
42 42 }
  43 +
  44 + case class StrategyOutcome(name: String = "", outcomes: Set[Outcome] = Set()) {
  45 + def append(outcome: StrategyOutcome) = StrategyOutcome(outcome.name, outcome.outcomes ++ outcomes)
  46 + def lift: Option[StrategyOutcome] = if ( name == "") None else Some(this)
  47 + }
43 48 }
2  src/main/scala/scalatron/botwar/botPlugin/responders/BotEnvironment.scala
@@ -4,7 +4,7 @@ import scalatron.botwar.botPlugin.configuration.BotConfig
4 4 import scalatron.botwar.botPlugin.strategies.Strategy
5 5
6 6 case class BotEnvironment(botConfig: BotConfig,
7   - strategies: List[Strategy#StrategyFunction],
  7 + strategies: Set[List[Strategy#StrategyFunction]],
8 8 trackedState: Map[String, Map[String, String]] = Map.empty,
9 9 sequenceGenerator: Stream[Int] = Stream.from(1)) {
10 10 def updateTrackedState(name: String, entries: Map[String, String]) =
2  src/main/scala/scalatron/botwar/botPlugin/responders/InitialisingResponder.scala
@@ -10,7 +10,7 @@ case object InitialisingResponder extends BotResponder with Configuration with S
10 10 protected def forCommand(command: Command): Option[BotResponder] = command match {
11 11 case Welcome(_, path, apocalypse, round) => {
12 12 val botConfig = configure(path, apocalypse, round)
13   - val strategies = createStrategies(botConfig)
  13 + val strategies = createStrategyGroups(botConfig)
14 14 Some(BotControlResponder(BotEnvironment(botConfig, strategies)))
15 15 }
16 16 case _ => None
5 src/main/scala/scalatron/botwar/botPlugin/strategies/StrategyChain.scala
@@ -4,11 +4,12 @@ import scalatron.botwar.botPlugin.domain.Request
4 4 import scalatron.botwar.botPlugin.configuration.BotConfig
5 5
6 6 trait StrategyChain {
  7 +
7 8 def forRequest(strategies: List[Strategy#StrategyFunction], request: Request): Option[Strategy#StrategyFunction] =
8 9 strategies find (_.isDefinedAt(request))
9 10
10   - def createStrategies(botConfig: BotConfig): List[Strategy#StrategyFunction] =
11   - botConfig.strategyNames map instantiate(botConfig)
  11 + def createStrategyGroups(botConfig: BotConfig): Set[List[Strategy#StrategyFunction]] =
  12 + (botConfig.strategyGroups map (_._2 map instantiate(botConfig))).toSet
12 13
13 14 private def instantiate(botConfig: BotConfig)(strategyName: String) = try {
14 15 val className = "scalatron.botwar.botPlugin.strategies." + strategyName
4 src/test/resources/bot.conf
... ... @@ -1,3 +1,5 @@
1 1 bot {
2   - strategies = [ RandomMovementStrategy ]
  2 + strategies = {
  3 + movement = [ RandomMovementStrategy ]
  4 + }
3 5 }
6 src/test/resources/configtest/bot.conf
... ... @@ -0,0 +1,6 @@
  1 +bot {
  2 + strategies = {
  3 + movement = [ RandomMovementStrategy ]
  4 + others = [ NoOpStrategy ]
  5 + }
  6 +}
12 src/test/scala/scalatron/botwar/botPlugin/configuration/ConfigurationSpec.scala
@@ -8,15 +8,23 @@ class ConfigurationSpec extends Specification with Configuration { def is =
8 8 endp^
9 9 "A Configuration should" ^
10 10 "load the bot configuration file" ! loadConfig^
  11 + "define the different strategy groups" ! strategyGroups^
11 12 end
12 13
13 14 def loadConfig = {
14   - val botConfig = configure("src/test/resources", 5000, 1)
  15 + val botConfig = configure("src/test/resources/configtest", 5000, 1)
15 16
16 17 (botConfig.apocalypse must_== 5000) and
17 18 (botConfig.round must_== 1) and
18   - (botConfig.strategyNames must_== ("RandomMovementStrategy" :: Nil)) and
19 19 (botConfig.config must not beNull)
20 20 }
  21 +
  22 + def strategyGroups = {
  23 + val botConfig = configure("src/test/resources/configtest", 5000, 1)
  24 +
  25 + (botConfig.strategyGroups.keys must containAllOf(Seq("movement", "others"))) and
  26 + (botConfig.strategyGroups.apply("movement") must_== ("RandomMovementStrategy" :: Nil)) and
  27 + (botConfig.strategyGroups.apply("others") must_== ("NoOpStrategy" :: Nil))
  28 + }
21 29 }
22 30
11 src/test/scala/scalatron/botwar/botPlugin/domain/OutcomeSpec.scala
@@ -21,6 +21,7 @@ class OutcomeSpec extends Specification { def is =
21 21 "be convertable into a result" ! asResult^
22 22 "exclude outcomes not compatible with a master" ! masterCompatibility^
23 23 "exclude outcomes not compatible with a mini-bot" ! miniBotCompatibility^
  24 + "not convert to a result if empty" ! emptyOutcomes^
24 25 end
25 26
26 27 def encodeMove = {
@@ -79,7 +80,7 @@ class OutcomeSpec extends Specification { def is =
79 80 }
80 81
81 82 def asResult = {
82   - val result = Outcome.asResult("Master", Stream.from(1), Set(MoveOutcome(DeltaOffset(1, -1)), SayOutcome("Test")))
  83 + val result = Outcome.asResult("Master", Stream.from(1), Set(MoveOutcome(DeltaOffset(1, -1)), SayOutcome("Test"))).get
83 84
84 85 (result.name must_== "Master") and
85 86 (result.sequenceGenerator.head must_== 1) and
@@ -94,7 +95,7 @@ class OutcomeSpec extends Specification { def is =
94 95 SayOutcome("say"),
95 96 UpdateRunningState("1", Map.empty),
96 97 UpdateTrackedState(Map("baz" -> "BAZ")))
97   - val result = Outcome.asResult("Master", Stream.from(1), allOutcomes)
  98 + val result = Outcome.asResult("Master", Stream.from(1), allOutcomes).get
98 99 (result.actions must haveSize(4)) and
99 100 (result.actions must containAllOf(Seq(Move(1, -1),
100 101 Spawn(1, -1, "1:foo/FOO", 100),
@@ -112,7 +113,7 @@ class OutcomeSpec extends Specification { def is =
112 113 SayOutcome("say"),
113 114 UpdateRunningState("1", Map("bam" -> "BAM")),
114 115 UpdateTrackedState(Map("baz" -> "BAZ")))
115   - val result = Outcome.asResult("1", Stream.from(1), allOutcomes)
  116 + val result = Outcome.asResult("1", Stream.from(1), allOutcomes).get
116 117 (result.actions must haveSize(5)) and
117 118 (result.actions must containAllOf(Seq(Move(1, -1),
118 119 Explode(5),
@@ -122,5 +123,9 @@ class OutcomeSpec extends Specification { def is =
122 123 (result.trackedState must contain(("baz" -> "BAZ"))) and
123 124 (result.newMiniBots must beEmpty)
124 125 }
  126 +
  127 + def emptyOutcomes = {
  128 + Outcome.asResult("1", Stream.from(1), Set.empty) must_== None
  129 + }
125 130 }
126 131
8 src/test/scala/scalatron/botwar/botPlugin/responders/BotControlResponderSpec.scala
@@ -35,7 +35,7 @@ class BotControlResponderSpec extends Specification {def is =
35 35 val configEntries = Map(("bot.strategies" -> (Nil).asJava)).asJava
36 36 val config = ConfigFactory.parseMap(configEntries)
37 37
38   - val environment = BotEnvironment(BotConfig(5000, 1, config), Nil)
  38 + val environment = BotEnvironment(BotConfig(5000, 1, config), Set.empty)
39 39 val responder = BotControlResponder(environment)
40 40
41 41 responder.response must_== ""
@@ -45,7 +45,7 @@ class BotControlResponderSpec extends Specification {def is =
45 45 val configEntries = Map(("bot.strategies" -> (Nil).asJava)).asJava
46 46 val config = ConfigFactory.parseMap(configEntries)
47 47
48   - val environment = BotEnvironment(BotConfig(5000, 1, config), Nil)
  48 + val environment = BotEnvironment(BotConfig(5000, 1, config), Set.empty)
49 49 val responder = BotControlResponder(environment, Vector(Move(1, -1), Say("Test")))
50 50
51 51 responder.response must_== "Move(dx=1,dy=-1)|Say(text=Test)"
@@ -55,7 +55,7 @@ class BotControlResponderSpec extends Specification {def is =
55 55 val configEntries = Map(("bot.strategies" -> (Nil).asJava)).asJava
56 56 val config = ConfigFactory.parseMap(configEntries)
57 57
58   - val environment = BotEnvironment(BotConfig(5000, 1, config), Nil)
  58 + val environment = BotEnvironment(BotConfig(5000, 1, config), Set.empty)
59 59 val responder = BotControlResponder(environment, Vector(Move(1, -1), Say("Test")))
60 60
61 61 responder.respond("Welcome(name=Test,path=src/test/resources,apocalypse=5000,round=1)") must_== responder
@@ -65,7 +65,7 @@ class BotControlResponderSpec extends Specification {def is =
65 65 val configEntries = Map(("bot.strategies" -> (Nil).asJava)).asJava
66 66 val config = ConfigFactory.parseMap(configEntries)
67 67
68   - val environment = BotEnvironment(BotConfig(5000, 1, config), Nil)
  68 + val environment = BotEnvironment(BotConfig(5000, 1, config), Set.empty)
69 69 val responder = BotControlResponder(environment, Vector(Move(1, -1), Say("Test")))
70 70
71 71 responder.respond("Goodbye(energy=1000)") must_== responder
3  src/test/scala/scalatron/botwar/botPlugin/responders/InitialisingResponderSpec.scala
@@ -24,8 +24,9 @@ class InitialisingResponderSpec extends Specification { def is =
24 24 val responder = InitialisingResponder.respond("Welcome(name=Test,path=src/test/resources,apocalypse=5000,round=1)").asInstanceOf[BotControlResponder]
25 25
26 26 (responder.lastActions must beEmpty) and
27   - (responder.environment.botConfig.strategyNames must_== "RandomMovementStrategy" :: Nil) and
  27 + (responder.environment.botConfig.strategyGroups must_== Map("movement" -> ("RandomMovementStrategy" :: Nil))) and
28 28 (responder.environment.strategies must haveSize(1)) and
  29 + (responder.environment.strategies.head must haveSize(1)) and
29 30 (responder.environment.trackedState must_== Map.empty[String, Map[String, String]]) and
30 31 (responder.environment.sequenceGenerator.head must_== 1)
31 32 }
26 src/test/scala/scalatron/botwar/botPlugin/strategies/StrategyChainSpec.scala
@@ -18,37 +18,39 @@ class StrategyChainSpec extends Specification with StrategyChain { def is =
18 18 end
19 19
20 20 def strategyCreation = {
21   - val configEntries = Map(("bot.strategies" -> ("TestStrategy1" :: "TestStrategy2" :: Nil).asJava)).asJava
  21 + val configEntries = Map("bot.strategies" -> Map("test" -> ("TestStrategy1" :: "TestStrategy2" :: Nil).asJava).asJava).asJava
22 22 val config = ConfigFactory.parseMap(configEntries)
23   - val strategies = createStrategies(new BotConfig(5000, 1, config))
  23 + val strategies = createStrategyGroups(new BotConfig(5000, 1, config))
24 24
25   - strategies must haveSize(2)
  25 + (strategies must haveSize(1)) and
  26 + (strategies.head must haveSize(2))
26 27 }
27 28
28 29 def installNoOpStrategy = {
29   - val configEntries = Map(("bot.strategies" -> ("FooBar" :: Nil).asJava)).asJava
  30 + val configEntries = Map("bot.strategies" -> Map("test" -> ("FooBar" :: Nil).asJava).asJava).asJava
30 31 val config = ConfigFactory.parseMap(configEntries)
31   - val strategies = createStrategies(new BotConfig(5000, 1, config))
  32 + val strategies = createStrategyGroups(new BotConfig(5000, 1, config))
32 33
33   - strategies must haveSize(1)
  34 + (strategies must haveSize(1)) and
  35 + (strategies.head must haveSize(1))
34 36 }
35 37
36 38 def findStrategy = {
37   - val configEntries = Map(("bot.strategies" -> ("TestStrategy1" :: "TestStrategy2" :: Nil).asJava)).asJava
  39 + val configEntries = Map("bot.strategies" -> Map("test" -> ("TestStrategy1" :: "TestStrategy2" :: Nil).asJava).asJava).asJava
38 40 val config = ConfigFactory.parseMap(configEntries)
39   - val strategies = createStrategies(new BotConfig(5000, 1, config))
  41 + val strategies = createStrategyGroups(new BotConfig(5000, 1, config))
40 42 val request = Request("Master", 1, 100, "____M____", None, Map.empty)
41 43
42   - forRequest(strategies, request) must beSome[Strategy#StrategyFunction]
  44 + forRequest(strategies.head, request) must beSome[Strategy#StrategyFunction]
43 45 }
44 46
45 47 def noSuitableStrategy = {
46   - val configEntries = Map(("bot.strategies" -> ("TestStrategy1" :: "TestStrategy2" :: Nil).asJava)).asJava
  48 + val configEntries = Map("bot.strategies" -> Map("test" -> ("TestStrategy1" :: "TestStrategy2" :: Nil).asJava).asJava).asJava
47 49 val config = ConfigFactory.parseMap(configEntries)
48   - val strategies = createStrategies(new BotConfig(5000, 1, config))
  50 + val strategies = createStrategyGroups(new BotConfig(5000, 1, config))
49 51 val request = Request("1:", 1, 100, "____M____", None, Map.empty)
50 52
51   - forRequest(strategies, request) must_== None
  53 + forRequest(strategies.head, request) must_== None
52 54 }
53 55 }
54 56

0 comments on commit 58e3235

Please sign in to comment.
Something went wrong with that request. Please try again.