
I know many people who started programming because they wanted to write a game of their own. I myself had never done game programming but after I ran into articles about programming a roguelike in Haskell I decided to give it a try using F#.
First I wanted to get hero to move around on map.
These are the types I came up with:
namespace SharpRogue
module Types =
    type Coordinate = { x:int; y:int; }
    type Hero = {
        currentPosition : Coordinate;
        oldPosition : Coordinate;
    }
    type MapTile = {
        coordinate : Coordinate;
        tile : char;
    }
    type World = {
        tiles : MapTile list
        hero : Hero
    }
    type Input = 
        Up 
        | Down
        | Left
        | Right
        | Open
        | Exit
Game World consists of MapTiles and Hero. MapTile has a coordinate in the world and a char that symbolises content of the tile, for example symbol for wall is '#'. Hero has data of it's location(currentPosition) and location on previous turn(oldPosition). Input is a discriminated union of the possible inputs from the player.
// Graphics.fs
let hideCursor() = System.Console.SetCursorPosition(0,0)
let drawHero (hero:Hero, world:MapTile list) = 
    System.Console.
        SetCursorPosition(hero.currentPosition.x, hero.currentPosition.y)
    System.Console.Write '@'
    let found = List.find (Utils.findTile hero.oldPosition) world
    System.Console.
        SetCursorPosition(hero.oldPosition.x, hero.oldPosition.y)
    found.tile |> System.Console.Write
    hideCursor()
let drawTile (tile:MapTile) = 
    System.Console.
        SetCursorPosition(tile.coordinate.x, tile.coordinate.y)
    System.Console.Write tile.tile
let drawWorld world = 
    System.Console.Clear()
    List.map (fun x -> drawTile(x)) world |> ignore
let drawOpenDoor coordinate =
    System.Console.SetCursorPosition(coordinate.x, coordinate.y)
    System.Console.Write '-'
    hideCursor()
Since my roguelike is basically a console app the "graphics" module deals with writing out chars on the correct coordinate in the console. HideCursor-function is used to set cursor on the upper left corner of screen. Otherwise it will stay where a character has been written last. drawHero draws the @-character representing hero in currentPosition of hero record. Original tile from world record will replace @-character in oldPosition of hero. Originally I reprinted whole map after every move, but that caused screen to flicker.
The game logic is located in the Program.fs-file. Starting at the main function:
[<EntryPoint>]
let main argv = 
    generateCoordinates |> drawWorld
    let world = {
        hero = { 
                oldPosition = {x = 1; y = 1;}; 
                currentPosition = {x = 1; y = 1;}; 
        }; 
        tiles = generateCoordinates
    }
    gameLoop world
    0 // return an integer exit code
GenerateCoordinates breaks level map into coordinates and tiles. World initialized with hero starting from the top left corner.
GameLoop is a recursive function that draws hero, gets player input and calls gameLoop again with new state of the world.
let rec gameLoop world =
    drawHero (world.hero, world.tiles)
    let input = getInput()
    match input with
        | Exit -> ()
        | Open -> openDoor world.hero world |> gameLoop
        | _ -> {world with hero = (move input world);} |> gameLoop 
Move returns hero with updated coordinates. Movement is allowed if the wanted location is not a wall(#) or a closed door(+). OpenDoor replaces closed door(+) located next to the hero in the given direction with an open door(-).
let move direction world = 
    let hero = world.hero
    let newCoordinates = getNextCoordinate hero direction
    let found = List.find (Utils.findTile newCoordinates) world.tiles   
    match found.tile with
        | '#' -> hero
        | '+' -> hero         
        | _ -> {hero with currentPosition = newCoordinates; 
                oldPosition = hero.currentPosition} 
let openDoor hero world = 
    let direction = getInput()
    let coordinate = getNextCoordinate hero direction
    let tile = List.find (Utils.findTile coordinate) world.tiles
    if tile.tile = '+' then drawOpenDoor tile.coordinate
    let newTiles = List.map 
        (fun x -> 
            if x.coordinate = coordinate then { x with tile = '-'} 
            else x) world.tiles
    {world with hero = hero; tiles = newTiles}
Here is the amazing result!

Not quite Nethack yet, but we'll see what features future brings. The codes for SharpRogue can be found on GitHub.
Software professional with a passion for quality. Likes TDD and working in agile teams. Has worked with wide-range of technologies, backend and frontend, from C++ to Javascript. Currently very interested in functional programming.
