Building a roguelike in F# from scratch - Repo and Initial Types
programming·@woz.software·
0.000 HBDBuilding a roguelike in F# from scratch - Repo and Initial Types
 # Introduction This is the first of my development blogs for my roguelike started in [Building a roguelike in F# from scratch - Introduction](https://steemit.com/programming/@woz.software/building-a-rougelike-in-f-from-scratch-introduction) But first some house keeping. The project repo is now up on GitHub as [Woz.FSharpeRoguelike](https://github.com/WozSoftware/Woz.FSharpRoguelike), this will always be in sync or slightly ahead of the development blog. I will pick a suitable article picture once the project is named. In this post I am exploring the structure used to store a game world level. I am using this post as a form of [Rubber Duck Debugging](https://steemit.com/programming/@woz.software/rubber-duck-debugging) as talking about the structures make me think about them in more detail :) # Level Structure We will start with a means to hold a coordinate pair. I could have used a tuple for this but the record type gives us named values hence I am using the type. I will layer the functionality from my C# Vector object later later on, you can see that code in [Roguelike line of sight calculation](https://steemit.com/programming/@woz.software/roguelike-line-of-sight-calculation) ``` type Vector = {x: int; y: int} ``` Time to build the structure to hold a level tile map. For now a map will be a 2D array of Tiles and a width and height held in a vector. I do not plan on this being mutated hence the 2D array, that would be too costly to constantly mutate on a large map. ``` type Tile = Void | Floor | Wall | Water type Map = {tiles: Tile[][]; mapSize: Vector} ``` You can open, close and unlock door meaning they mutate. This is the reason they are not contained in Map, they will be in a dictionary keyed by their location in the level, a cheaper update solution. For now just the door states, you will see the map in the level. The string held against the locked door is the name of the key that opens it. ``` type Door = | Open | Closed | Locked of string // Key name ``` The item definition is just a basic place holder for the time being. The ItemId type is just to ensure we use the correct ID when talking about items, and the name should be self evident. As with doors, there will be a dictionary to say where items are in the map, when not held by an actor. ``` type ItemId = int type Item = {id: ItemId; name: string} ``` Actors are used to describe the Player and NPCs. By now you should be able to figure out the structure from the following code, this has been designed for best balance between flexibility, performance and update speed. It is simple to add more stats if and when needed. ``` type Stats = | Health | Strength | Intelligence | Stamina | Dexterity type Stat = {current: int; max: int} type ActorId = int type Actor = { id: int isNpc: bool name: string stats: Map<Stats, Stat> location: Vector backpack: List<ItemId> } ``` The level ties all of the above together. The only real surprise here is probably mapItems, I could have just stuffed the location in Item but that would not have been a viable field when held by an actor. So the choice was an Optional/Maybe Monad or a dictionary lookup. The extra dictionary trades lookup speed against update costs and complexity but for now it felt the more flexible choice. ``` type Level = { playerId: ActorId map: Map; doors: Map<Vector, Door>; actors: Map<ActorId, Actor> items: Map<ItemId, Item> mapItems: Map<Vector, List<ItemId>> } ``` That is actually a fair chunk of storage structure for a small amount of code. If you were to build this as classes in an OO language would be far more verbose, even more so if you forced immutability into the classes. Here is the full code from above unbroken by comments, not even a page :) ``` type Vector = {x: int; y: int} type Tile = Void | Floor | Wall | Water type Map = {tiles: Tile[][]; mapSize: Vector} type Door = | Open | Closed | Locked of string // Key name type ItemId = int type Item = {id: ItemId; name: string; } type Stats = | Health | Strength | Intelligence | Stamina | Dexterity type Stat = {current: int; max: int} type ActorId = int type Actor = { id: int isNpc: bool name: string stats: Map<Stats, Stat> location: Vector backpack: List<ItemId> } type Level = { playerId: ActorId map: Map; doors: Map<Vector, Door>; actors: Map<ActorId, Actor> items: Map<ItemId, Item> mapItems: Map<Vector, List<ItemId>> } ``` Ask if you have any questions on my choices and I will try to explain them Happy Coding Woz
👍 woz.software, mahdiyari, jonrhythmic, stefanonsense, teofilex11, samanthabonin, curie, velourex, meerkat, dunia, hendrikdegrote, anwenbaumeister, kushed, pharesim, utari, dartigan, badastroza, black-eye, ladycat, cwatch, peterehab, akaimyers, amartinezque, supersnooper221, siniceku, carping, daily-post, carbunco10, phonetix, justusagenstum, phonyfelix, free999enigma, writer1, steppingout23, epicdrilltime, ivarlothbrok, qed, clayboyn, ramzialhaddadtm, sstefan, mckenziegary, deveerei, johnyboi, elgeko, siddartha, brianjuice, derwinaugustus, cyrbyrus, marnee,