Author Archives: Faron Bracy

Using EditorConfig to Maintain a Consistent Coding Style

Have you ever tried to work on someone else’s code and realized that they have completely different code formatting than you are used to? Maybe they use tabs, 2 spaces, or 4 spaces for indentation. Maybe they use underscore prefixed private member variables such as private string _name;

Wouldn’t it be nice if when you pulled down the project from source control, it also brought all of the code formatting styles with it? That’s what EditorConfig is meant for.

Not long ago I added an .editorconfig file to RogueSharp. I recently mapped all of the ReSharper settings to the config as both Visual Studio and ReSharper come bundled with native support for EditorConfig.

Download

First you’ll need to download the file. I created a public Github Gist which is available here:

Add to Solution

  • Copy the downloaded file to the root of your solution where the .sln file lives.
  • Make sure that the name is .editorconfig
  • Create a new Solution Items folder in Visual Studio if it doesn’t already exist
  • Add .editorconfig to the Solution Items folder

EditorConfig Solution Item

Modify Settings

Although the .editorconfig will work as is, you’ll likely want to make some modifications to it, to match your coding style.

Common Settings

  • tabs vs spaces
  • indent size
  • trim trailing whitespace

EditorConfig Common Settings

You’ll likely want to change the indent size at a minimum. At TechSmith where I am currently employed we use 3 spaces, but 2 or 4 is more typical.

.NET Language Conventions

.NET Language Conventions

Microsoft Documentation For .NET Language Conventions

.NET Formatting Conventions

.NET Formatting Conventions

Microsoft Documentation For .NET Formatting Conventions

.NET Naming Conventions

.NET Formatting Conventions

Microsoft Documentation For .NET Naming Conventions

ReSharper Settings

ReSharper Settings

JetBrains Documentation For ReSharper EditorConfig Settings

Note: I organized the ReSharper settings according to how they appear in the menus. There should be a 1-1 relationship between each .editorconfig setting and it’s corresponding ReSharper menu entry.

ReSharper Menus

About Faron

Faron Bracy is a husband, father, and TechSmith employee. He maintains the open source library RogueSharp as a hobby project. Other hobbies include painting fantasy miniatures, playing board games, and playing retro video games with his family.

Advertisements

Collecting Code Coverage Metrics for .NET Core Libraries in Azure DevOps

Last time I talked about how I added status badges to the Readme file for RogueSharp. One of the badges was for showing the Code Coverage results from my continuous Azure DevOps build.

Before you can have a badge display those numbers, you need to have a build that can actually generate and collect them. While searching I found a lot of information about how to do it for full .NET Framework projects but information on .NET Core was scarce.

Since it wasn’t very straightforward for me I’ll share what I did here. Maybe there is a better way now?

MSTest Version

The first issue I struggled with was realizing that the version of MSTest that was I was using did not support the option to collect code coverage.

https://www.nuget.org/packages/MSTest.TestFramework/

You’ll need at least version 1.4.0.

Here is the commit where I upped my version, so up yours!

https://github.com/FaronBracy/RogueSharp/commit/10775b73baaec0785155ad2be287a5c255d0212b

Azure DevOps Menu

Azure DevOps Continuous Build

The Azure DevOps continuous build should then be using the .NET Core task.

.NET Core Build Task

I added three distinct steps.

  1. Restore NuGet Packages
  2. Build
  3. Run Tests

Build Steps

It’s this third step for running tests that will get us code coverage.

Test Step Setup

  • Under Arguments check the box next to Publish test results and code coverage
  • The Arguments should be --configuration $(BuildConfiguration) --collect "Code coverage"

The --collect "Code coverage" bit will only work if you are on MSTest version 1.4.0 or higher. It also appears to be case sensitive so make sure you type it exactly as shown.

Viewing Code Coverage Metrics

Once this is setup trigger the continuous build. If you look in the summary you should see the code coverage showing up now.

Build Summary

View Build Results In Azure DevOps

Code Coverage Badge

Code coverage

Shields.IO supports code coverage badges for multiple build services.

The formatted URL for Azure DevOps looks like this:

https://img.shields.io/azure-devops/coverage/{ORGANIZATION}/{PROJECT}/{DEFINITION_ID}.svg

See my previous post on status badges/shields for more information

About Faron

Faron Bracy is a husband, father, and TechSmith employee. He maintains the open source library RogueSharp as a hobby project. Other hobbies include painting fantasy miniatures, playing board games, and playing retro video games with his family.

Creating Status Badges and Shields for your Repository

I recently added status badges to the Readme file for the open source project I maintain, RogueSharp. I’ve seen other repositories do this but I wasn’t sure how. After a lot of looking around I compiled a list of services that worked for me. I couldn’t seem to find all this information in one place, so I’m hoping that this post showcasing what worked for me will help others.

Shields.IO

Shields.IO is a service for creating concise, consistent, and legible badges in SVG format. This should be your first stop when looking for a particular badge.

Resource Links

Tutorials: Blog Documentation: Github License: MIT

I use Shields.IO to create badges that link to static resources. To create these you just need a specially formatted URL.

https://img.shields.io/badge/LABEL-MESSAGE-COLOR.svg

To create a blue badge for a BSD 3-Clause license we could use

https://img.shields.io/badge/license-BSD%203--Clause-blue.svg

Because our license has a space and a dash in it we will need to treat those specially. The space will need to be replaced with it’s equivalent URL encoding “%20” and the dash will need to be escaped with a preceding dash.

Finally we need to decorate it with the appropriate markdown to get it to display as an image which links to the license.

[![License: BSD 3-Clause](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://github.com/TechSmith/hyde/blob/master/LICENSE.txt)

License: BSD 3-Clause

Test Status

Test status

It’s likely that no matter which service you are using for running continuous builds, Shields.IO also has you covered.

The formatted URL to get a test status badge for an Azure DevOps pipeline project will look like this:

https://img.shields.io/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg

When I did this for my own repo I wasn’t quite sure how to fill out “ORGANIZATION”, “PROJECT” and “DEFINITION_ID” placeholders.

What I found is that you can pull these directly from the query string when going to Azure DevOps and viewing your continuous build. In my case the URL is:

https://dreamersdesign.visualstudio.com/RogueSharp/_build?definitionId=1

In this example “dreamersdesign” is the organization, “RogueSharp” is the project and “1” is the definition ID.

Code Coverage

Code coverage

Shields.IO also supports code coverage badges for multiple build services.

The formatted URL for Azure DevOps looks like this:

https://img.shields.io/azure-devops/coverage/ORGANIZATION/PROJECT/DEFINITION_ID.svg

You’ll notice it looks like the test status badge, only replacing “tests” with “coverage”

BuildStats.info

Even though Shields.IO supported most of what I needed it didn’t support everything. It looks like development keeps happening on the service, so perhaps that will have changed by the time you read this. For my remaining badges I turned to BuildStats.info.

BuildStats.info creates SVG widgets to display build history charts and NuGet badges.

NuGet

Nuget

This one couldn’t be simpler. Just create a URL to

https://buildstats.info/nuget/PACKAGE_NAME

So for RogueSharp this was just

https://buildstats.info/nuget/roguesharp

It will automatically pull in the latest version number as well as the number of downloads.

Build History Chart

Build History

At the time that I setup my badges, BuildStats.info didn’t support the Build History Chart for Azure DevOps. It does look like this is now supported, but for my chart I used AppVeyor.

The formatted URL looks like this:

https://buildstats.info/BUILD_SYSTEM/chart/ACCOUNT/PROJECT

Substituting the values for my repository this yielded:

https://buildstats.info/appveyor/chart/FaronBracy/roguesharp-20n28

Build Service Specific

It’s worth noting that many build services provide their own status badge links. They probably won’t provide you with anything that you can’t get with Shields.IO, but it may be worth looking into.

Azure DevOps

Build status

When you are looking at your build pipeline you’ll see an elipsis for the build… click that.

Azure DevOps Menu

Then click on Status badge

Azure DevOps Status Badges

This will give you markdown that you can copy and paste directly into your readme.

AppVeyor

Build status

Click on AppVeyor configuration for a repo (the gear icon). Then select “Badges” from the tabs on the left.

AppVeyor Status Badges

This will give you markdown that you can copy and paste directly into your readme.

About Faron

Faron Bracy is a husband, father, and TechSmith employee. He maintains the open source library RogueSharp as a hobby project. Other hobbies include painting fantasy miniatures, playing board games, and playing retro video games with his family.

Guest Blog Post – RogueSharp Tutorial Series Ported to Unity

Introduction

I’m excited that one of the visitors to the blog took it upon himself to port the code from the RogueSharp tutorial series to Unity. I asked if he would be interested in writing a guest blog post and he agreed! A huge thank you to Brian for all the hard work with the port and for the  information posted below.

– Faron

About the Author

First off, thank you Faron Bracy for developing the RogueSharp library and all the time you’ve spent developing the library and writing tutorials on it!

I have been developing with Unity for some time now and have been looking for a C# Rogue or 2D RPG library. There a couple Rogue/RPG libraries on the Unity asset store but the libraries seem to be too integrated or reliant on Unity. I was looking for a pure C# implementation and something I could work into Unity but change graphics engine later if I wanted. Thus RogueSharp.

About the Port to Unity

I developed a port of Faron’s RogueSharp v3 tutorial series with Unity except I ended up using RogueSharp V4! I originally went through the entire tutorial series using RLNet Console, which gave me a comfortable feel for the library and how I wanted to approach integrating with Unity. With the Unity integration and some of my own personal preferences, I’ve made a numbers changes to the original tutorial for the Unity port. Some of notable changes:

  1. Uses Unity (version 2018.2.1f1) instead of RLNet Console, so no more RLColor or RLConsole calls;
  2. The code has been reorganized mostly in a MVC pattern. There are no dependencies in the Model and Controller logic against Unity. Only View logic has any reliance on Unity. This could make ports on other consoles/backends easier (maybe?);
  3. Uses diagonal movement in RogueSharp v4 including monster pathfinding;
  4. Most static references removed, passes data by reference. Personal preference, I’m not big on lots of static functions;
  5. Uses RogueSharp v4, not v3 as in the original tutorial series. Only caused a few minor changes to the tutorial code;
  6. Object pooling used from Catlike coding,https://catlikecoding.com/unity/tutorials/ for Unity GameObjects that represent cells on the console. This was necessary as the height/width sizes of the console cause too many GameObects to be created (thousands). This caused significant slowdowns in the FPS, so I needed an optimization early in the process. As a result, the View logic will only display tiles/cells that are visible in the Camera and dynamically change the viewable tiles/cells as the player moves. This allows for arbitrarily large maps;
  7. There are few odds and ends added to the code that are placeholders for a future implementation that allows switching between ASCII characters and graphical tiles.

The port can be found at: https://github.com/Olivexe/RogueSharpTutorialUnityPort. Along with the project source code, I’ve put in a Unity Package file, named RogueSharpTutorialPort.unitypackage. You should be able to import the package file into a new Unity Project, click the sample scene, and hit play. It should play exactly as the tutorial. There is now a second branch in my port that I am slowly porting the remainder of the complete RogueSharp tutorial over to Unity.

Thank you,

Brian “Olivexe” Pleffner

RogueSharp v4.1.0 Released

Version 4.1.0 of RogueSharp was released

Announcements:

New features:

  • Optimized cave map generator (Thanks to James Neal)
  • DijkstraShortestPath.FindPath method which terminates upon finding destination for improved speed (Thanks to flend for this update)
  • Added AppVeyor continuous builds (Thanks to Glenn Hoeppner)
  • New constructors for GoalMap and PathFinder to allow for paths which include diagonal movement
  • Map.GetCellsInCircle and Map.GetBorderCellsInCircle methods use midpoint circle algorithm to get cells within a radius
  • Multiple new “Try” methods which work like their normal counterparts except they return null instead of throwing exceptions
  • Path.TryStepForward and Path.TryStepBackward which will return null instead of throwing a NoMoreStepsException
  • PathFinder.TryFindShortestPath will return null instead of throwing a PathNotFoundException
  • GoalMap.TryFindPath will return null instead of throwing a PathNotFoundException

Breaking changes:

  • Bug fix (4.1.0) – selecting border Cells along edge of map no longer selects center Cell
  • Point and Rectangle classes are now structs (Thanks to James Neal)
  • Updated all the appropriate references to Cell with ICell (Thanks to Courtney Strachan)
  • Map.ComputeFov and Map.AppendFov both return a ReadonlyCollection<ICell> for the cells in the field-of-view instead of returning void.
  • The Path returned from PathFinder.ShortestPath now includes the source cell in the returned Path. This behavior is consistent with how the GoalMap pathfinder works
  • Map.GetCellsInArea was renamed to Map.GetCellsInSquare
  • Map.GetBorderCellsInArea was renamed to Map.GetBorderCellsInSquare
  • Map.GetCellsInRadius was renamed to Map.GetCellsInDiamond
  • Map.GetBorderCellsInRadius was renamed to Map.GetBorderCellsInDiamond

RogueSharp 4.0 Pre-Release

Version 4.0.0-pre of RogueSharp was just released

In order to obtain pre-release versions via Nuget make sure to choose “Include prerelease” packages from the Nuget Package Manager.

PrereleaseNuget

Pre-release Nuget Package

RogueSharp 4.0.0-pre NuGet Package
RogueSharp 4.0.0-pre Source Code

Thank you to numerous contributors for this release including James Neal, flend, Glenn Hoeppner, and Courtney Strachan. Any feedback regarding this pre-release version is appreciated!

New features:

  • Optimized cave map generator (Thanks to James Neal)
  • DijkstraShortestPath.FindPath method which terminates upon finding destination for improved speed (Thanks to flend for this update)
  • Added AppVeyor continuous builds (Thanks to Glenn Hoeppner)
  • New constructors for GoalMap and PathFinder to allow for paths which include diagonal movement
  • Map.GetCellsInCircle and Map.GetBorderCellsInCircle methods use midpoint circle algorithm to get cells within a radius
  • Multiple new “Try” methods which work like their normal counterparts except they return null instead of throwing exceptions
  • Path.TryStepForward and Path.TryStepBackward which will return null instead of throwing a NoMoreStepsException
  • PathFinder.TryFindShortestPath will return null instead of throwing a PathNotFoundException
  • GoalMap.TryFindPath will return null instead of throwing a PathNotFoundException

Breaking changes:

  • Point and Rectangle classes are now structs (Thanks to James Neal)
  • Updated all the appropriate references to Cell with ICell (Thanks to Courtney Strachan)
  • Map.ComputeFov and Map.AppendFov both return a ReadonlyCollection<ICell> for the cells in the field-of-view instead of returning void.
  • The Path returned from PathFinder.ShortestPath now includes the source cell in the returned Path. This behavior is consistent with how the GoalMap pathfinder works
  • Map.GetCellsInArea was renamed to Map.GetCellsInSquare
  • Map.GetBorderCellsInArea was renamed to Map.GetBorderCellsInSquare
  • Map.GetCellsInRadius was renamed to Map.GetCellsInDiamond
  • Map.GetBorderCellsInRadius was renamed to Map.GetBorderCellsInDiamond

Pathfinder Diagonal Path Example:

By using the new constructor on the PathFinder class and providing a second argument which is the cost of diagonal movement you can now return paths that consider diagonals.

public Path FindPath( ICell source, ICell destination, IMap map )
{
  // 1.41 is the cost of diagonal movment compared to horizontal or vertical of 1
  var pathFinder = new PathFinder( map, 1.41 );

  // TryFindShortestPath is a new method that will return null
  // instead of throwing PathNotFoundException if there isn't a path.
  return pathFinder.TryFindShortestPath( source, destination );
}
DiagonalPathfinder

Diagonal Pathfinder

GoalMap Diagonal Path Example:

The GoalMap class also received a new constructor which will allow it to use diagonals.

/// Constructs a new instance of a GoalMap for the specified Map 
/// that will consider diagonal movements to be valid if allowDiagonalMovement is set to true.
/// "map" The Map that this GoalMap will be created for
/// "allowDiagonalMovement" True if diagonal movements are allowed. False otherwise
public GoalMap( IMap map, bool allowDiagonalMovement )
DiagonalGoalMap

Diagonal Goal Map

Map.GetCellsInCircle Example:

RogueSharp has always had a lot of different Cell selection methods as seen in this old post.

Cell Selection

Previous Cell Selection

What it has been missing is a good method for getting cells in a Circle. We now have two new methods which will help with this.

/// Get an IEnumerable of Cells in a circle around the center Cell up 
/// to the specified radius using Bresenham's midpoint circle algorithm
/// "xCenter" X location of the center Cell with 0 as the farthest left
/// "yCenter" Y location of the center Cell with 0 as the top
/// "radius" The number of Cells to get in a radius from the center Cell
public IEnumerable<ICell> GetCellsInCircle( int xCenter, int yCenter, int radius )


/// Get an IEnumerable of outermost border Cells in a circle around the center 
/// Cell up to the specified radius using Bresenham's midpoint circle algorithm
/// "xCenter"X location of the center Cell with 0 as the farthest left
/// "yCenter"Y location of the center Cell with 0 as the top
/// "radius"The number of Cells to get in a radius from the center Cell
public IEnumerable<ICell> GetBorderCellsInCircle( int xCenter, int yCenter, int radius )
CircleSelection

Circle Selection

Using the new circle selection code it is possible to create poor implementation of circular field-of-view even though RogueSharp doesn’t have native support for it. Here is an example of some code that will do it.

private static IEnumerable<ICell> GetFieldOfView( int x, int y, IMap map )
{
  List<ICell> circleFov = new List<ICell>();
  var fieldOfView = new FieldOfView( map );
  var cellsInFov = fieldOfView.ComputeFov( x, y, (int) ( _selectionSize * 1.5 ), true );
  var circle = map.GetCellsInCircle( x, y, _selectionSize ).ToList();
  foreach ( ICell cell in cellsInFov )
  {
    if ( circle.Contains( cell ) )
    {
      circleFov.Add( cell );
    }
  }
  return circleFov;
}

It will create field-of-view that looks like this:

CircularFieldOfView

Circular Field-of-View

RogueSharp V3 Tutorial – Stairs

Next Tutorial Post – Not Available Yet
Previous Tutorial Post – Doors

Goal

The purpose of this tutorial is start placing stairs in our dungeon. We want to be able to proceed down stairs to get into deeper and more difficult levels.

  • Stairs can either go up or down
  • The symbol for stairs going down will be a greater than sign “>”
  • The symbol for stairs going up will be a less than sign “<“
  • Each dungeon level will have one up and one down staircase
  • Stairs going down can be descended by the player by pressing the ‘>’ or ‘.’ key
  • Once a player has gone down to the next level they cannot go back
  • Stairs going up are only used to indicate where the player came from
  • When a player goes down stairs a brand new level is generated

Creating the Stairs Class

To begin we need a new class to represent the stairs as we outlined in our goals above. The stairs class should inherit from IDrawable because we will want to draw it on the map console. Create a new file named Stairs.cs in the Core folder and place the following code in it.

public class Stairs : IDrawable
{
  public RLColor Color
  {
    get; set;
  }
  public char Symbol
  {
    get; set;
  }
  public int X
  {
    get; set;
  }
  public int Y
  {
    get; set;
  }
  public bool IsUp
  {
    get; set;
  }

  public void Draw( RLConsole console, IMap map )
  {
    if ( !map.GetCell( X, Y ).IsExplored )
    {
      return;
    }

    Symbol = IsUp ? '<' : '>';

    if ( map.IsInFov( X, Y ) )
    {
      Color = Colors.Player;
    }
    else
    {
      Color = Colors.Floor;
    }

    console.Set( X, Y, Color, null, Symbol );
  }
}

Updating DungeonMap Class

Now that our Stairs class is created, we need to update DungeonMap.cs with a few changes to be able to use the Stairs. At the top of DungeonMap.cs add the following two properties for our StairsUp and StairsDown next to the existing Rooms and Doors properties.

public Stairs StairsUp { get; set; }
public Stairs StairsDown { get; set; }

Next update the DungeonMap constructor to call Clear() on the SchedulingSystem when a new DungeonMap is constructed. We do this because when we make a new level by going down stairs we want to make sure that all of the monsters from the previous level are removed from the schedule and do not continue to try to act.

public DungeonMap()
{
  Game.SchedulingSystem.Clear();

  // Previous code omitted...
}

Now we need a new method which will check to see if the player is standing on the stairs going down. Create a method called CanMoveDownToNextLevel() with the following code.

public bool CanMoveDownToNextLevel()
{
  Player player = Game.Player;
  return StairsDown.X == player.X && StairsDown.Y == player.Y;
}

In the Doors tutorial last time we forgot to update the Draw() method and had to have a blog reader point out the mistake. We’ll try not to make the same mistake this time. Update the Draw() method and add the following lines of code to draw the stairs.

// Add the following code after we finish drawing doors.
StairsUp.Draw( mapConsole, this );
StairsDown.Draw( mapConsole, this );

Updating the MapGenerator Class

The next class we need to update is MapGenerator. We need to make sure that when we generate new maps, stairs are created along with the rest of the dungeon features. Open MapGenerator.cs and create a new private method named CreateStairs().

private void CreateStairs()
{
  _map.StairsUp = new Stairs
  {
    X = _map.Rooms.First().Center.X + 1,
    Y = _map.Rooms.First().Center.Y,
    IsUp = true
  };
  _map.StairsDown = new Stairs
  {
    X = _map.Rooms.Last().Center.X,
    Y = _map.Rooms.Last().Center.Y,
    IsUp = false
  };
}

We are not doing anything too fancy to generate the stairs. We are creating the stairs up in the center of the first room that was generated. This is the same room that the player starts in and the player is also in the center of the room, so we’ll offset the X coordinate by 1 to put the stairs next to the player. The last room we generated gets stairs going down and again we place them in the center of the room.

Make sure to call the CreateStairs() method from the existing CreateMap() method right before calling PlacePlayer();

public DungeonMap CreateMap()
{
  // Previous code omitted...

  // Call right before calling PlacePlayer();
  CreateStairs();

  // Previous code
  PlacePlayer();
}

Also we want to change the signature of the MapGenerator constructor and add an additional integer parameter called mapLevel.

public MapGenerator( int width, int height, int maxRooms, int roomMaxSize, int roomMinSize, int mapLevel )
{
  // Keep all existing code in the constructor
}

If you have any sort of static analysis on like FxCop it will complain about having an unused parameter in the method. We really shouldn’t add it until we are prepared to use it, but rest assured we will use it very soon.

Updating the Game Class

Open up Game.cs which will be the final class that we need to update. Start by adding a new private static int member variable in with the rest of the member variables at the top of the class.

private static int _mapLevel = 1;

Next change the line was setting the console title in the Main() method. Also add the _mapLevel parameter to line where we instantiate a new MapGenerator.

// Old code was...
// string consoleTitle = $"RougeSharp V3 Tutorial - Level 1 - Seed {seed}";

// New code is
string consoleTitle = $"RougeSharp V3 Tutorial - Level {_mapLevel} - Seed {seed}";


// Old code was...
// MapGenerator mapGenerator = new MapGenerator( _mapWidth, _mapHeight, 20, 13, 7 );

// New code is
MapGenerator mapGenerator = new MapGenerator( _mapWidth, _mapHeight, 20, 13, 7, _mapLevel );

Finally in the OnRootConsoleUpdate(…) method where we are checking to see which key was pressed ad the following else if onto the end of the if block.

else if ( keyPress.Key == RLKey.Period )
{
  if ( DungeonMap.CanMoveDownToNextLevel() )
  {
    MapGenerator mapGenerator = new MapGenerator( _mapWidth, _mapHeight, 20, 13, 7, ++_mapLevel );
    DungeonMap = mapGenerator.CreateMap();
    MessageLog = new MessageLog();
    CommandSystem = new CommandSystem();
    _rootConsole.Title = $"RougeSharp RLNet Tutorial - Level {_mapLevel}";
    didPlayerAct = true;
  }
}

This last bit of code just checks for the “>” or “.” key being pressed. It then calls into DungeonMap.CanMoveDownToNextLevel() which will return true if the Player is standing on a stairway leading down. We then generate a new map and increment the _mapLevel.

If you run the game now you should be able to explore until you find stairs and descend deeper into the dungeon.

stairs

Going down stairs

Closing Thoughts

Although we got our stairs working we are still missing some important things. Currently levels don’t get any more difficult the deeper we go.

As always the code for the tutorial series so far can be found on Bitbucket:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/16Stairs

Bored waiting for the next tutorial post? The completed project is already available on Bitbucket.