F# Enumerations

F# Discriminated Unions are amazing. To give a common example using shapes, they simplify this C#

 
    interface IShape
    {
        double Area { get; }
    }
 
    class Circle : IShape
    {
        public double Radius { get; }
        public double Area { get; }
 
        public Circle(double radius)
        {
            Radius = radius;
            Area = Radius * Radius * Math.PI;
        }
    }
 
    class Rectangle : IShape
    {
        public double Width { get; }
        public double Height { get; }
        public double Area { get; }
 
        public Rectangle(double width, double height)
        {
            Width = width;
            Height = height;
            Area = Width * Height;
        }
    }

to this F#:

type Shape = 
  | Rectangle of Width : float * Height : float
  | Circle of Radius : float
  member this.Area = 
    match this with
    | Rectangle(width, height) -> (width * height)
    | Circle(radius) -> (radius * radius * Math.PI)

This F# Area property also illustrates something powerful gained: pattern matching on a discriminated union. While logic with pattern matching is very powerful, F# discriminated unions have another use, as regular enumerations on a finite set.

type GreekDeity = 
  | Zeus
  | Poseidon
  | Hades
  member this.Realm =
    match this with 
    | Zeus -> "Sky"
    | Poseidon -> "Ocean"
    | Hades -> "Underworld"

A F# Discriminated Union and Enumerations are similiar with some distinct differences. To rewrite GreekDeity as an enum:

type GreekDeity = 
  | Zeus = 0
  | Poseidon = 1
  | Hades = 2
 
module DeityLogic = 
  let Realm (deity: GreekDeity) =
      match deity with 
      | GreekDeity.Zeus -> "Sky"
      | GreekDeity.Poseidon -> "Ocean"
      | GreekDeity.Hades -> "Underworld"
      | _ -> ""

The realm logic had to be broken out since enumerations cannot contain members. Also, since enumerations are not completely safe like discriminated unions, the blank case has to be handled.

Superficially, this seems like a step backward with some extra steps to run through. But we’ve gained something very important: ease of use from C#.

//This only works if GreekDeity is an enum.
foreach (string name in Enum.GetNames(typeof(Logic.GreekDeity)))
{
    Console.WriteLine(name);
}
 
Console.WriteLine();
 
foreach (Logic.GreekDeity deity in Enum.GetValues(typeof(Logic.GreekDeity)).Cast<Logic.GreekDeity>())
{
    string realm = Logic.DeityLogic.Realm(deity);
    Console.WriteLine("Deity \"{0}\" - Realm \"{1}\"", deity, realm);
}
Zeus
Poseidon
Hades

Deity "Zeus" - Realm "Sky"
Deity "Poseidon" - Realm "Ocean"
Deity "Hades" - Realm "Underworld"

This is important because a common use case for F# is to be business logic for C#. An enumeration is also easier to serialize and deserialize since it’s just an integer. Example code.

Morgemil Part 19 – Throw me for a loop

Games require human input. Games that do not require human input are simulations.

At the heart of every game is a way of acquiring the player’s input. So most games have the concept of a game loop. A game loop keeps the game running by continuously checking for user input, drawing to the screen, and updating state internally.

I have a basic game loop with some mocked up data

open Morgemil.Core
open Morgemil.Logic
 
[<EntryPoint>]
let main argv = 
  let player = 
    { Entity.Id = EntityId 3
      Type = EntityType.Person }
 
  let position = 
    { PositionComponent.EntityId = player.Id
      Position = Vector2i(5, 5)
      Mobile = true }
 
  let controller = 
    { PlayerComponent.EntityId = player.Id
      IsHumanControlled = true }
 
  let resource = 
    { ResourceComponent.EntityId = player.Id
      ResourceAmount = 50.0 }
 
  let level = 
    Morgemil.Core.DungeonGeneration.Generate({ DungeonParameter.Depth = 1
                                               RngSeed = 5
                                               Type = DungeonGenerationType.Square })
 
  let game = Morgemil.Logic.Game(level, [| player |], [| position |], [| controller |], [| resource |])
 
  let rec _continue() = 
    game.Update()
    let key = System.Console.ReadKey()
 
    let direction = 
      match key.Key with
      | System.ConsoleKey.W -> Vector2i(-1, 0)
      | System.ConsoleKey.E -> Vector2i(1, 0)
      | System.ConsoleKey.N -> Vector2i(0, -1)
      | System.ConsoleKey.S -> Vector2i(0, 1)
      | _ -> Vector2i()
    game.HumanRequest({ RequestedMovement.EntityId = player.Id
                        Direction = direction })
    |> ignore
    _continue()
  _continue()
  0

Running this from console gives this output
19_1
Those messages shown is the results of each turn as generated by this engine

namespace Morgemil.Logic
 
open Morgemil.Core
 
type Game(level : Level, entities : seq<Entity>, positions : seq<PositionComponent>, players : seq<PlayerComponent>, resources : seq<ResourceComponent>) = 
  let _world = World(level, Set.ofSeq (positions), Set.ofSeq (resources), Set.ofSeq (players))
  //TODO: fix
  let mutable _globalTurnQueue = (entities |> Seq.toList).Head
 
  let _handleRequest request = 
    TurnBuilder () { 
      match request with
      | EventResult.EntityMovementRequested(req) -> 
        let old_position = _world.Spatial.[req.EntityId]
        //TODO: Check that this move is actually valid
        let new_position = 
          _world.Spatial.Replace(old_position, { old_position with Position = old_position.Position + req.Direction })
        yield Message.PositionChange(old_position, new_position)
        //Movement takes one resource. More for testing purposes. 
        let old_resource = _world.Resources.[req.EntityId]
        let new_resource = 
          _world.Resources.Replace
            (old_resource, { old_resource with ResourceAmount = old_resource.ResourceAmount - 1.0 })
        yield Message.ResourceChange(old_resource, new_resource)
      | _ -> ()
    }
 
  member this.Update() = 
    let nextEntity = _globalTurnQueue //TODO: Actually have more than one entity (the player)
    let player = _world.Players.[nextEntity.Id]
    match player.IsHumanControlled with
    | true -> ()
    | false -> () //TODO: process AI turn
 
  ///Humans can only move units right now
  member this.HumanRequest(request : RequestedMovement) = 
    let results = TurnQueue.HandleMessages _handleRequest (EventResult.EntityMovementRequested request)
    //TODO: Display results through gui
    printfn ""
    results |> Seq.iter (fun res -> printfn "%A" res)
    true

Notice something interesting about those messages’ names? They are verbs in the past tense. The messages are saying that something happened. It’s then up to everything else to react.

The ability for the human to interact with the system is this method

  ///Humans can only move units right now
  member this.HumanRequest(request : RequestedMovement) = 
    let results = TurnQueue.HandleMessages _handleRequest (EventResult.EntityMovementRequested request)
    //TODO: Display results through gui
    printfn ""
    results |> Seq.iter (fun res -> printfn "%A" res)
    true

Given a human input, the game loop will process while messages are still being generated. That happens thanks to this method

    let results = TurnQueue.HandleMessages _handleRequest (EventResult.EntityMovementRequested request)

The logic in that is a continuous loop while messages are available to process

namespace Morgemil.Logic
 
///The callback to handle a request and results.
type EventMessageHandler = EventResult -> TurnStep
 
///Receives and processes events that make up a turn.
///Example: The action of moving onto a tile with a trap causes a new message chain starting with the trap's activation.
module TurnQueue = 
  let HandleMessages (handler : EventMessageHandler) (initialRequest : EventResult) : TurnStep = 
    let _processedEvents = new System.Collections.Generic.List<EventResult>()
    let _eventQueue = new System.Collections.Generic.Queue<EventResult>()
 
    let rec _handle() = 
      match _eventQueue.Count with
      | 0 -> ()
      | _ -> 
        let request = _eventQueue.Dequeue()
        request |> _processedEvents.Add
        request
        |> handler
        |> Seq.iter _eventQueue.Enqueue
        _handle()
    _eventQueue.Enqueue(initialRequest)
    _handle()
    (List.ofSeq _processedEvents)

See that fancy type definition of a callback at the top?

///The callback to handle a request and results.
type EventMessageHandler = EventResult -> TurnStep

Perhaps the definition of an EventResult will start to clear things up

namespace Morgemil.Logic
 
open Morgemil.Core
 
type RequestedMovement = 
  { EntityId : EntityId
    Direction : Vector2i }
 
type ResultMoved = 
  { EntityId : EntityId
    MovedFrom : Vector2i
    MovedTo : Vector2i }
 
type ResultResourceChanged = 
  { EntityId : EntityId
    OldValue : double
    NewValue : double }
  member this.ResourceChanged = this.OldValue - this.NewValue
 
///This represents the results of an action
type EventResult = 
  | EntityMoved of ResultMoved
  | EntityMovementRequested of RequestedMovement
  | EntityResourceChanged of ResultResourceChanged

But that doesn’t say what a TurnStep is

type TurnStep = List<EventResult>

So an EventMessageHandler is a type definition for a function that given an EventResult, returns a list of EventResult.

///The callback to handle a request and results.
type EventMessageHandler = EventResult -> TurnStep

The game engine has a method which is exactly that:

  let _handleRequest request = 
    TurnBuilder () { 
      match request with
      | EventResult.EntityMovementRequested(req) -> 
        let old_position = _world.Spatial.[req.EntityId]
        //TODO: Check that this move is actually valid
        let new_position = 
          _world.Spatial.Replace(old_position, { old_position with Position = old_position.Position + req.Direction })
        yield Message.PositionChange(old_position, new_position)
        //Movement takes one resource. More for testing purposes. 
        let old_resource = _world.Resources.[req.EntityId]
        let new_resource = 
          _world.Resources.Replace
            (old_resource, { old_resource with ResourceAmount = old_resource.ResourceAmount - 1.0 })
        yield Message.ResourceChange(old_resource, new_resource)
      | _ -> ()
    }

Now you might notice that the function in no way returns a list of event results. But… it does yield EventResult within the context of a TurnBuilder.

  let _handleRequest request = 
    TurnBuilder () { 
      ...
    }

A TurnBuilder is a computation expression which takes away the hassle of having to keep track of a return list as it does it for me.

type TurnBuilder() = 
  member this.Bind(x, f) = f x
  member this.Zero() = TurnStep.Empty
  member this.Yield(expr : EventResult) : TurnStep = [ expr ]
  member this.Return(expr) = TurnStep.Empty
  member this.Yield(expr) = TurnStep.Empty
  member this.Combine(a : TurnStep, b : TurnStep) = List.concat [ a; b ]
  member this.Delay(f) = f()

This post ended up being me throwing a lot of code out there with minimal explanation. That’s probably a sign for me to nail down some of this logic.

Morgemil Part 18 – Side effects

A beautiful thing about functional languages is that they are not fond of side-effects. And that’s what makes the following code so ugly, it’s full of side effects.

But first, a refresher on the entity and its components defined so far:

type EntityId = 
  | EntityId of int
 
type EntityType = 
  | Person
  | Door
  | Stairs
  | Object
  | Spell
 
type Entity = 
  { Id : EntityId
    Type : EntityType }
 
type PositionComponent = 
  { EntityId : EntityId
    Position : Vector2i
    Mobile : bool }
 
type PlayerComponent = 
  { EntityId : EntityId
    IsHumanControlled : bool }
 
type ResourceComponent = 
  { EntityId : EntityId
    ResourceAmount : double }

From there I created “component systems” which is fancy terminology for wrapping a dictionary: a way for me to access components by their EntityId efficiently. Do note that the coloring on the following code snippet is thrown off by the single quote in the generic type ‘T

open Morgemil.Core
open Morgemil.Logic.Extensions
 
type ComponentSystem<'T when 'T : comparison>(initialComponents : Set<'T>, getId : 'T -> EntityId) = 
 
  let mutable _components : Map<EntityId, 'T> = 
    [ for item in initialComponents -> (getId item), item ]
    |> Map.ofSeq
 
  let _added = new Event<'T>()
  let _removed = new Event<'T>()
  let _replaced = new Event<'T * 'T>()
  let _matches entityId (item : 'T) = (getId (item) = entityId)
  new(getId) = ComponentSystem(Set.empty, getId)
  member this.Components = _components |> Seq.map (fun t -> t.Value)
  member this.ComponentRemoved = _removed.Publish
  member this.ComponentAdded = _added.Publish
  member this.ComponentReplaced = _replaced.Publish
 
  member this.Find entityId = 
    match _components.ContainsKey(entityId) with
    | true -> Some(_components.[entityId])
    | _ -> None
 
  member this.Item 
    with get (entityId : EntityId) = _components.[entityId]
 
  member this.Add item = 
    _components <- _components.Add(getId item, item)
    _added.Trigger(item)
 
  member this.Remove item = 
    _components <- _components.Remove(getId item)
    _removed.Trigger(item)
 
  member this.Remove entityId = this.Remove _components.[entityId]
 
  member this.Replace(old_value : 'T, new_value : 'T) = 
    _components <- _components.Replace(getId old_value, new_value)
    _replaced.Trigger(old_value, new_value)
    new_value
 
  member this.Replace(entityId : EntityId, replacement) = 
    let old_value = this.[entityId]
    (old_value, this.Replace(old_value, replacement old_value))

There are multiple component systems in a world. One system for each component type.

open Morgemil.Core
 
type World(level, spatialComponents, resourceComponents, playerComponents) = 
  let _spatial = SpatialSystem(spatialComponents)
  let _resources = ComponentSystem<ResourceComponent>(resourceComponents, (fun resource -> resource.EntityId))
  let _players = ComponentSystem<PlayerComponent>(playerComponents, (fun player -> player.EntityId))
  let _level : Level = level
  member this.Spatial = _spatial
  member this.Level = _level
  member this.Resources = _resources
  member this.Players = _players
  static member Empty = World(Level.Empty, Set.empty, Set.empty, Set.empty)

Wait a second, I just said “One system for each component type” but there are only two components here…

The third component “PositionComponent” is there in a specialized system: “SpatialSystem”.

open Morgemil.Core
 
type SpatialSystem(initial) = 
  inherit ComponentSystem<PositionComponent>(initial, (fun position -> position.EntityId))
  member this.InRectangle(area : Rectangle) = this.Components |> Seq.filter (fun value -> area.Contains(value.Position))
  static member Empty = SpatialSystem(Set.empty)

This inherits from ComponentSystem, and tacks on some extra functionality. I can now easily check for entities that are positioned inside a rectangle. A good spatial system would do that efficiently except I’m more concerned with correctness right now.

Want an example? How about my basic game engine right now

open Morgemil.Core
open Morgemil.Logic.Extensions
 
type Game(level : Level, entities : seq<Entity>, positions : seq<PositionComponent>, players : seq<PlayerComponent>, resources : seq<ResourceComponent>) = 
  let _world = World(level, Set.ofSeq (positions), Set.ofSeq (resources), Set.ofSeq (players))
  //TODO: fix
  let mutable _globalTurnQueue = (entities |> Seq.toList).Head
 
  let _handleRequest request = 
    TurnBuilder () { 
      match request with
      | EventResult.EntityMovementRequested(req) -> 
        let old_position = _world.Spatial.[req.EntityId]
        //TODO: Check that this move is actually valid
        let new_position = 
          _world.Spatial.Replace(old_position, { old_position with Position = old_position.Position + req.Direction })
        yield Message.PositionChange(old_position, new_position)
        //Movement takes one resource. More for testing purposes. 
        let old_resource = _world.Resources.[req.EntityId]
        let new_resource = 
          _world.Resources.Replace
            (old_resource, { old_resource with ResourceAmount = old_resource.ResourceAmount - 1.0 })
        yield Message.ResourceChange(old_resource, new_resource)
      | _ -> ()
    }
 
  member this.Update() = 
    let nextEntity = _globalTurnQueue //TODO: Actually have more than one entity (the player)
    let player = _world.Players.[nextEntity.Id]
    match player.IsHumanControlled with
    | true -> ()
    | false -> () //TODO: process AI turn
 
  ///Humans can only move units right now
  member this.HumanRequest(request : RequestedMovement) = 
    let results = TurnQueue.HandleMessages _handleRequest (EventResult.EntityMovementRequested request)
    //TODO: Display results through gui
    printfn ""
    results |> Seq.iter (fun res -> printfn "%A" res)
    true

The world is created from passed in data, doesn’t really matter how right now.

What matters is the ease of use

      match request with
      | EventResult.EntityMovementRequested(req) -> 
        let old_position = _world.Spatial.[req.EntityId]
        //TODO: Check that this move is actually valid
        let new_position = 
          _world.Spatial.Replace(old_position, { old_position with Position = old_position.Position + req.Direction })
        yield Message.PositionChange(old_position, new_position)
        //Movement takes one resource. More for testing purposes. 
        let old_resource = _world.Resources.[req.EntityId]
        let new_resource = 
          _world.Resources.Replace
            (old_resource, { old_resource with ResourceAmount = old_resource.ResourceAmount - 1.0 })
        yield Message.ResourceChange(old_resource, new_resource)
      | _ -> ()

When an entity has movement requested, I’m taking the old position and replacing it with a new value, which is the old position + direction requested.

        let old_position = _world.Spatial.[req.EntityId]
        //TODO: Check that this move is actually valid
        let new_position = 
          _world.Spatial.Replace(old_position, { old_position with Position = old_position.Position + req.Direction })

I do the same thing with the resources.

        //Movement takes one resource. More for testing purposes. 
        let old_resource = _world.Resources.[req.EntityId]
        let new_resource = 
          _world.Resources.Replace
            (old_resource, { old_resource with ResourceAmount = old_resource.ResourceAmount - 1.0 })

This has side effects, the list of components is constantly changing with removal and insertions. But the side effects are limited in that I’m never updating the component’s value directly. the only way for it to be updated, is for that entire component associated with that Entity to be replaced.

Even with this compromise, record immutability has some fantastic possibilities. I can pass those component records in messages with wild abandon. Indeed, I’ve been doing so and that is next week’s topic.

Morgemil Part 17 – Engine Rework Chapter 2

Here’s where I left off. Everything is made up of behaviors.

interface ICastSpell
{
  void Behave(Spell spell);
}
class DungeonMonster
{
  public IMove MoveBehavior {get; set; }
  public IAttack AttackBehavior {get; set; }
  public ICastSpell SpellBehavior {get; set }
  public void Move(Direction direction)
  {
     MoveBehavior.Behave(direction);
  }
  public void Attack(Direction direction)
  {
     AttackBehavior.Behave(direction);
  }
  public void CastSpell(Spell spell)
  {
     SpellBehavior.Behave(spell);
  }
}
class LightMold: DungeonMonster
{  
  public LightMold()
  {   
    MoveBehavior = new Immobile();
    AttackBehavior = new Defenseless();
    SpellBehavior = new EvilSpellCast(); //LightMolds cast evil spells apparently.
  }
}
class Goblin: DungeonMonster
{ 
  public Goblin()
  {
    MoveBehavior = new Mobile();
    AttackBehavior = new Defensive();
    SpellBehavior = new NoSpells(); //Goblins have no magical aptitude
  }
}

Remember, the idea is composition. Composition should provide flexibility. I plan to do it through the use of entities. So here’s my implementation of an entity:

type EntityId = EntityId of int
 
type EntityType = 
  | Person
  | Door
  | Stairs
  | Object
 
type Entity = 
  { Id : EntityId
    Type : EntityType }

It is an integer (technically a single case discriminated union) with a bit of immutable meta-data.

An entity has nothing. All info about an entity is contained in components.

type PositionComponent = 
  { EntityId : EntityId
    Position : Vector2i
    Mobile : bool }
 
type PlayerComponent = 
  { EntityId : EntityId
    IsHumanControlled : bool }
 
type ResourceComponent = 
  { EntityId : EntityId
    ResourceAmount : double }

Entities and components are linked together only by the entity’s ID. Certainly a far departure from Object-Oriented convention. But does this remind you of anything?

Relational databases are fond of using an integer as a primary key to make nice foreign key. So the entity and components above could look like this
17_1
This has a couple benefits:

  • Composition of behavior through whether an entity has a component(s) or not.
  • No longer have to maintain giant object hierarchies
  • Systems can operate on isolated information. Example: Field-of-view, collision detection, and spatial operations in general should only need to operate on a collection of PositionComponents. They don’t care what other components an entity may have.
  • Serialization is much easier. This is important when loading or saving game state, sending game state over network, and logging.

There are other benefits as well, but those will become apparent over time and examples. Of course there are drawbacks too, but those will become apparent with time as well.

Morgemil Part 16 – Engine Rework Chapter 1

I’ve come up with some new design decisions in my rework of the engine:

  • Everything inside a level is an entity
  • Logic is reactionary
  • Mutability is not a complete evil so long as it is contained

Entities are this concept that things are better through composition. Now this is not a new idea, object oriented programmers recognize the idea of preferring composition over inheritance.

So in the beginning was inheritance

class DungeonMonster
{
  public void Move(Direction direction)...
  public void Attack(Direction direction)...
}
class Goblin: DungeonMonster
{
   ...
}

This seems pretty straightforward, monsters can move and attack. But there’s only one monster, the goblin.

Now let’s add some unique monsters, like some evil sentient plant. Not too different from those trees in the Wizard of Oz that throw apples at Dorothy.

class EvilAppleThrowingTree: DungeonMonster
{
  ...
}

Now we’re in trouble: not only are there evil trees, but they don’t work well with the base class DungeonMonster because trees can’t move. So, next step is to rework those classes:

class DungeonMonster
{
  public virtual void Move(Direction direction)...  //Default is now able to move.
  public void Attack(Direction direction)...
}
class Goblin: DungeonMonster
{
  //Don't override Move.
}
class EvilAppleThrowingTree: DungeonMonster
{
  public override void Move(Direction direction)...  //Does not allow move
}

Boom! That wasn’t so bad. We can just override whatever behavior whenever we want.

Let’s add a few more monsters to make this game really unique:

class PoisonIvy: DungeonMonster
{  
  public override void Move(Direction direction)...  //Does not allow move
}
class Mouse: DungeonMonster
{
  //Shouldn't be able to attack. Just moves around and stuff. 
}

Now this is getting to be a problem. PoisonIvy also can’t move. So I have the option of either copy-pasting code from EvilAppleThrowingTree’s Move function into PoisonIvy’s Move function which is stupid, or I could add another layer to the heirarchy.

class ImmobileDungeonMonster: DungeonMonster
{
  public override void Move(Direction direction)...  //unable to move.
}
class PoisonIvy: ImmobileDungeonMonster
{  
}
class EvilAppleThrowingTree: ImmobileDungeonMonster
{
}

But wait, didn’t I add a mouse too?

class Mouse: DungeonMonster
{
  //Shouldn't be able to attack. Just moves around and stuff. 
}

I guess the Attack function needs the same reworking that the Move function did. This is all the code so far

class DungeonMonster
{
  public virtual void Move(Direction direction)...  //Default is now able to move.
  public virtual void Attack(Direction direction)...
}
class ImmobileDungeonMonster: DungeonMonster
{
  public override void Move(Direction direction)...  //unable to move.
}
class Goblin: DungeonMonster
{
  //Don't override Move.
  //Don't override Attack.
}
class Mouse: DungeonMonster
{
  public override void Attack(Direction direction) ... //Shouldn't be able to attack.
}
class PoisonIvy: ImmobileDungeonMonster
{  
}
class EvilAppleThrowingTree: ImmobileDungeonMonster
{
}

I could keep going on and adding more monsters, but I’m trying to make the point that a dungeon monster can have many attributes. And a multitude of monsters means these attributes will be combined in many different ways. For example: I might just add a LightMold. A LightMold is a sentient fungus that cannot move, but it can cast spells.

class LightMold: ImmobileDungeonMonster
{  
  public override void Attack(Direction direction) ... //Shouldn't be able to attack. 
}

We also need to add the spell-casting attribute to DungeonMonster.

class DungeonMonster
{
  public virtual void Move(Direction direction)...  //Default is now able to move.
  public virtual void Attack(Direction direction)...
  public virtual void CastSpell(Spell spell)...  //Most monsters can't cast spells. So virtual
}

Guess what? An egregious sin has happened for now I have to go back and override all the other monster’s CastSpell function to do nothing. I could have the base class DungeonMonster do nothing with spells, but then the problem of copy pasting spell casting logic to all classes that can cast spells appears (we haven’t even added evil wizards yet). Not to mention the fact that I just copy pasted the Mouse’s Attack function override.

In short, I’ve made a mess of everything.

Let’s rework this using composition. A dungeon monster is made up of behaviors. A behavior is how something acts.

interface IMove
{
  void Behave(Direction direction)
}
interface IAttack
{
  void Behave(Direction direction)
}
 
class DungeonMonster
{
  public IMove MoveBehavior {get; set; }
  public IAttack AttackBehavior {get; set; }
  public void Move(Direction direction)
  {
     MoveBehavior.Behave(direction);
  }
  public void Attack(Direction direction)
  {
     AttackBehavior.Behave(direction);
  }
}

I don’t care what those behaviors do so long as they behave. They’re just an interface.

class Mobile: IMove
{
  public void Behave(Direction direction) ... //Move
}
class Immobile: IMove
{
  public void Behave(Direction direction) ... //Don't Move
}
 
class Defensive: IAttack 
{
  public void Behave(Direction direction) ... //Attack
}
class Defenseless: IAttack 
{
  public void Behave(Direction direction) ... //Don't Attack
}

I have any kind of monster I could ever wish for now by combining these behaviors

class Goblin: DungeonMonster
{ 
  public Goblin()
  {
    MoveBehavior = new Mobile();
    AttackBehavior = new Defensive();
  }
}

Adding an attribute to DungeonMonster got a lot easier too. No more complex inheritance or copy-pasting logic. All logic is sitting in those behaviors. Combine the behaviors to make the logic how you wish.

interface ICastSpell
{
  void Behave(Spell spell);
}
class DungeonMonster
{
  public IMove MoveBehavior {get; set; }
  public IAttack AttackBehavior {get; set; }
  public ICastSpell SpellBehavior {get; set }
  public void Move(Direction direction)
  {
     MoveBehavior.Behave(direction);
  }
  public void Attack(Direction direction)
  {
     AttackBehavior.Behave(direction);
  }
  public void CastSpell(Spell spell)
  {
     SpellBehavior.Behave(spell);
  }
}
class LightMold: DungeonMonster
{  
  public LightMold()
  {   
    MoveBehavior = new Immobile();
    AttackBehavior = new Defenseless();
    SpellBehavior = new EvilSpellCast(); //LightMolds cast evil spells apparently.
  }
}
class Goblin: DungeonMonster
{ 
  public Goblin()
  {
    MoveBehavior = new Mobile();
    AttackBehavior = new Defensive();
    SpellBehavior = new NoSpells(); //Goblins have no magical aptitude
  }
}

This is not what I’m actually doing, but it is a great step forward. I’ve laid out the code above because that is actually how I arrived at my current solution. So join me next time when I explain my understanding of entities.

Morgemil Part 15 – A change of direction

When I started this project I came up with an overly complicated plan for an open world system using chunks.

/// <summary>
/// A 2d Chunk of tiles. Used in the overworld and in dungeons.
/// </summary>
/// <param name="area">edge-inclusive area of tiles</param>
/// <param name="tiles">2d array [row,column]</param>
type Chunk = 
  { Area : Morgemil.Math.Rectangle
    Tiles : TileDefinition array }  
 
  /// <summary>
  /// Local coordinates. Zero-based indices: [y * area.Width + x]
  /// No check for valid coordinates
  /// </summary>
  member this.TileLocal(vec2 : Morgemil.Math.Vector2i) = this.Tiles.[vec2.Y * this.Area.Width + vec2.X]
 
  /// <summary>
  /// Global coordinates. Zero-based indices relative to this.Area.Position
  ///</summary>
  member this.Tile(vec2 : Morgemil.Math.Vector2i) = this.Tiles.[this.Area.FlatCoord(vec2)]
 
  /// <summary>
  /// True if every tile is TileDefinition.Default
  /// </summary>
  member this.IsEmpty = this.Tiles |> Array.forall (TileDefinition.IsDefault)
 
  ///Seq<Math.Vector2i * TileDefinition>
  member this.TileCoordinates = Seq.map (fun coord -> coord, (this.Tile coord)) this.Area.Coordinates

As it turns out, generating chunks in a continuous world is not the difficult part. The difficult part is keeping a continuous world. What happens when buildings are spread across multiple chunks? When pathfinding needs to go across unloaded chunks? When loaded entities are no longer in loaded chunks? How to continuously write/read chunks from files?

So I’ve replaced every instance where I’m operating on a collection of chunks, with an operation on a single level. The level structure has simplified from the chunk since there is now no difference between local coordinates and global coordinates.

/// <summary>
/// A 2d Level.
/// </summary>
/// <param name="area">edge-inclusive area of tiles</param>
/// <param name="tiles">2d array [row,column]</param>
type Level = 
  { ///[0,0] (MaxX,MaxY)
    Area : Morgemil.Math.Rectangle
    Tiles : TileDefinition array }
 
  /// <summary>
  /// Global coordinates. Zero-based indices relative to this.Area.Position
  ///</summary>
  member this.Tile(vec2 : Morgemil.Math.Vector2i) = this.Tiles.[this.Area.FlatCoord(vec2)]
 
  ///Seq<Math.Vector2i * TileDefinition>
  member this.TileCoordinates = Seq.map (fun coord -> coord, (this.Tile coord)) this.Area.Coordinates

As you might imagine, this also signifies a change in direction. From an open-world system towards a more traditional dungeon crawler.

Morgemil Part 14 – Minimalism

After a month’s sojourn I have returned to my long term project with more knowledge. I’m replacing my Object-Oriented F# that looks like

namespace Morgemil.Map
 
type TileType =
  | Void = 0
  | Land = 1
  | Water = 2
 
/// <summary>
/// This defines a tile. Each tile instance will hold a reference to one of these as a base attribute.
/// </summary>
type TileDefinition(id : int, name : string, description : string, blocksMovement : bool, blocksSight : bool, tileType : TileType) =
 
  /// <summary>
  /// Use this in file storage. When saving a chunk or map, use this ID.
  /// </summary>
  member this.ID = id
 
  /// <summary>
  /// A short name. eg: "Lush Grass"
  /// </summary>
  member this.Name = name
 
  /// <summary>
  /// A long description. eg: "Beware the burning sand. Scorpions and asps make their home here."
  /// </summary>
  member this.Description = description
 
  /// <summary>
  /// If true, this tile ALWAYS blocks ALL movement by ANYTHING.
  /// </summary>
  member this.BlocksMovement = blocksMovement
 
  /// <summary>
  /// If true, this tile ALWAYS blocks ALL Line-Of-Sight of ANYTHING.
  /// </summary>
  member this.BlocksSight = blocksSight
 
  /// <summary>
  /// The tile type determines some things like if they can breath or not.
  /// </summary>
  member this.Type = tileType
 
  /// <summary>
  /// A default, minimal definition. Could be used as the edge of the map blackness?
  /// </summary>
  static member Default = TileDefinition(0, "Empty", "", true, true, TileType.Void)
 
  static member IsDefault(tile : TileDefinition) = (tile.ID = TileDefinition.Default.ID)

with something that looks a bit more F#-ish.

namespace Morgemil.Map
 
type TileType = 
  | Void = 0
  | Land = 1
  | Water = 2
 
/// This defines a tile. Each tile instance will hold a reference to one of these as a base attribute.
type TileDefinition = 
  { ///Use this in file storage. When saving a chunk or map, use this ID.
    Id : int
    /// A short name. eg: "Lush Grass"
    Name : string
    ///A long description. eg: "Beware the burning sand. Scorpions and asps make their home here."
    Description : string
    ///If true, this tile ALWAYS blocks ALL movement by ANYTHING.
    BlocksMovement : bool
    ///If true, this tile ALWAYS blocks ALL Line-Of-Sight of ANYTHING.
    BlocksSight : bool
    ///The tile type determines some things like if they can breath or not.
    TileType : TileType }
 
  /// <summary>
  /// A default, minimal definition. Could be used as the edge of the map blackness?
  /// </summary>
  static member Default = 
    { Id = 0
      Name = "Empty"
      Description = ""
      BlocksMovement = true
      BlocksSight = true
      TileType = TileType.Void }
 
  static member IsDefault(tile : TileDefinition) = (tile.Id = TileDefinition.Default.Id)

I think this is a lot more concise and removes unnecessary encapsulation.

Here is another example of something I’ve learned. I have a test project that I script odd things in to test out concepts with and I’ve had this “Walkabout” class that I use to test movement from a console window window. It was defined as this

type Walkabout(dungeon : Morgemil.Map.Chunk, player : Morgemil.Game.Person) =
  member this.Dungeon = dungeon
  member this.Player = player
  member this.Act(act : Morgemil.Game.Actions) =
    let offset =
      match act with
      | Morgemil.Game.MoveEast -> Vector2i(1, 0)
      | Morgemil.Game.MoveWest -> Vector2i(-1, 0)
      | Morgemil.Game.MoveNorth -> Vector2i(0, 1)
      | Morgemil.Game.MoveSouth -> Vector2i(0, -1)
    Walkabout(dungeon,
              { Id = player.Id
                //Body = player.Body
                Position = player.Position + offset })

For now, whether it is good design or not, the position is included in a “Person” definition. So the only thing I wanted to change on that player was the Position, but I was having to pass everything which was a pain. Then I found the context of the “with” keyword with Record Types.

type Walkabout(dungeon : Morgemil.Map.Chunk, player : Morgemil.Game.Person) = 
  member this.Dungeon = dungeon
  member this.Player = player
  member this.Act(act : Morgemil.Game.Actions) = 
    let offset = 
      match act with
      | Morgemil.Game.MoveEast -> Vector2i(1, 0)
      | Morgemil.Game.MoveWest -> Vector2i(-1, 0)
      | Morgemil.Game.MoveNorth -> Vector2i(0, 1)
      | Morgemil.Game.MoveSouth -> Vector2i(0, -1)
    Walkabout(dungeon, { player with Position = player.Position + offset })

So the person can now be passed around effortlessly

///Wight, human, etc.
type Race = 
  { Id : int
    ///Proper noun
    Noun : string
    ///Proper adjective
    Adjective : string
    ///User-visible description
    Description : string }
 
///A body with base stats/characteristics.
///Any mutable data is in a higher level.
type Body =
  { Id : int
    Race : Race
    ///Normal "bodies" fit in one tile (1,1). Bosses and the largest entities can take up multiple tiles.
    Size : Morgemil.Math.Vector2i }
 
///This is a high level view of an entity. Typically holds any mutable data (can change each game step).
type Person = 
  { Id : int
    Body : Body
    ///This plus Body.Size constructs the person's hitbox
    Position : Morgemil.Math.Vector2i }

I’m testing this by console since my Monogame view is still very lacking.

let Instruct() = printfn "(E)ast (N)orth (W)est (S)outh (Q)uit"
 
let Prompt() = 
  let response = System.Console.ReadLine()
  match System.Char.ToLower(response.[0]) with
  | 'e' -> Some(Morgemil.Game.MoveEast)
  | 'n' -> Some(Morgemil.Game.MoveNorth)
  | 'w' -> Some(Morgemil.Game.MoveWest)
  | 's' -> Some(Morgemil.Game.MoveSouth)
  | _ -> None
 
let rec Continue (depth : int) (walkabout : Morgemil.Test.Walkabout) = 
  System.Console.WriteLine(walkabout.Player.Position)
  match Prompt() with
  | None -> ()
  | Some(act) -> 
    let filename = "map_test" + depth.ToString("0000") + ".bmp"
    let dungeonDraw = Morgemil.Test.DungeonVisualizer.Visualize [| walkabout.Dungeon |]
    Morgemil.Test.DungeonVisualizer.DrawPlayer walkabout.Player dungeonDraw
    dungeonDraw.Save(filename)
    Continue (depth + 1) (walkabout.Act act)
 
[<EntryPoint>]
let main argv = 
  let createdBspDungeon = Morgemil.Map.DungeonGeneration.Generate 656556
 
  let walkAbout = 
    Morgemil.Test.Walkabout(createdBspDungeon, 
                            { Id = 5
                              Body = 
                                { Id = 5
                                  Size = Morgemil.Math.Vector2i(1, 1)
                                  Race = 
                                    { Id = 5
                                      Noun = "Dwarf"
                                      Adjective = "Dwarven"
                                      Description = "I am a dwarf" } }
                              Position = Morgemil.Math.Vector2i(5, 5) })
  Instruct()
  Continue 0 walkAbout

Morgemil Part 11 – Someone has to live in this world

The first inhabitant of every game world should be the player himself. Not because he might have something interesting to do yet, but to do Usability Testing. The most important thing to a game is how the users view it. Games receive little points for being technically sound but magnitudes more for how fun it is.

Know who the first player is? The developer(s) should be the first player. The most effective builders of a product are those who use it themselves.

Continue reading Morgemil Part 11 – Someone has to live in this world