Skip to content

Commit b2d4219

Browse files
authored
Merge pull request #1 from isaacabraham/refactors
Sample refactorings
2 parents 3cc672b + feec42f commit b2d4219

File tree

6 files changed

+179
-176
lines changed

6 files changed

+179
-176
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
1-
namespace MattEland.FSharpGeneticAlgorithm.ConsoleTestApp
1+
module MattEland.FSharpGeneticAlgorithm.ConsoleTestApp.Display
22

33
open System
44
open MattEland.FSharpGeneticAlgorithm.Logic.World
55

6-
module Display =
6+
let printCell char isLastCell =
7+
if isLastCell then
8+
printfn "%c" char
9+
else
10+
printf "%c" char
711

8-
let printCell char isLastCell =
9-
if isLastCell then
10-
printfn "%c" char
11-
else
12-
printf "%c" char
12+
let displayWorld (world: World) =
13+
printfn ""
1314

14-
let displayWorld (world: World) =
15-
printfn ""
15+
for y in 1..world.MaxX do
16+
for x in 1..world.MaxY do
17+
// Determine which character should exist in this line
18+
let char = world |> getCharacterAtCell(x,y)
19+
printCell char (x = world.MaxX)
1620

17-
for y in 1..world.MaxX do
18-
for x in 1..world.MaxY do
19-
// Determine which character should exist in this line
20-
let char = world.GetCharacterAtCell(x,y)
21-
printCell char (x = world.MaxX)
21+
let getUserInput(): ConsoleKeyInfo =
22+
printfn ""
23+
printfn "Press Arrow Keys to move, R to regenerate, or X to exit"
2224

23-
let getUserInput(): ConsoleKeyInfo =
24-
printfn ""
25-
printfn "Press Arrow Keys to move, R to regenerate, or X to exit"
26-
27-
Console.ReadKey(true)
28-
25+
Console.ReadKey(true)
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,72 @@
11
open System
2+
open MattEland.FSharpGeneticAlgorithm.Logic.Actors
23
open MattEland.FSharpGeneticAlgorithm.Logic.World
34
open MattEland.FSharpGeneticAlgorithm.Logic.Simulator
45
open MattEland.FSharpGeneticAlgorithm.ConsoleTestApp.Display
5-
6-
let generateWorld randomizer =
7-
new World(13, 13, randomizer)
86

7+
type GameCommand =
8+
| MoveLeft | MoveRight
9+
| MoveUp | MoveDown
10+
| MoveUpLeft | MoveUpRight
11+
| MoveDownLeft | MoveDownRight
12+
| Wait
13+
| Restart
14+
15+
type Command =
16+
| Action of GameCommand
17+
| Exit
18+
19+
let tryParseInput (info:ConsoleKeyInfo) =
20+
match info.Key with
21+
| ConsoleKey.LeftArrow -> Some (Action MoveLeft)
22+
| ConsoleKey.RightArrow -> Some (Action MoveRight)
23+
| ConsoleKey.UpArrow -> Some (Action MoveUp)
24+
| ConsoleKey.DownArrow -> Some (Action MoveDown)
25+
| ConsoleKey.NumPad7 | ConsoleKey.Home -> Some (Action MoveUpLeft)
26+
| ConsoleKey.NumPad9 | ConsoleKey.PageUp -> Some (Action MoveUpRight)
27+
| ConsoleKey.NumPad1 | ConsoleKey.End -> Some (Action MoveDownRight)
28+
| ConsoleKey.NumPad3 | ConsoleKey.PageDown -> Some (Action MoveDownRight)
29+
| ConsoleKey.NumPad5 | ConsoleKey.Spacebar | ConsoleKey.Clear -> Some (Action Wait)
30+
| ConsoleKey.X -> Some Exit
31+
| ConsoleKey.R -> Some (Action Restart)
32+
| _ -> None
33+
34+
type GameState = { World : World; Player : Actor }
35+
936
[<EntryPoint>]
1037
let main argv =
1138
printfn "F# Console Application Tutorial by Matt Eland"
1239

13-
let randomizer = new Random()
40+
let getRandomNumber =
41+
let r = Random()
42+
fun max -> (r.Next max) + 1
1443

15-
let mutable simulating: bool = true
16-
let mutable world = generateWorld(randomizer)
44+
let endState =
45+
let world = makeWorld 13 13 getRandomNumber
46+
let player = world.Squirrel
47+
let state = { World = world; Player = world.Squirrel }
1748

18-
while simulating do
19-
displayWorld world
49+
let playTurn state command =
50+
match command with
51+
| MoveLeft -> { state with World = moveActor world player -1 0 }
52+
| MoveRight -> { state with World = moveActor world player 1 0 }
53+
| MoveUp -> { state with World = moveActor world player 0 -1 }
54+
| MoveDown -> { state with World = moveActor world player 0 1 }
55+
| MoveUpLeft -> { state with World = moveActor world player -1 -1 }
56+
| MoveUpRight -> { state with World = moveActor world player 1 -1 }
57+
| MoveDownLeft -> { state with World = moveActor world player -1 1 }
58+
| MoveDownRight -> { state with World = moveActor world player 1 1 }
59+
| Wait ->
60+
printfn "Time Passes..."
61+
state
62+
| Restart ->
63+
let world = makeWorld 13 13 getRandomNumber
64+
{ World = world; Player = world.Squirrel }
2065

21-
let player = world.Squirrel
22-
let key = getUserInput()
23-
24-
Console.Clear()
25-
26-
match key.Key with
27-
| ConsoleKey.LeftArrow ->
28-
world <- moveActor world player -1 0
29-
| ConsoleKey.RightArrow ->
30-
world <- moveActor world player 1 0
31-
| ConsoleKey.UpArrow ->
32-
world <- moveActor world player 0 -1
33-
| ConsoleKey.DownArrow ->
34-
world <- moveActor world player 0 1
35-
| ConsoleKey.NumPad7 | ConsoleKey.Home ->
36-
world <- moveActor world player -1 -1
37-
| ConsoleKey.NumPad9 | ConsoleKey.PageUp ->
38-
world <- moveActor world player 1 -1
39-
| ConsoleKey.NumPad1 | ConsoleKey.End ->
40-
world <- moveActor world player -1 1
41-
| ConsoleKey.NumPad3 | ConsoleKey.PageDown ->
42-
world <- moveActor world player 1 1
43-
| ConsoleKey.NumPad5 | ConsoleKey.Spacebar | ConsoleKey.Clear ->
44-
printfn "Time Passes..."
45-
| ConsoleKey.X -> simulating <- false
46-
| ConsoleKey.R -> world <- generateWorld(randomizer)
47-
| _ -> printfn "Invalid input '%c'" key.KeyChar
66+
Seq.initInfinite(fun _ -> getUserInput())
67+
|> Seq.choose tryParseInput
68+
|> Seq.takeWhile (function | Exit -> false | _ -> true)
69+
|> Seq.choose(function | Exit -> None | Action gameCommand -> Some gameCommand)
70+
|> Seq.fold playTurn state
4871

4972
0 // return an integer exit code
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,22 @@
1-
namespace MattEland.FSharpGeneticAlgorithm.Logic
1+
module MattEland.FSharpGeneticAlgorithm.Logic.Actors
22

33
open MattEland.FSharpGeneticAlgorithm.Logic.WorldPos
44

5-
module Actors =
6-
7-
[<AbstractClass>]
8-
type Actor(pos: WorldPos) =
9-
member val Pos = pos with get, set
10-
abstract member Character: char
11-
12-
type Squirrel(pos: WorldPos, hasAcorn: bool) =
13-
inherit Actor(pos)
14-
member this.HasAcorn = hasAcorn
15-
override this.Character = 'S'
16-
17-
let createSquirrel pos = new Squirrel(pos, false)
18-
19-
type Tree(pos: WorldPos) =
20-
inherit Actor(pos)
21-
override this.Character = 't'
22-
23-
let createTree pos = new Tree(pos)
24-
25-
type Acorn(pos: WorldPos) =
26-
inherit Actor(pos)
27-
override this.Character = 'a'
28-
29-
let createAcorn pos = new Acorn(pos)
30-
31-
type Rabbit(pos: WorldPos) =
32-
inherit Actor(pos)
33-
override this.Character = 'R'
34-
35-
let createRabbit pos = new Rabbit(pos)
36-
37-
type Doggo(pos: WorldPos) =
38-
inherit Actor(pos)
39-
override this.Character = 'D'
40-
41-
let createDoggo pos = new Doggo(pos)
5+
type ActorKind =
6+
| Squirrel of hasAcorn:bool
7+
| Tree
8+
| Acorn
9+
| Rabbit
10+
| Doggo
11+
12+
type Actor =
13+
{ Pos : WorldPos
14+
ActorKind : ActorKind }
15+
16+
let getChar actor =
17+
match actor.ActorKind with
18+
| Squirrel _ -> 'S'
19+
| Tree _ -> 't'
20+
| Acorn _ -> 'a'
21+
| Rabbit _ -> 'R'
22+
| Doggo _ -> 'D'
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
1-
namespace MattEland.FSharpGeneticAlgorithm.Logic
1+
module MattEland.FSharpGeneticAlgorithm.Logic.Simulator
22

33
open MattEland.FSharpGeneticAlgorithm.Logic.WorldPos
44
open MattEland.FSharpGeneticAlgorithm.Logic.World
55
open MattEland.FSharpGeneticAlgorithm.Logic.Actors
66

7-
module Simulator =
8-
9-
let isValidPos pos (world: World): bool =
10-
pos.X >= 1 && pos.Y >= 1 && pos.X <= world.MaxX && pos.Y <= world.MaxY
11-
12-
let hasObstacle pos (world: World): bool =
13-
let mutable obstructed = false
14-
for actor in world.Actors do
15-
if isSamePos pos actor.Pos then
16-
obstructed <- true
17-
obstructed
18-
19-
let moveActor (world: World) (actor: Actor) (xDiff: int32) (yDiff: int32): World =
20-
let pos = newPos (actor.Pos.X + xDiff) (actor.Pos.Y + yDiff)
21-
22-
if (isValidPos pos world) && not (hasObstacle pos world) then
23-
actor.Pos <- pos
24-
7+
let isValidPos pos (world: World): bool =
8+
pos.X >= 1 && pos.Y >= 1 && pos.X <= world.MaxX && pos.Y <= world.MaxY
9+
10+
let hasObstacle pos (world: World) : bool =
11+
world.Actors
12+
|> Seq.exists(fun actor -> pos = actor.Pos)
13+
14+
let moveActor world actor xDiff yDiff =
15+
let pos = newPos (actor.Pos.X + xDiff) (actor.Pos.Y + yDiff)
16+
17+
if (isValidPos pos world) && not (hasObstacle pos world) then
18+
let actor = { actor with Pos = pos }
19+
match actor.ActorKind with
20+
| Squirrel _ -> { world with Squirrel = actor }
21+
| Tree -> { world with Tree = actor }
22+
| Acorn -> { world with Acorn = actor }
23+
| Rabbit -> { world with Rabbit = actor }
24+
| Doggo -> { world with Doggo = actor }
25+
else
2526
world
2627

27-
// TODO: I'll need a way of simulating an actor's turn
28+
// TODO: I'll need a way of simulating an actor's turn
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,71 @@
1-
namespace MattEland.FSharpGeneticAlgorithm.Logic
1+
module MattEland.FSharpGeneticAlgorithm.Logic.World
22

33
open System
44
open MattEland.FSharpGeneticAlgorithm.Logic.Actors
55
open MattEland.FSharpGeneticAlgorithm.Logic.WorldPos
66

7-
module World =
7+
let getRandomPos(maxX:int32, maxY:int32, getRandom): WorldPos =
8+
let x = getRandom maxX
9+
let y = getRandom maxY
10+
newPos x y
811

9-
let getRandomPos(maxX:int32, maxY:int32, random: Random): WorldPos =
10-
let x = random.Next(maxX) + 1
11-
let y = random.Next(maxY) + 1
12-
newPos x y
12+
let buildItemsArray (maxX:int32, maxY:int32, getRandom): Actor array =
13+
[| { Pos = getRandomPos(maxX, maxY, getRandom); ActorKind = Squirrel false }
14+
{ Pos = getRandomPos(maxX, maxY, getRandom); ActorKind = Tree }
15+
{ Pos = getRandomPos(maxX, maxY, getRandom); ActorKind = Doggo }
16+
{ Pos = getRandomPos(maxX, maxY, getRandom); ActorKind = Acorn }
17+
{ Pos = getRandomPos(maxX, maxY, getRandom); ActorKind = Rabbit }
18+
|]
19+
let hasInvalidlyPlacedItems (items: Actor array, maxX: int32, maxY: int32): bool =
20+
let mutable hasIssues = false
1321

14-
let buildItemsArray (maxX:int32, maxY:int32, random: Random): Actor array =
15-
[|
16-
createSquirrel (getRandomPos(maxX, maxY, random))
17-
createTree (getRandomPos(maxX, maxY, random))
18-
createDoggo (getRandomPos(maxX, maxY, random))
19-
createAcorn (getRandomPos(maxX, maxY, random))
20-
createRabbit (getRandomPos(maxX, maxY, random))
21-
|]
22+
for itemA in items do
23+
// Don't allow items to spawn in corners
24+
if (itemA.Pos.X = 1 || itemA.Pos.X = maxX) && (itemA.Pos.Y = 1 || itemA.Pos.Y = maxY) then
25+
hasIssues <- true
2226

23-
let hasInvalidlyPlacedItems (items: Actor array, maxX: int32, maxY: int32): bool =
24-
let mutable hasIssues = false
27+
for itemB in items do
28+
if itemA <> itemB then
2529

26-
for itemA in items do
27-
// Don't allow items to spawn in corners
28-
if (itemA.Pos.X = 1 || itemA.Pos.X = maxX) && (itemA.Pos.Y = 1 || itemA.Pos.Y = maxY) then
29-
hasIssues <- true
30+
// Don't allow two objects to start next to each other
31+
if isAdjacentTo itemA.Pos itemB.Pos then
32+
hasIssues <- true
33+
34+
hasIssues
3035

31-
for itemB in items do
32-
if itemA <> itemB then
36+
let generate (maxX:int32, maxY:int32, getRandom): Actor array =
37+
let mutable items: Actor array = buildItemsArray(maxX, maxY, getRandom)
3338

34-
// Don't allow two objects to start next to each other
35-
if isAdjacentTo itemA.Pos itemB.Pos then
36-
hasIssues <- true
37-
38-
hasIssues
39+
// It's possible to generate items in invalid starting configurations. Make sure we don't do that.
40+
while hasInvalidlyPlacedItems(items, maxX, maxY) do
41+
items <- buildItemsArray(maxX, maxY, getRandom)
3942

40-
let generate (maxX:int32, maxY:int32, random: Random): Actor array =
41-
let mutable items: Actor array = buildItemsArray(maxX, maxY, random)
43+
items
4244

43-
// It's possible to generate items in invalid starting configurations. Make sure we don't do that.
44-
while hasInvalidlyPlacedItems(items, maxX, maxY) do
45-
items <- buildItemsArray(maxX, maxY, random)
45+
type World =
46+
{ MaxX : int
47+
MaxY : int
48+
Squirrel : Actor
49+
Tree : Actor
50+
Doggo : Actor
51+
Acorn : Actor
52+
Rabbit : Actor }
53+
member this.Actors = [| this.Squirrel; this.Tree; this.Doggo; this.Acorn; this.Rabbit |]
54+
let makeWorld maxX maxY random =
55+
let actors = generate(maxX, maxY, random)
56+
{ MaxX = maxX
57+
MaxY = maxY
58+
Squirrel = actors.[0]
59+
Tree = actors.[1]
60+
Doggo = actors.[2]
61+
Acorn = actors.[3]
62+
Rabbit = actors.[4] }
4663

47-
items
64+
let getCharacterAtCell(x, y) (world:World) =
65+
let actorAtCell =
66+
world.Actors
67+
|> Seq.tryFind(fun actor -> actor.Pos.X = x && actor.Pos.Y = y)
4868

49-
type World (maxX: int32, maxY: int32, random: Random) =
50-
let actors = generate(maxX, maxY, random)
51-
member this.Actors = actors
52-
member this.MaxX = maxX
53-
member this.MaxY = maxY
54-
55-
member this.Squirrel = actors.[0]
56-
member this.Tree = actors.[1]
57-
member this.Doggo = actors.[2]
58-
member this.Acorn = actors.[3]
59-
member this.Rabbit = actors.[4]
60-
61-
member this.GetCharacterAtCell(x, y) =
62-
let mutable char = '.'
63-
for actor in this.Actors do
64-
if actor.Pos.X = x && actor.Pos.Y = y then
65-
char <- actor.Character
66-
char
69+
match actorAtCell with
70+
| Some actor -> getChar actor
71+
| None -> '.'

0 commit comments

Comments
 (0)