11namespace Examples.GeneticAlgorithm
22
33open System
4+ open System.Collections
45open Avalonia.Controls
56open Avalonia.Controls .Primitives
67open Avalonia.Controls .Shapes
@@ -12,7 +13,8 @@ open Avalonia.FuncUI
1213open Examples.GeneticAlgorithm .Lib
1314
1415type StateStore =
15- { ShowHeatMap: IWritable < bool >
16+ { Random: Random
17+ ShowHeatMap: IWritable < bool >
1618 Worlds: IWritable < World list >
1719 Squirrels: IWritable < SimulationResult list >
1820
@@ -25,6 +27,16 @@ type StateStore =
2527 SelectedGameStateIdx: IWritable < int >
2628 SelectedGameState: IReadable < GameState option > }
2729
30+ member this.NextGeneration () : unit =
31+ let brains =
32+ this.Squirrels.Current
33+ |> List.map ( fun squirrel -> squirrel.brain)
34+
35+ let brains = Population.mutateBrains( this.Random, brains)
36+ let generation = Population.simulateGeneration this.Worlds.Current brains
37+
38+ this.Squirrels.Set ( List.ofSeq generation)
39+
2840 static member Init () =
2941 let seed = Random 42
3042 let worlds = WorldGeneration.makeWorlds( seed, 10 )
@@ -68,12 +80,15 @@ type StateStore =
6880 |> Option.defaultValue List.empty
6981 )
7082 |> State.readTryFindByKey ( fun ( idx , _ ) -> idx) selectedGameStateIdx
71- |> State.readMap ( fun world ->
72- world
83+ |> State.readMap ( fun gameState ->
84+ printfn $" game state changed {gameState}"
85+
86+ gameState
7387 |> Option.map snd
7488 )
7589
76- { ShowHeatMap = showHeatMap
90+ { Random = seed
91+ ShowHeatMap = showHeatMap
7792 Worlds = worlds
7893 Squirrels = population
7994 SelectedSquirrelId = selectedSimulationResultId
@@ -109,7 +124,7 @@ type Views =
109124 (* Next Generation *)
110125 Button.create [
111126 Button.content " Next Generation"
112- Button.onClick ignore
127+ Button.onClick ( ignore >> StateStore.shared.NextGeneration )
113128 ]
114129
115130 (* Next 10 Generation *)
@@ -198,19 +213,39 @@ type Views =
198213 UniformGrid.columns world.MaxX
199214 UniformGrid.rows world.MaxY
200215 UniformGrid.children [
201- for x = 0 to world.MaxX - 1 do
202- for y = 0 to world.MaxY - 1 do
216+ (* world cords state at 1 *)
217+ for x = 1 to world.MaxX do
218+ for y = 1 to world.MaxY do
203219 let actor =
204220 world.Actors
205221 |> Array.tryFind ( fun actor -> actor.Pos.X = x && actor.Pos.Y = y)
206222
207223 match actor with
208224 | Some actor ->
209- Rectangle.create [
210- Rectangle.row y
211- Rectangle.column x
212- Rectangle.fill " red"
213- Rectangle.margin ( 2.0 , 2.0 )
225+ ContentControl.create [
226+ ContentControl.row y
227+ ContentControl.column x
228+ ContentControl.margin ( 2.0 , 2.0 )
229+ ContentControl.background " #27ae60"
230+ ContentControl.fontSize 24.0
231+ ContentControl.verticalContentAlignment VerticalAlignment.Center
232+ ContentControl.horizontalContentAlignment HorizontalAlignment.Center
233+
234+ yield ! [
235+ match actor.ActorKind with
236+ | ActorKind.Acorn ->
237+ ContentControl.content " 🌰"
238+ | ActorKind.Squirrel true ->
239+ ContentControl.content " 🐿🌰"
240+ | ActorKind.Squirrel false ->
241+ ContentControl.content " 🐿"
242+ | ActorKind.Tree ->
243+ ContentControl.content " 🌲"
244+ | ActorKind.Rabbit ->
245+ ContentControl.content " 🐇"
246+ | ActorKind.Doggo ->
247+ ContentControl.content " 🐕"
248+ ]
214249 ]
215250 | None ->
216251 Rectangle.create [
@@ -231,41 +266,80 @@ type Views =
231266 TextBlock.text " No Game State Selected"
232267 ] :> _
233268 )
234- static member worldView ( simulationResult : IReadable < SimulationResult > ) =
269+ static member simulationView ( ) =
235270 Component.create ( " content-view" , fun ctx ->
236- let showHeatMap = ctx.usePassedStateReadOnly StateStore.shared.ShowHeatMap
237- let simulationResult = ctx.usePassedStateReadOnly simulationResult
271+ let worldResult = ctx.usePassedStateReadOnly StateStore.shared.SelectedWorldResult
272+ let gameStateIdx = ctx.usePassedState StateStore.shared.SelectedGameStateIdx
273+
274+ match worldResult.Current with
275+ | Some worldResult ->
276+ DockPanel.create [
277+ DockPanel.lastChildFill true
278+ DockPanel.children [
279+ DockPanel.create [
280+ DockPanel.dock Dock.Bottom
281+ DockPanel.children [
282+
283+ Slider.create [
284+ Slider.dock Dock.Top
285+ Slider.minimum ( double 0 )
286+ Slider.maximum ( double ( worldResult.states.Length - 1 ))
287+ Slider.tickFrequency ( double 1 )
288+ Slider.value ( double gameStateIdx.Current)
289+ Slider.onValueChanged ( fun value ->
290+ gameStateIdx.Set ( int value)
291+ )
292+ ]
238293
239- DockPanel.create [
240- DockPanel.children [
241- Views.selectedGameStateView ()
242- ]
294+ StackPanel.create [
295+ StackPanel.dock Dock.Top
296+ StackPanel.children [
297+ TextBlock.create [
298+ TextBlock.text $" world score: {worldResult.score}"
299+ ]
300+
301+ TextBlock.create [
302+ TextBlock.text (
303+ let state =
304+ match int ( List.last worldResult.states) .SimState with
305+ | 0 ->
306+ " running"
307+ | 1 ->
308+ " won"
309+ | 2 ->
310+ " lost"
311+ | _ ->
312+ " unknown"
313+
314+ $" result: {state}"
315+ )
243316
244- ] :> IView
317+
318+
319+ ]
320+ ]
321+
322+ ]
323+ ]
324+ ]
325+
326+ Views.selectedGameStateView ()
327+ ]
328+
329+ ] :> IView
330+ | None ->
331+ TextBlock.create [
332+ TextBlock.text " No World Result Selected"
333+ ] :> IView
245334 )
246335
247336 static member contentView () =
248337 Component.create ( " content-view" , fun ctx ->
249- let selected = ctx.usePassedState StateStore.shared.SelectedSquirrel
250-
251338
252339 DockPanel.create [
253340 DockPanel.lastChildFill true
254341 DockPanel.children [
255- match selected.Current with
256- | Some _ ->
257- let s =
258- selected
259- |> State.readMap ( fun s -> s.Value)
260- |> ctx.usePassedStateReadOnly
261-
262- Views.worldView s
263-
264- | None ->
265- TextBlock.create [
266- TextBlock.text " no inhabitant selected"
267- ]
268-
342+ Views.simulationView ()
269343 ]
270344 ]
271345 :> IView
0 commit comments