Now that our agent can find out how happy it is, it can turn an abtract issue of “not losing tetrix to a human” problem into tree searching problem. At any point in time, the agent and the scheduled timer can take one of the five actions we have been looking at:
def receive = {
case MoveLeft => updateState {moveLeft}
case MoveRight => updateState {moveRight}
case RotateCW => updateState {rotateCW}
case Tick => updateState {tick}
case Drop => updateState {drop}
}
In other words, bestMove
is a GameState => StageMessage
function. What’s with the tree? At the initial state s0
(at time=0), the agent can take five actions: MoveLeft
, MoveRight
etc. The actions result in five states s1
, s2
, s3
, s4
, s5
(at time=1). Each of the states then can branch into five more s11
, s12
, …, s55
. Draw this out, and we have a tree structure.
s0
|
+--------------------+--------------------+-------...
s1 s2 s3
| | |
+---+---+---+---+ +---+---+---+---+ +---+---+---+---+
s11 s12 s13 s14 s15 s21 s22 s23 s24 s25 s31 s32 s33 s34 s35
The number of the nodes grows exponentially. 1 + 5 + 5^2
. For now, let’s just start with one level.
Here’s how we can contruct a test. Make a state named s3
, which is one Drop
action away from deleting a line. We tell the agent to pick a move, and it should select Drop
. As a negative control, we also need some other state s1
, which the agent can pick whatever action:
s2"""
Solver should
pick MoveLeft for s1 $solver1
pick Drop for s3 $solver2
"""
...
def solver1 =
agent.bestMove(s1) must_== MoveLeft
def solver2 =
agent.bestMove(s3) must_== Drop
And here’s a stub:
def bestMove(state: GameState): StageMessage = MoveLeft
This fails the test as expected.
[info] Solver should
[info] + pick MoveLeft for s1
[info] x pick Drop for s3
[error] 'MoveLeft' is not equal to 'Drop' (AgentSpec.scala:32)
We’ll get back to this tomorrow.
$ git fetch origin
$ git co day6v2 -b try/day6
$ sbt swing/run