1
+ package scalatron .core
2
+
1
3
/** This material is intended as a community resource and is licensed under the
2
4
* Creative Commons Attribution 3.0 Unported License. Feel free to use, modify and share it.
3
5
*/
4
- package scalatron .botwar
5
6
6
7
import akka .util .Duration
7
8
import akka .dispatch .{ExecutionContext , Future , Await }
8
9
import akka .actor .ActorSystem
9
- import scalatron .core .Plugin
10
10
11
11
12
12
/** Traits for generic simulations, of which a game like BotWar is an example.
13
13
*/
14
14
object Simulation
15
15
{
16
+ type Time = Long
17
+
18
+ object Time
19
+ {
20
+ val MaxValue = Long .MaxValue
21
+ val SomtimeInThePast = - 1
22
+ }
23
+
24
+
25
+ /** Base trait for entities (bots, etc.) exposed by a simulation. */
26
+ trait Entity {
27
+ /** Returns the unique ID of this entity. */
28
+ def id : Int
29
+
30
+ /** Returns the unique name of this entity, e.g. "Master" or "Slave_12345". */
31
+ def name : String
32
+
33
+ /** Returns true if this entity is a master (bot), false if it is a slave (mini-bot). */
34
+ def isMaster : Boolean
35
+
36
+ /** Returns the input string that was most recently received by the control function of
37
+ * this entity. Note that for Master bots this may be out of date, since they are only
38
+ * called every second cycle. */
39
+ def mostRecentControlFunctionInput : String
40
+
41
+ /** Returns the list of commands that was most recently returned by the control function
42
+ * of this entity. Note that for Master bots this may be out of date, since they are only
43
+ * called every second cycle. Also note that this is NOT the command string returned by
44
+ * the bot; rather, it is the collection of commands actually recognized and accepted by
45
+ * the server. The returned structure is: Iterable[(opcode,Iterable[(paramName,value])]
46
+ */
47
+ def mostRecentControlFunctionOutput : Iterable [(String , Iterable [(String , String )])]
48
+
49
+ /** Returns the debug log output most recently made by the bot. */
50
+ def debugOutput : String
51
+ }
52
+
53
+
54
+
16
55
/** Simulation.UntypedState: non-polymorphic base trait for State that simplifies passing State to contexts
17
56
* where we don't want to introduce the types of S and R.
18
57
*/
19
58
trait UntypedState
59
+ {
60
+ /** @return the time value associated with this simulation state; a monotonically increasing, zero-based, Long integer step counter. */
61
+ def time : Time
62
+
63
+ /** Advances the simulation one step and returns either an updated state or a result.
64
+ * @param actorSystem the actor system to use for trusted computation.
65
+ * @param executionContextForUntrustedCode the execution context to use for untrusted computation.
66
+ * @return either an updated state or a result.
67
+ */
68
+ def step (actorSystem : ActorSystem , executionContextForUntrustedCode : ExecutionContext ): Either [UntypedState ,TournamentRoundResult ]
69
+
70
+ /** Returns a collection containing all entities controlled by the control function implemented in the plug-in
71
+ * (and thus associated with the player) with the given name. */
72
+ def entitiesOfPlayer (name : String ) : Iterable [Entity ]
73
+ }
74
+
75
+
20
76
21
77
/** Simulation.State: base traits for simulation state implementations.
22
78
* @tparam S type of the simulation state implementation (extends Simulation.State)
23
- * @tparam R type of the result returned by the simulator (arbitrary)
24
79
*/
25
- trait State [S <: State [S , R ], R ] extends UntypedState {
26
- def step (actorSystem : ActorSystem , executionContextForUntrustedCode : ExecutionContext ) : Either [S , R ]
80
+ trait State [S <: State [S ]] extends UntypedState
81
+ {
82
+ def step (actorSystem : ActorSystem , executionContextForUntrustedCode : ExecutionContext ): Either [S ,TournamentRoundResult ]
27
83
}
28
84
29
85
30
86
/** Simulation.Factory: base traits for simulation state factory implementations.
31
87
* @tparam S type of the simulation state implementation (extends Simulation.State)
32
- * @tparam R type of the result returned by the simulator (arbitrary)
33
88
*/
34
- trait Factory [S <: State [S , R ], R ] {
35
- def createInitialState (randomSeed : Int , plugins : Iterable [Plugin .FromJarFile ])(executionContextForUntrustedCode : ExecutionContext ) : S
89
+ trait Factory [S <: State [S ]]
90
+ {
91
+ def createInitialState (randomSeed : Int , plugins : Iterable [Plugin .FromJarFile ])(executionContextForUntrustedCode : ExecutionContext ): S
36
92
}
37
93
38
94
/** Simulation.Runner: a generic runner for simulations that uses .step() to iteratively
39
95
* compute new simulation states.
40
96
* @tparam S type of the simulation state implementation (extends Simulation.State)
41
- * @tparam R type of the result returned by the simulator (arbitrary)
42
97
*/
43
- case class Runner [S <: State [S , R ], R ](
44
- factory : Factory [S , R ],
98
+ case class Runner [S <: State [S ] ](
99
+ factory : Factory [S ],
45
100
stepCallback : S => Boolean , // callback invoked at end of every simulation step; if it returns false, the sim terminates without result
46
- resultCallback : (S , R ) => Unit ) // callback invoked after the end of very last simulation step
101
+ resultCallback : (S ,TournamentRoundResult ) => Unit ) // callback invoked after the end of very last simulation step
47
102
{
48
103
/** @param plugins the collection of external plug-ins to bring into the simulation
49
104
* @param randomSeed the random seed to use for initializing the simulation
@@ -57,28 +112,28 @@ object Simulation
57
112
)(
58
113
actorSystem : ActorSystem ,
59
114
executionContextForUntrustedCode : ExecutionContext
60
- ): Option [R ] =
115
+ ): Option [TournamentRoundResult ] =
61
116
{
62
117
var currentState = factory.createInitialState(randomSeed, plugins)(executionContextForUntrustedCode) // state at entry of loop turn
63
- var priorStateOpt : Option [S ] = None // result of state.step() at exit of prior loop turn
64
- var finalResult : Option [R ] = None
118
+ var priorStateOpt : Option [S ] = None // result of state.step() at exit of prior loop turn
119
+ var finalResult : Option [TournamentRoundResult ] = None
65
120
66
121
var running = true
67
- while ( running ) {
122
+ while (running) {
68
123
// we'll use Akka Futures to compute the next state concurrently with the callback on the prior state.
69
124
// the callback will generally render the prior state to the screen.
70
125
71
126
// process state update, returns either next state or result
72
127
val executionContextForTrustedCode = actorSystem.dispatcher
73
- val stepFuture = Future ( { currentState.step(actorSystem, executionContextForUntrustedCode) } )(executionContextForTrustedCode) // compute next state
128
+ val stepFuture = Future ({ currentState.step(actorSystem, executionContextForUntrustedCode)} )(executionContextForTrustedCode) // compute next state
74
129
75
130
// process callback (usually rendering) on prior state, returns true if to continue simulating, false if not
76
- val callbackFuture = Future ( {
131
+ val callbackFuture = Future ({
77
132
priorStateOpt match {
78
133
case None => true // there is no state to call back about (e.g. nothing to render)
79
134
case Some (priorState) => stepCallback(priorState)
80
135
}
81
- } )(executionContextForTrustedCode)
136
+ })(executionContextForTrustedCode)
82
137
83
138
// let the processing complete
84
139
val stepResult = Await .result(stepFuture, Duration .Inf )
0 commit comments