3
3
open MattEland.FSharpGeneticAlgorithm .Logic .WorldPos
4
4
open MattEland.FSharpGeneticAlgorithm .Logic .World
5
5
open MattEland.FSharpGeneticAlgorithm .Logic .Actors
6
+ open MattEland.FSharpGeneticAlgorithm .Logic .Commands
7
+ open MattEland.FSharpGeneticAlgorithm .Logic .WorldGeneration
6
8
7
- type GameState = { World : World ; Player : Actor }
9
+ type SimulationState = Simulating | Won | Lost
8
10
9
- let isValidPos pos ( world : World ): bool =
10
- pos.X >= 1 && pos.Y >= 1 && pos.X <= world.MaxX && pos.Y <= world.MaxY
11
+ type GameState = { World : World ; SimState: SimulationState ; TurnsLeft: int }
11
12
12
- let hasObstacle pos ( world : World ) : bool =
13
- world.Actors
14
- |> Seq.exists( fun actor -> pos = actor.Pos)
13
+ let canEnterActorCell actor target =
14
+ match target with
15
+ | Rabbit | Squirrel _ -> actor = Doggo // Dog can eat the squirrel or rabbit
16
+ | Doggo _ -> false // Nobody bugs the dog
17
+ | Tree _ -> actor = Squirrel true // Only allow if squirrel has an acorn
18
+ | Acorn _ -> actor = Squirrel false // Only allow if squirrel w/o acorn
15
19
16
- let moveActor world actor xDiff yDiff =
17
- let pos = newPos ( actor.Pos.X + xDiff ) ( actor.Pos.Y + yDiff )
20
+ let moveActor state actor pos =
21
+ let world = state.World
18
22
19
- if ( isValidPos pos world ) && not ( hasObstacle pos world ) then
23
+ let performMove =
20
24
let actor = { actor with Pos = pos }
21
25
match actor.ActorKind with
22
- | Squirrel _ -> { world with Squirrel = actor }
23
- | Tree -> { world with Tree = actor }
24
- | Acorn -> { world with Acorn = actor }
25
- | Rabbit -> { world with Rabbit = actor }
26
- | Doggo -> { world with Doggo = actor }
26
+ | Squirrel _ -> { state with World={ world with Squirrel = actor }}
27
+ | Tree -> { state with World={ world with Tree = actor }}
28
+ | Acorn -> { state with World={ world with Acorn = actor }}
29
+ | Rabbit -> { state with World={ world with Rabbit = actor }}
30
+ | Doggo -> { state with World={ world with Doggo = actor }}
31
+
32
+ let handleDogMove state otherActor =
33
+ if otherActor.ActorKind = Rabbit then
34
+ { state with World = { world with
35
+ Rabbit = { world.Rabbit with IsActive = false }
36
+ Doggo = { world.Doggo with Pos = pos}
37
+ }}
38
+ else
39
+ { state with SimState = Lost; World = { world with
40
+ Squirrel = { world.Squirrel with IsActive = false }
41
+ Doggo = { world.Doggo with Pos = pos}
42
+ }
43
+ }
44
+
45
+ let handleSquirrelMove otherActor hasAcorn =
46
+ if not hasAcorn && otherActor.ActorKind = Acorn && otherActor.IsActive then
47
+ // Moving to the acorn for the first time should give the squirrel the acorn
48
+ { state with World =
49
+ {
50
+ world with
51
+ Squirrel = { ActorKind = Squirrel true ; Pos = pos; IsActive = true }
52
+ Acorn = { world.Acorn with IsActive = false }
53
+ }
54
+ }
55
+ else if hasAcorn && otherActor.ActorKind = Tree then
56
+ // Moving to the tree with the acorn - this should win the game
57
+ {
58
+ state with SimState = Won; World = {
59
+ world with Squirrel = { ActorKind = Squirrel true ; Pos = pos; IsActive = true }
60
+ }
61
+ }
62
+ else
63
+ performMove
64
+
65
+ let target = tryGetActor( pos.X, pos.Y) world
66
+
67
+ match target with
68
+ | None -> performMove
69
+ | Some otherActor ->
70
+ if otherActor <> actor && canEnterActorCell actor.ActorKind otherActor.ActorKind then
71
+ match actor.ActorKind with
72
+ | Doggo -> handleDogMove state otherActor
73
+ | Squirrel hasAcorn -> handleSquirrelMove otherActor hasAcorn
74
+ | _ -> performMove
75
+ else
76
+ state
77
+
78
+ let getCandidates ( current : WorldPos , world : World , includeCenter : bool ): WorldPos seq =
79
+ let mutable candidates : WorldPos seq = Seq.empty
80
+ for x in - 1 .. 1 do
81
+ for y in - 1 .. 1 do
82
+ if ( includeCenter || x <> y || x <> 0 ) then
83
+ // Make sure we're in the world boundaries
84
+ let candidatePos = { X= current.X + x; Y= current.Y + y}
85
+ if isValidPos candidatePos world then
86
+ candidates <- Seq.append candidates [ candidatePos]
87
+ candidates
88
+
89
+ let moveRandomly state actor getRandomNumber =
90
+ let current = actor.Pos
91
+ let movedPos = getCandidates( current, state.World, false )
92
+ |> Seq.sortBy( fun _ -> getRandomNumber 1000 )
93
+ |> Seq.head
94
+
95
+ moveActor state actor movedPos
96
+
97
+ let simulateDoggo ( state : GameState ) =
98
+ let doggo = state.World.Doggo
99
+ let rabbit = state.World.Rabbit
100
+ let squirrel = state.World.Squirrel
101
+
102
+ // Eat any adjacent actor
103
+ if rabbit.IsActive && isAdjacentTo doggo.Pos rabbit.Pos then
104
+ moveActor state doggo rabbit.Pos
105
+ else if squirrel.IsActive && isAdjacentTo doggo.Pos squirrel.Pos then
106
+ moveActor state doggo squirrel.Pos
27
107
else
28
- world
108
+ state
109
+
110
+ let decreaseTimer ( state : GameState ) =
111
+ if state.SimState = Simulating then
112
+ if state.TurnsLeft > 0 then
113
+ { state with TurnsLeft = state.TurnsLeft - 1 }
114
+ else
115
+ { state with TurnsLeft = 0 ; SimState = Lost}
116
+ else
117
+ state
118
+
119
+ let simulateActors ( state : GameState ) getRandomNumber =
120
+ moveRandomly state state.World.Rabbit getRandomNumber
121
+ |> simulateDoggo
122
+ |> decreaseTimer
29
123
30
- type GameCommand =
31
- | MoveLeft | MoveRight
32
- | MoveUp | MoveDown
33
- | MoveUpLeft | MoveUpRight
34
- | MoveDownLeft | MoveDownRight
35
- | Wait
36
- | Restart
124
+ let handlePlayerCommand state command =
125
+ let player = state.World.Squirrel
126
+ let xDelta =
127
+ match command with
128
+ | MoveLeft | MoveDownLeft | MoveUpLeft -> - 1
129
+ | MoveRight | MoveDownRight | MoveUpRight -> 1
130
+ | _ -> 0
131
+ let yDelta =
132
+ match command with
133
+ | MoveUpLeft | MoveUp | MoveUpRight -> - 1
134
+ | MoveDownLeft | MoveDown | MoveDownRight -> 1
135
+ | _ -> 0
37
136
38
- let playTurn state player getRandomNumber command =
137
+ let movedPos = { X= player.Pos.X + xDelta; Y= player.Pos.Y + yDelta}
138
+
139
+ if isValidPos movedPos state.World then
140
+ moveActor state player movedPos
141
+ else
142
+ state
143
+
144
+ let playTurn state getRandomNumber command =
39
145
let world = state.World
40
146
match command with
41
- | MoveLeft -> { state with World = moveActor world player - 1 0 }
42
- | MoveRight -> { state with World = moveActor world player 1 0 }
43
- | MoveUp -> { state with World = moveActor world player 0 - 1 }
44
- | MoveDown -> { state with World = moveActor world player 0 1 }
45
- | MoveUpLeft -> { state with World = moveActor world player - 1 - 1 }
46
- | MoveUpRight -> { state with World = moveActor world player 1 - 1 }
47
- | MoveDownLeft -> { state with World = moveActor world player - 1 1 }
48
- | MoveDownRight -> { state with World = moveActor world player 1 1 }
49
- | Wait ->
50
- printfn " Time Passes..."
51
- state
52
- | Restart ->
53
- let world = makeWorld 13 13 getRandomNumber
54
- { World = world; Player = world.Squirrel }
55
-
56
- // TODO: I'll need a way of simulating an actor's turn
147
+ | Restart -> { World = makeWorld world.MaxX world.MaxY getRandomNumber; SimState = Simulating; TurnsLeft = 30 }
148
+ | _ ->
149
+ match state.SimState with
150
+ | Simulating ->
151
+ let newState = handlePlayerCommand state command
152
+ simulateActors newState getRandomNumber
153
+ | _ -> state
0 commit comments