Skip to content

Commit 05a7747

Browse files
mohsen-ghaffari1992wasowski
authored andcommitted
Add continuous maze.
1 parent cd63ab2 commit 05a7747

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package symsim
2+
package examples.concrete.continuousmaze
3+
4+
import cats.{Eq, Foldable, Monad}
5+
import cats.kernel.BoundedEnumerable
6+
7+
import org.scalacheck.{Arbitrary, Gen}
8+
9+
import symsim.concrete.Randomized
10+
11+
/**
12+
*
13+
*/
14+
15+
case class MazeState (x: Double, y: Double):
16+
override def toString: String = s"[x=$x, y=$y]"
17+
18+
type MazeObservableState = MazeState
19+
type MazeReward = Double
20+
21+
enum Direction:
22+
case R, L, U, D
23+
24+
enum Velocity:
25+
case B, S
26+
27+
type MazeAction = (Direction, Velocity)
28+
29+
val MAZE_LENGTH: Int = 4
30+
val MAZE_WIDTH: Int = 3
31+
32+
val BIG_STEP: Double = 1
33+
val SMALL_STEP: Double = 0.5
34+
35+
object ContinuousMaze
36+
extends
37+
Agent[MazeState, MazeObservableState, MazeAction, MazeReward, Randomized],
38+
Episodic:
39+
40+
val TimeHorizon: Int = 2000
41+
42+
def isFinal (s: MazeState): Boolean =
43+
s == MazeState (MAZE_LENGTH, MAZE_WIDTH) || s == MazeState (MAZE_LENGTH, MAZE_WIDTH - 1)
44+
45+
// Maze is discrete
46+
def observe (s: MazeState): MazeObservableState = MazeState (s.x.floor, s.y.floor)
47+
48+
// We are not using the original reward function from AIAMA as it
49+
// gives to unstable learning results
50+
private def mazeReward (s: MazeState): MazeReward = s match
51+
case MazeState (MAZE_LENGTH, MAZE_WIDTH) => +0.0 // Good final state
52+
case MazeState (MAZE_LENGTH, y) if y == MAZE_WIDTH - 1 => -1000.0 // Bad final state (dead)
53+
case MazeState (_, _) => -1.0
54+
55+
56+
def distort (a: MazeAction): Randomized[MazeAction] = a match
57+
case (Direction.U, a._2) | (Direction.D, a._2) =>
58+
Randomized.oneOf ((Direction.R, a._2), (Direction.L, a._2))
59+
case (Direction.L, a._2) | (Direction.R, a._2) =>
60+
Randomized.oneOf((Direction.U, a._2), (Direction.D, a._2))
61+
62+
63+
def successor (s: MazeState) (a: MazeAction): MazeState =
64+
require (valid (s))
65+
66+
val result = a match
67+
case (Direction.U, Velocity.B) => MazeState (s._1, s._2 + BIG_STEP)
68+
case (Direction.U, Velocity.S) => MazeState (s._1, s._2 + SMALL_STEP)
69+
case (Direction.D, Velocity.B) => MazeState (s._1, s._2 - BIG_STEP)
70+
case (Direction.D, Velocity.S) => MazeState (s._1, s._2 - SMALL_STEP)
71+
case (Direction.R, Velocity.B) => MazeState (s._1 + BIG_STEP, s._2)
72+
case (Direction.R, Velocity.S) => MazeState (s._1 + SMALL_STEP, s._2)
73+
case (Direction.L, Velocity.B) => MazeState (s._1 - BIG_STEP, s._2)
74+
case (Direction.L, Velocity.S) => MazeState (s._1 - SMALL_STEP, s._2)
75+
if valid (result) then result else s
76+
77+
def valid (s: MazeState): Boolean =
78+
s._1 >= 1 && s._1 <= 4 && s._2 >= 1 && s._2 <= 3 && s != MazeState (2, 2)
79+
80+
val attention = 0.8
81+
82+
def step (s: MazeState) (a: MazeAction): Randomized[(MazeState, MazeReward)] =
83+
for
84+
precise <- Randomized.coin (attention)
85+
action <- if precise then Randomized.const (a) else distort (a)
86+
newState = successor (s) (action)
87+
yield (newState, mazeReward (newState))
88+
89+
def initialize: Randomized[MazeState] =
90+
Randomized.repeat (Randomized.oneOf (instances.allObservableStates*))
91+
.filter (s => !isFinal (s))
92+
93+
val instances = ContinuousMazeInstances
94+
95+
end ContinuousMaze
96+
97+
98+
/** Here is a proof that our types actually deliver on everything that an Agent
99+
* needs to be able to do to work in the framework.
100+
*/
101+
object ContinuousMazeInstances
102+
extends AgentConstraints[MazeState, MazeObservableState, MazeAction, MazeReward, Randomized]:
103+
104+
given enumAction: BoundedEnumerable[MazeAction] =
105+
BoundedEnumerableFromList ((Direction.U, Velocity.B), (Direction.U, Velocity.S),
106+
(Direction.D, Velocity.B), (Direction.D, Velocity.S),
107+
(Direction.R, Velocity.B), (Direction.R, Velocity.S),
108+
(Direction.L, Velocity.B), (Direction.L, Velocity.S))
109+
110+
given enumState: BoundedEnumerable[MazeObservableState] =
111+
val ss = for
112+
y <- (1 to MAZE_WIDTH).toSeq
113+
x <- (1 to MAZE_LENGTH).toSeq
114+
result = MazeState (x, y)
115+
if ContinuousMaze.valid (result)
116+
yield result
117+
BoundedEnumerableFromList (ss*)
118+
119+
given schedulerIsMonad: Monad[Randomized] = Randomized.randomizedIsMonad
120+
121+
given schedulerIsFoldable: Foldable[Randomized] = Randomized.randomizedIsFoldable
122+
123+
given canTestInScheduler: CanTestIn[Randomized] = Randomized.canTestInRandomized
124+
125+
lazy val genMazeState: Gen[MazeState] = for
126+
y <- Gen.choose[Double](1, MAZE_WIDTH)
127+
x <- Gen.choose[Double](1, MAZE_LENGTH)
128+
if (x != 2 && y != 2)
129+
yield MazeState (x.abs, y.abs)
130+
131+
given arbitraryState: Arbitrary[MazeState] = Arbitrary (genMazeState)
132+
133+
given eqMazeState: Eq[MazeState] = Eq.fromUniversalEquals
134+
135+
given arbitraryReward: Arbitrary[MazeReward] = Arbitrary (Gen.double)
136+
137+
given rewardArith: Arith[MazeReward] = Arith.arithDouble
138+
139+
end ContinuousMazeInstances
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package symsim
2+
package examples.concrete.continuousmaze
3+
4+
import ContinuousMaze.instances.given
5+
6+
class SarsaExperiments
7+
extends ExperimentSpec[MazeState, MazeState, MazeAction]:
8+
9+
val sarsa = symsim.concrete.ConcreteSarsa (
10+
agent = ContinuousMaze,
11+
alpha = 0.1,
12+
gamma = 1,
13+
epsilon = 0.05,
14+
episodes = 50000,
15+
)
16+
17+
s"SimpleMaze experiment with ${sarsa}" in {
18+
val policy = learnAndLog (sarsa)
19+
}

0 commit comments

Comments
 (0)