Skip to content

Commit c6f68d2

Browse files
committed
wip
1 parent 77b1b07 commit c6f68d2

File tree

14 files changed

+903
-28
lines changed

14 files changed

+903
-28
lines changed

src/Avalonia.FuncUI.Components/State/State.Adapters.fs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace Avalonia.FuncUI
22

33
open System
4+
open Avalonia.Controls.Primitives
45
open Avalonia.FuncUI
56

67
type internal UniqueValueReadOnly<'value>
@@ -31,13 +32,31 @@ type internal UniqueValue<'value>
3132
member this.Set (newValue: 'value) =
3233
src.Set newValue
3334

34-
type internal ValueMap<'value, 'key when 'key : comparison>
35-
( src: IWritable<'value list>,
35+
type internal ValueMapped<'a, 'b>
36+
( src: IReadable<'a>,
37+
mapFunc: 'a -> 'b ) =
38+
39+
let mutable current: 'b = mapFunc src.Current
40+
41+
interface IReadable<'b> with
42+
member this.InstanceId with get () = src.InstanceId
43+
member this.Current with get () = current
44+
member this.Subscribe (handler: 'b -> unit) =
45+
src.Subscribe (fun value ->
46+
current <- mapFunc value
47+
handler current
48+
)
49+
50+
member this.Dispose () =
51+
()
52+
53+
type internal ReadValueMap<'value, 'key when 'key : comparison>
54+
( src: IReadable<'value list>,
3655
keyPath: 'value -> 'key ) =
3756

3857
let disposable = new DisposableBag ()
39-
let value: IWritable<_> =
40-
let value = new UniqueValue<_>(src)
58+
let value: IReadable<_> =
59+
let value = new UniqueValueReadOnly<_>(src)
4160
disposable.Add value
4261
value :> _
4362

@@ -47,7 +66,7 @@ type internal ValueMap<'value, 'key when 'key : comparison>
4766
|> Map.ofSeq
4867
let mutable current: Map<'key, 'value> = makeMap value.Current
4968

50-
interface IWritable<Map<'key, 'value>> with
69+
interface IReadable<Map<'key, 'value>> with
5170
member this.InstanceId with get () = value.InstanceId
5271
member this.Current with get () = current
5372
member this.Subscribe (handler: Map<'key, 'value> -> unit) =
@@ -57,22 +76,30 @@ type internal ValueMap<'value, 'key when 'key : comparison>
5776
handler current'
5877
)
5978

79+
member this.Dispose () =
80+
(disposable :> IDisposable).Dispose ()
81+
82+
type internal ValueMap<'value, 'key when 'key : comparison>
83+
( src: IWritable<'value list>,
84+
keyPath: 'value -> 'key ) =
85+
86+
inherit ReadValueMap<'value, 'key>(src, keyPath)
87+
88+
interface IWritable<Map<'key, 'value>> with
89+
6090
member this.Set (signal: Map<'key, 'value>) : unit =
6191
src.Current
6292
|> Seq.choose (fun item -> Map.tryFind (keyPath item) signal)
6393
|> Seq.toList
6494
|> src.Set
6595

66-
member this.Dispose () =
67-
(disposable :> IDisposable).Dispose ()
68-
69-
type internal KeyFocusedValue<'value, 'key when 'key : comparison>
70-
( src: IWritable<Map<'key, 'value>>,
96+
type internal ReadKeyFocusedValue<'value, 'key when 'key : comparison>
97+
( src: IReadable<Map<'key, 'value>>,
7198
key: IReadable<'key> ) =
7299

73100
let disposable = new DisposableBag ()
74-
let value: IWritable<_> =
75-
let value = new UniqueValue<_>(src)
101+
let value: IReadable<_> =
102+
let value = new UniqueValueReadOnly<_>(src)
76103
disposable.Add value
77104
value :> _
78105

@@ -92,13 +119,23 @@ type internal KeyFocusedValue<'value, 'key when 'key : comparison>
92119
do disposable.Add (key.Subscribe (ignore >> onKeyOrValueChanged))
93120
do disposable.Add (value.Subscribe (ignore >> onKeyOrValueChanged))
94121

95-
interface IWritable<'value option> with
122+
interface IReadable<'value option> with
96123
member this.InstanceId with get () = value.InstanceId
97124
member this.Current with get () = current
98125

99126
member this.Subscribe (handler: 'value option -> unit) =
100127
onChange.Publish.Subscribe handler
101128

129+
member this.Dispose () =
130+
(disposable :> IDisposable).Dispose()
131+
132+
type internal KeyFocusedValue<'value, 'key when 'key : comparison>
133+
( src: IWritable<Map<'key, 'value>>,
134+
key: IReadable<'key> ) =
135+
136+
inherit ReadKeyFocusedValue<'value, 'key>(src, key)
137+
138+
interface IWritable<'value option> with
102139
member this.Set (signal: 'value option) : unit =
103140
match signal with
104141
| Some signal ->
@@ -110,9 +147,6 @@ type internal KeyFocusedValue<'value, 'key when 'key : comparison>
110147
|> Map.remove key.Current
111148
|> src.Set
112149

113-
member this.Dispose () =
114-
(disposable :> IDisposable).Dispose()
115-
116150
type internal FilteringValueList<'value, 'filter>
117151
( src: IWritable<'value list>,
118152
filter: IReadable<'filter>,

src/Avalonia.FuncUI.Components/State/State.Functions.fs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,18 @@ module State =
3131
uniqueWire :> _
3232

3333
let subscribe (handler: 'value -> unit) (state: IWritable<'value>) : IDisposable =
34-
state.Subscribe handler
34+
state.Subscribe handler
35+
36+
(* read only functions prefixed with 'read' *)
37+
38+
let readUnique (state: IReadable<'value>) : IReadable<'value> =
39+
let uniqueWire = new UniqueValueReadOnly<'value>(state)
40+
uniqueWire :> _
41+
42+
let readMap (mapFunc: 'a -> 'b) (value: IReadable<'a>) : IReadable<'b> =
43+
new ValueMapped<'a, 'b>(value, mapFunc) :> _
44+
45+
let readTryFindByKey (keyPath: 'signal -> 'key) (key: IReadable<'key>) (wire: IReadable<list<'signal>>) : IReadable<'signal option> =
46+
let keyedWire: IReadable<Map<'key, 'signal>> = new ReadValueMap<'signal, 'key>(wire, keyPath) :> _
47+
let keyFocusedWire: IReadable<'signal option> = new ReadKeyFocusedValue<'signal, 'key>(keyedWire, key) :> _
48+
keyFocusedWire

src/Examples/Component Examples/Examples.GeneticAlgorithm/Examples.GeneticAlgorithm.fsproj

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17-
<Compile Include="Program.fs"/>
17+
<Compile Include="Lib\WorldPos.fs" />
18+
<Compile Include="Lib\Actors.fs" />
19+
<Compile Include="Lib\World.fs" />
20+
<Compile Include="Lib\WorldGeneration.fs" />
21+
<Compile Include="Lib\States.fs" />
22+
<Compile Include="Lib\Genetics.fs" />
23+
<Compile Include="Lib\Fitness.fs" />
24+
<Compile Include="Lib\Simulator.fs" />
25+
<Compile Include="Lib\Population.fs" />
26+
<Compile Include="Views.fs" />
27+
<Compile Include="Program.fs" />
1828
</ItemGroup>
1929

2030
</Project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Examples.GeneticAlgorithm.Lib
2+
3+
type ActorKind =
4+
| Squirrel of hasAcorn: bool
5+
| Tree
6+
| Acorn
7+
| Rabbit
8+
| Doggo
9+
10+
type Actor =
11+
{ Pos : WorldPos;
12+
ActorKind : ActorKind;
13+
IsActive : bool }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Examples.GeneticAlgorithm.Lib
2+
3+
module Fitness =
4+
5+
let evaluateFitness (gameStates: GameState[], fitnessFunction): float = fitnessFunction(gameStates)
6+
7+
let standardFitnessFunction (gameStates: GameState[]): float =
8+
let lastState: GameState = Seq.last gameStates
9+
10+
let gameLength = float(gameStates.Length)
11+
12+
let gotAcornBonus =
13+
match lastState.World.Acorn.IsActive with
14+
| true -> 100.0
15+
| false -> 0.0
16+
17+
let finalStateBonus =
18+
match lastState.SimState with
19+
| SimulationState.Won -> 1000.0 - (gameLength * 10.0) // Reward quick wins
20+
| _ -> -50.0 + gameLength
21+
22+
gotAcornBonus + finalStateBonus
23+
24+
let killRabbitFitnessFunction (gameStates: GameState[]): float =
25+
let lastState: GameState = Seq.last gameStates
26+
27+
let gameLength = float(gameStates.Length)
28+
29+
let gotAcornBonus =
30+
match lastState.World.Acorn.IsActive with
31+
| true -> 100.0
32+
| false -> 0.0
33+
34+
let isRabbitAlive = lastState.World.Rabbit.IsActive
35+
36+
let finalStateBonus =
37+
match lastState.SimState with
38+
| SimulationState.Won -> match isRabbitAlive with
39+
| false -> 1000.0 // Heavily reward dead rabbits
40+
| true -> 250.0 - (gameLength * 10.0) // Reward quick wins
41+
| _ -> match isRabbitAlive with
42+
| true -> -50.0 + gameLength
43+
| false -> gameLength
44+
45+
gotAcornBonus + finalStateBonus
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
namespace Examples.GeneticAlgorithm.Lib
2+
3+
open System
4+
5+
type ActorGeneIndex =
6+
| Doggo = 0
7+
| Acorn = 1
8+
| Rabbit = 2
9+
| Tree = 3
10+
| Squirrel = 4
11+
| NextToDoggo = 5
12+
| NextToRabbit = 6
13+
14+
type ActorChromosome =
15+
{
16+
genes: double list
17+
age: int
18+
}
19+
20+
type IndividualWorldResult = {
21+
score: float
22+
states: GameState list
23+
}
24+
25+
type SimulationResult = {
26+
identity: Guid
27+
totalScore: float
28+
results: IndividualWorldResult list
29+
brain: ActorChromosome
30+
}
31+
32+
module Genes =
33+
34+
let getRandomGene (random: System.Random) = (random.NextDouble() * 2.0) - 1.0
35+
36+
let getRandomChromosome (random: System.Random) =
37+
{
38+
age = 0
39+
genes = Seq.init 7 (fun _ -> getRandomGene random) |> Seq.toList
40+
}
41+
42+
let mutate (random: System.Random, magnitude, value) =
43+
(value + (random.NextDouble() * magnitude))
44+
|> max -1.0 |> min 1.0
45+
46+
let mutateGenes (random: System.Random) mutationChance genes =
47+
List.map (fun g -> if random.NextDouble() <= mutationChance then
48+
mutate(random, 0.5, g)
49+
else
50+
g
51+
) genes
52+
53+
let getChildGenes (random: System.Random) parent1 parent2 mutationChance =
54+
55+
// Map from one parent to another, choosing a point to switch from one parent as the source
56+
// to the other. Being an identical copy to either parent is also possible
57+
let crossoverIndex = random.Next(List.length parent1 + 1)
58+
59+
List.mapi2 (fun i m f -> if i <= crossoverIndex then
60+
m
61+
else
62+
f
63+
) parent1 parent2
64+
// Next allow each gene to be potentially mutated
65+
|> mutateGenes random mutationChance
66+
67+
let createChild (random: System.Random, parent1: double list, parent2: double list, mutationChance: float) =
68+
69+
let genes = getChildGenes random parent1 parent2 mutationChance
70+
{
71+
age = 0
72+
genes = Seq.toList genes
73+
}
74+
75+
let evaluateProximity actor pos weight =
76+
if actor.IsActive then
77+
let maxDistance = 225.0
78+
let distance = WorldPos.getDistance(actor.Pos, pos)
79+
if distance < maxDistance then
80+
((maxDistance - distance)/maxDistance) * weight
81+
else
82+
0.0
83+
else
84+
0.0
85+
86+
let evaluateAdjacentTo actor pos weight =
87+
if actor.IsActive && actor.Pos <> pos then
88+
if WorldPos.getDistance(actor.Pos, pos) <= 1.5 then
89+
0.05 * weight
90+
else
91+
0.0
92+
else
93+
0.0
94+
95+
let getGene (geneIndex: ActorGeneIndex) (genes: double list) =
96+
genes.[int geneIndex]
97+
98+
let evaluateTile brain world pos =
99+
let genes = brain.genes
100+
101+
let proxSquirrel = evaluateProximity world.Squirrel pos (getGene ActorGeneIndex.Squirrel genes)
102+
let proxRabbit = evaluateProximity world.Rabbit pos (getGene ActorGeneIndex.Rabbit genes)
103+
let proxDoggo = evaluateProximity world.Doggo pos (getGene ActorGeneIndex.Doggo genes)
104+
let proxAcorn = evaluateProximity world.Acorn pos (getGene ActorGeneIndex.Acorn genes)
105+
let proxTree = evaluateProximity world.Tree pos (getGene ActorGeneIndex.Tree genes)
106+
let adjDoggo = evaluateAdjacentTo world.Doggo pos (getGene ActorGeneIndex.NextToDoggo genes)
107+
let adjRabbit = evaluateAdjacentTo world.Rabbit pos (getGene ActorGeneIndex.NextToRabbit genes)
108+
109+
proxSquirrel + proxRabbit + proxDoggo + proxAcorn + proxTree + adjDoggo + adjRabbit
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace Examples.GeneticAlgorithm.Lib
2+
3+
module Population =
4+
5+
let simulateGeneration states actors =
6+
actors
7+
|> Seq.map (fun b -> Simulator.simulate b states)
8+
|> Seq.sortByDescending (fun r -> r.totalScore)
9+
10+
let buildInitialPopulation random =
11+
Seq.init<ActorChromosome> 20 (fun _ -> Genes.getRandomChromosome random)
12+
13+
let simulateFirstGeneration states random =
14+
buildInitialPopulation random |> simulateGeneration states
15+
16+
let mutateBrains (random: System.Random, brains: ActorChromosome[]): ActorChromosome[] =
17+
let numBrains = brains.Length
18+
let survivors = [| brains.[0]; brains.[1]; |]
19+
let randos = Seq.init (numBrains - 4) (fun _ -> Genes.getRandomChromosome random) |> Seq.toArray
20+
21+
let children = [|
22+
Genes.createChild(random, survivors.[0].genes, survivors.[1].genes, 0.25);
23+
Genes.createChild(random, survivors.[0].genes, survivors.[1].genes, 0.5);
24+
|]
25+
26+
Array.append children randos |> Array.append survivors
27+
28+
let mutateAndSimulateGeneration (random: System.Random, worlds: World[], results: SimulationResult[]) =
29+
let brains = Seq.map (fun b -> b.brain) results |> Seq.toArray
30+
mutateBrains(random, brains) |> simulateGeneration worlds
31+
32+
let mutateAndSimulateMultiple (random: System.Random, worlds: World[], generations: int, results: SimulationResult[]) =
33+
let mutable currentResults = results
34+
for _ = 1 to generations do
35+
let brains = Seq.map (fun b -> b.brain) currentResults |> Seq.toArray
36+
currentResults <- mutateBrains(random, brains) |> simulateGeneration worlds |> Seq.toArray
37+
currentResults

0 commit comments

Comments
 (0)