Skip to content

Map View System

Chris3606 edited this page Jan 11, 2019 · 72 revisions

GoRogue offers a comprehensive, simple to use system that allows game maps to be represented in many different ways to GoRogue algorithms.

Table of Contents

Code Examples

Code examples in this section do not show needed using statements. The code provided assumes that the following "using" statements are at the top of the code file:

using GoRogue;
using GoRogue.MapGeneration;
using GoRogue.MapViews;
using Troschuetz.Random;
using Troschuetz.Random.Generators;

Introduction

GoRogue offers many different algorithms that either help to generate or operate on maps -- path-finding, map generation, FOV, sense-mapping, and other algorithms all need to extract some sort of data from a map to function. However, the data the algorithms need from the map (eg. how a given algorithm "views" a map) may differ in all of these cases.

For example, in the case of path-finding, these algorithms view a map as a boolean value per location, saying whether or not that location is passable -- this is the only map data it needs to function. By contrast, however, the sense-mapping algorithm views the map as a double value per location, which gives its resistance as a percentage [0.0, 1.0]. While the data in these two examples may (or may not, depending on use case) be inter-related, each algorithm views the map as a different set of values.

Furthermore, since GoRogue is designed to be a highly portable library that doesn't require the use of any particular code architecture or arrangement of data, its algorithms must view the map in the appropriate way without any real knowledge of how the data is stored or acquired. For example in the case of path-finding algorithms, its boolean data could be stored in a simple 2D array of boolean values. It could also be a boolean value out of some MapObject class, or even not directly stored but rather the result of an operation such as raycasting. One of the goals of GoRogue is to allow all these options (and any other that a user might create) to be valid, without necessitating code modifications, and without being difficult to use. This is the purpose of the members of the GoRogue.MapViews namespace.

Representing a View of a Map

The IMapView interface is the basis of a very simple solution to passing maps to GoRogue algorithms. an implementation of IMapView<T> uses indexers to take in a Coord (or an x and y value), and return the value of type T corresponding to that location. In addition, the interface specifies properties to read the width and height of the map. This allows, for example, pathfinding algorithms to take a map in as a parameter of type IMapView<bool>, and an FOV or sense-mapping algorithm to take a map as a parameter of type IMapView<double>. In this way, GoRogue requires no knowledge of how the data is actually stored or retrieved. An implementing class simply creates the required indexers to perform whatever operations are required to extract data of the proper type.

Representing a Settable View of a Map

IMapView works very well with algorithms such as path-finding, where the algorithm does not modify the map. However, if an algorithm needs to modify the map, we must use ISettableMapView<T>. This interface is exactly like IMapView<T> (in fact, it itself implements IMapView<T>), however its indexers must also define set functions. These functions take a value of type T, and apply whatever changes are necessary to map structure, etc. to implement that change.

Representing a Sub-View of a Map

The Map View system also provides a convenient way to represent a subsection of a map. For instance, suppose you had a Goal Map, and you wanted to compute the goal map for only a portion of the actual map (perhaps for performance reasons). You could do this by using the Viewport<T> class. This class takes an IMapView<T> instance, and a Rectangle representing the area of the given IMapView that is initially represented by the Viewport. Viewport<T> then implements IMapView<T>, such that the indexers perform coordinate translation between the sub-view and the overall IMapView it was given. This allows for a "sub-view" of an existing IMapView to be presented to an algorithm as easily as the entire map could be.

Viewport<T> also implements its indexers as virtual, so that custom functionality can be introduced if needed.

Finally, Viewport has a ViewArea property that gets you get and set the area of the map being referenced.

In similar fashion to ISettableMapView, for cases when you need the sub-view of the map to be settable, a SettableMapView class is provided, which implements ISettableMapView.

Code Example

Since Viewport<T> concretely implements IMapView<T>, we can finally produce a simple, relevant code example! The following illustrates the concept of a Viewport:

// ArrayMap is a simple, GoRogue-provided implementation of IMapView.  The specifics are covered later in documentation.
ArrayMap<int> myMap = new ArrayMap<int>(10, 10);
// We simply use the ToIndex function to assign each element a unique number
foreach (var position in myMap.Positions())
    myMap[position] = position.ToIndex(myMap.Width);

System.Console.WriteLine("myMap:");
System.Console.WriteLine(myMap.ToString(4));

// Viewport implements IMapView, and represents a sub-view of an existing map view
Viewport<int> myViewport = new Viewport<int>(myMap, new Rectangle(2, 2, 4, 4));
System.Console.WriteLine("\nmyViewPort:");
System.Console.WriteLine(myViewport.ToString(4));

// We can use a settable viewport (which implements ISettableMapView) to represent a sub-view of,
// and set values to, a sub-view of an existing map view
ISettableMapView<int> settableViewport = new SettableViewport<int>(myMap, new Rectangle(2, 2, 4, 4));
settableViewport[2, 1] = 501; // (2, 1) in viewport is (4, 3) in original MapView
System.Console.WriteLine("\nsettableViewport after change:");
System.Console.WriteLine(settableViewport.ExtendToString(4));

System.Console.WriteLine("\nmyViewport after change:");
System.Console.WriteLine(myMap.ToString(4));

// We can also set the view area via the ViewArea property, since its getter returns a ref Rectangle
myViewport.ViewArea = myViewport.ViewArea.Move(1, 2);
System.Console.WriteLine("\nmyViewport after move:");
System.Console.WriteLine(myViewport.ToString(4));

Provided Interface Implementations

While some cases may involve the need for Viewports, or complex map structure where some sort of translation has to occur to retrieve values of the proper type, in many simple (or prototyping) cases, such data might be stored in an actual 2D array, or require only very simple translation (like returning a field of a class). For these cases, GoRogue provides a number of concrete implementations of IMapView and ISettableMapView.

ArrayMap<T>

ArrayMap<T> is one of the simplest concrete map views provided by GoRogue. It implements ISettableMapView<T> by simply wrapping a 2D array of type T. When it is created, you give it a width and a height, and it internally creates an array of type T[,], and exposes that array to you via the indexer functions and Width/Height properties required to implement ISettableMapView<T>.

While this data structure is not well suited to allowing GoRogue to interact with complex map structures, it can be useful for simple use cases, when you really do want the data that GoRogue interacts with to simply be 2D array of type T. It is also useful for algorithms like Map Generation, where you may want to store GoRogue's data in a temporary data structure, and then transform it into a more complex map structure after generation algorithms have finished.

Code Example

In the following example, we will demonstrate basic functionality of ArrayMap:

ArrayMap<bool> myMap = new ArrayMap<bool>(10, 10);

// Sets all edges of the map to false (non-walkable), and all other locations to true (walkable).
// See Map Generation documentation for details.  Notably, this function takes an `ISettableMapView`
// as its parameter.
QuickGenerators.GenerateRectangleMap(myMap);

// Prints '.' for true values, '#' for false values in map view.  See ExtendToString/ToString section
// for details
System.Console.WriteLine("myMap:");
System.Console.WriteLine(myMap.ToString(v => v ? "." : "#"));

// Instances of ArrayMap implement ICloneable to do a deep copy
ArrayMap<bool> newMap = (ArrayMap<bool>)myMap.Clone();
System.Console.WriteLine("\nnewMap is identical to myMap:");
System.Console.WriteLine(newMap.ToString(v => v ? "." : "#"));

// After set, newMap has changed, myMap has not
newMap[1, 2] = false;
System.Console.WriteLine("\nnewMap after set:");
System.Console.WriteLine(newMap.ToString(v => v ? "." : "#"));
System.Console.WriteLine("\nmyMap after newMap set has not changed:");
System.Console.WriteLine(myMap.ToString(v => v ? "." : "#"));

LambdaMapView<T> and LambdaSettableMapView<T>

In many cases, extracting the proper data type per location for a MapView may be a simple matter of a call to an already existing function, or extracting a property from a field. Instead of requiring that you create a completely custom IMapView or ISettableMapView implementation in these cases, GoRogue provides LambdaMapView and LambdaSettableMapView classes to simplify this. In addition to Width and Height, these classes take as parameters at construction functions that are used to perform get and set operations, respectively. Width and height may either be specified as constants, or as functions that retrieve them.

Code Example

In this example we will create a LambdaSettableMapView that extracts data from a simple Tile class.

struct Tile
{
    public bool IsWalkable;
    public bool IsTransparent;

    public Tile(bool isWalkable = true, bool isTransparent = true)
    {
        IsWalkable = isWalkable;
        IsTransparent = isTransparent;
    }
}
class Program
{
    static void Main(string[] args)
    {
        // For the sake of example, our Map is a 2D array of Tile structs.
        Tile[,] realMap = new Tile[10, 10];

        // We pass in as parameters simple functions that access the above array.
        // The functions we give are used to implement get and set functionality for IMapView
        var mapView = new LambdaSettableMapView<bool>(10, 10, pos => realMap[pos.X, pos.Y].IsWalkable, (pos, val) => realMap[pos.X, pos.Y].IsWalkable = val);

        // This Map Generation algorithm takes a ISettableMapView and sets all the edges to true, and all the rest of the values to false.
        RectangleMapGenerator.Generate(mapView);

        // Our tiles were updated via the map view
        for (int y = 0; y < 10; y++)
        {
            for (int x = 0; x < 10; x++)
                System.Console.Write(realMap[x, y].IsWalkable ? ". " : "# ");
            System.Console.WriteLine();
        }

    }
}

LambdaMapView<T> is exactly like the above example, you just don't supply a setter function.

TranslationMap<T1, T2> and SettableTranslationMap<T1, T2>

In some cases, when we already have something that implements IMapView<T>, particularly where T is a complex type, it may be convenient to simply define "translation" functions that translate between IMapView<T>, and IMapView<bool> or whatever type you need to feed to an algorithm. This is the purpose of TranslationMap<T1, T2> and SettableTranslationMap<T1, T2> -- they allow you to define methods that perform translation from IMapView<T1> to IMapView<T2>, and then simply pass in as a constructor parameter an IMapView<T1> or ISettableMapView<T1> instance. The translation functions are used automatically to implement the required IMapView<T2> or ISettableMap<T2>, as appropriate. We can also override an overload of TranslateGet/TranslateSet that take the position of the item being translated as well, if the positional context is necessary.

Code Example

In this example, we will implement a "map" as an ArrayMap<bool> instance. We will then use a custom TranslationMap<bool, double> implementation to translate to a map of doubles that Semse Maps, for instance, could understand. Although we only use TranslationMap here, SettableTranslationMap would function in the exact same way, except that it would also require the implementation of a TranslateSet function that goes from T2 to T1.

    class DoubleTranslator : TranslationMap<bool, double>
    {
        public DoubleTranslator(IMapView<bool> baseMap)
            : base(baseMap) { }

        // Return 0.0 for true values, 1.0 for false values.
        // Here we do not need the position of the value being translated to perform the translation.
        // If we did, we could override double TranslationGet(Coord position, bool value) instead.
        protected override double TranslateGet(bool value) => value ? 0.0 : 1.0;
    }

    class Program
    {
        static void Main(string[] args)
        {
            // For the sake of example, our Map is a 2D array of bools.
            ArrayMap<bool> realMap = new ArrayMap<bool>(10, 10);
            // Sets all edges to false, and remaining locations to true.  See Map Generation documentation for details
            RectangleMapGenerator.Generate(realMap);

            // Prints '.' for true values, '#' for false values in map view.  See ExtendToString/ToString section
            // for details
            System.Console.WriteLine("realMap: ");
            System.Console.WriteLine(realMap.ToString(v => v ? "." : "#"));


            var doubleTranslator = new DoubleTranslator(realMap);
            System.Console.WriteLine("\nDouble Translation Map: ");
            System.Console.WriteLine(doubleTranslator);
        }
    }

In a more realistic example, the ArrayMap would likely be IMapView<Tile> or some complex type other than bool, however for the sake of brevity in this example, it is simply bool.

LambdaTranslationMap<T1, T2> and LambdaSettableTranslationMap<T1, T2>

Translation maps as described above can be very useful. However, in many cases, like the one presented in the code example for Translation Maps above, the translation is extremely simple to perform. Thus, GoRogue provides the LambdaTranslationMap<T1, T2> and LambdaSettableTranslationMap<T1, T2> types. These types take as constructor parameters functions that define the translations. In this way, when the translation is simple, a user is not required to implement an entire subclass for only a few lines of actual functionality.

Code Example

This code example is precisely the same as the one given for Translation Maps, however it uses lambdas to define the translation functions, instead of subclasses. `LambdaSettableTranslationMap would work in the same way, except you would also supply a setter function.

class Program
    {
        static void Main(string[] args)
        {
            // For the sake of example, our Map is a 2D array of bools.
            ArrayMap<bool> realMap = new ArrayMap<bool>(10, 10);
            // Sets all edges to false, and remaining locations to true.  See Map Generation documentation for details
            RectangleMapGenerator.Generate(realMap);

            // Prints '.' for true values, '#' for false values in map view.  See ExtendToString/ToString section
            // for details
            System.Console.WriteLine("realMap: ");
            System.Console.WriteLine(realMap.ToString(v => v ? "." : "#"));


            var doubleTranslator = new LambdaTranslationMap<bool, double>(realMap, b => b ? 0.0 : 1.0);
            System.Console.WriteLine("\nDouble Translation Map: ");
            System.Console.WriteLine(doubleTranslator);
        }
	

Useful Extension Methods

Given only the properties of a MapView, it is possible to implement some useful helper functions. However, since IMapView is an interface, it cannot have implemented methods. As a result, any such functionality is offered via extension methods.

Bounds

The Bounds() function provides a convenient way to get a Rectangle representing the bounding box of a map.

ArrayMap<int> myMap = new ArrayMap<int>(10, 12);
System.Console.WriteLine(myMap.Bounds());

// Remember that a Viewport is just an IMapView, so if you call Bounds, you get the bounds relative
// to the sub-view. If you want the bounds of the viewport relative to the complete map, you want the ViewArea.
Viewport<int> viewport = new Viewport<int>(myMap, new Rectangle(4, 4, 2, 3));
System.Console.WriteLine("viewport.Bounds: " + viewport.Bounds());
System.Console.WriteLine("viewport.ViewArea: " + viewport.ViewArea);

Positions

The Positions() function can be used as a substitute for the typical nested for-loop to iterate over all locations in a MapView in many cases. It returns an IEnumerable<Coord> of all Coords within the MapView bounds.

Depending on use case, it may have a small performance penalty compared to a traditional manual nested-for loop, so be aware of this if performance becomes an issue in tight loops.

ArrayMap<int> myMap = new ArrayMap<int>(3, 3);
// Extension method that prints IEnumerable like a python list
System.Console.WriteLine("myMap.Positions(): " + myMap.Positions().ExtendToString());

// Remember that Viewport implements IMapView, so calling Positions() on a viewport gets
// you the sub-view relative positions.  If you're looking for base map-relative positions,
// use ViewArea.Positions().  If you're looking for all positions in the base map view, use
// MapView.Positions().
Viewport<int> myViewport = new Viewport<int>(myMap, new Rectangle(1, 1, 1, 1));
System.Console.WriteLine("myViewport.Positions: " + myViewport.Positions().ExtendToString());
System.Console.WriteLine("myViewport.ViewArea.Positions: " + myViewport.ViewArea.Positions().ExtendToString());
System.Console.WriteLine("myViewport.MapView.Positions: " + myViewport.MapView.Positions().ExtendToString());

RandomPosition

The various RandomPosition() function overloads provide convenient ways to get random positions from the bounds of an IMapView.

Basic Selection

The simplest way is to simply call the RandomPosition() function with no arguments, which uses the default RNG (see Random Number Generation documentation for details). An RNG can also be specified.

ArrayMap<int> myMap = new ArrayMap<int>(100, 100);
System.Console.WriteLine("Default RNG: " + myMap.RandomPosition());

IGenerator gen = new XorShift128Generator();
System.Console.WriteLine("Custom RNG: " + myMap.RandomPosition(gen));

Using a Selector Function

We can also provide a function that acts as a "selector". A random position is generated, and then the position selected (and value at that location) are passed to the selector. If the selector returns true, the position is returned. If the selector returns false, another position is generated, and we retry the selector. This can be useful for selecting random positions while ensuring that the position selected satisfies a certain requirement:

ArrayMap<int> myMap = new ArrayMap<int>(100, 100);
// Select a random coordinate in an even column
Coord pos = myMap.RandomPosition((coord, value) => coord.X % 2 == 0);
System.Console.WriteLine("Even random Coord: " + pos);

// We can also specify a custom RNG
IGenerator gen = new XorShift128Generator();
pos = myMap.RandomPosition((coord, value) => coord.X % 2 == 0, gen);
System.Console.WriteLine("Even random Coord with custom RNG: " + pos);

Specifying an Acceptable Value

Similarly, instead of specifying a selector function, we can simply specify a value of type T that is considered valid. This is a more simplistic approach, but may be useful for example, in selecting a random tile that is walkable from an IMapView<bool> representing a walkability map:

ArrayMap<bool> myMap = new ArrayMap<bool>(100, 100);
// See MapGeneration documentation for details, but sets myMap such that walls
// are false (unwalkable), and floors are true (walkable)
RectangleMapGenerator.Generate(myMap);

// Select a random coordinate whose value in the ArrayMap is set to true -- eg. select a "walkable" tile
Coord pos = myMap.RandomPosition(true);
System.Console.WriteLine("Walkable Pos: " + pos);

// Similarly to other overloads, we can also use a custom RNG
IGenerator gen = new XorShift128Generator();
pos = myMap.RandomPosition(true, gen);
System.Console.WriteLine("Walkable Pos with custom RNG: " + pos);

Specifying Multiple Acceptable Values

Finally, we can pass in a list of acceptable values for the selected position as an IEnumerable<T>, a HashSet<T> for added efficiency, or a params[] list:

ArrayMap<int> myMap = new ArrayMap<int>(5, 5);
// Set even columns to value of either 2 or 3, for sake of example
for (int x = 0; x < myMap.Width; x += 2)
    for (int y = 0; y < myMap.Height; y++)
        if (y % 2 == 0)
            myMap[x, y] = 2;
        else
            myMap[x, y] = 3;

// Select a random coordinate wherein the value at the selected location is 2 or 3
int[] acceptableValues = new int[] { 2, 3 };
Coord pos = myMap.RandomPosition(acceptableValues);
System.Console.WriteLine("Random Coord: " + pos);

// Select similar values, just using a HashSet, which may be useful if there is a large
// number of values to select from
HashSet<int> acceptableHash = new HashSet<int> { 2, 3 };
pos = myMap.RandomPosition(acceptableHash);
System.Console.WriteLine("Random Coord Hash: " + pos);

// Finally, we can simply pass the parameters in as an arbitrary-length params list
pos = myMap.RandomPosition(null, 2, 3);
System.Console.WriteLine("Random Coord params: " + pos);

// Using any of the above methods, we can also specify a custom RNG:
IGenerator gen = new XorShift128Generator();
pos = myMap.RandomPosition(acceptableValues, gen);
System.Console.WriteLine("Random coord with custom RNG: " + pos);

RandomItem

Instead of retrieving a randomly generated position as with RandomPosition, in some cases it may be more convenient to retrieve the actual value at that position. For these cases, the RandomItem function is provided. You may optionally pass a selector function that determines whether a position is valid as a return value, and a custom RNG:

class Tile
{
    public bool IsWalkable;
    public bool IsTransparent;

    public Tile(bool isWalkable, bool isTransparent)
    {
        IsWalkable = isWalkable;
        IsTransparent = isTransparent;
    }
}

class Program
{
    static void Main(string[] args)
    {
        ArrayMap<Tile> myMap = new ArrayMap<Tile>(5, 5);
        // Set even columns to walkable, odd to not, every third tile transparent, the rest opaque, 
        // for sake of example
        for (int x = 0; x < myMap.Width; x += 2)
            for (int y = 0; y < myMap.Height; y++)
            {
                bool walkable = false;

                if (y % 2 == 0)
                    walkable = true;

                bool transparent = false;
                if (y % 3 == 0)
                    transparent = true;

                myMap[x, y] = new Tile(walkable, transparent);
            }

        // Select a random tile from the map
        Tile tile = myMap.RandomItem();
        System.Console.WriteLine("Random tile walkability: ");
        System.Console.WriteLine(tile.IsWalkable);

        // Select a random, transparent tile
        Tile transparentTile = myMap.RandomItem((pos, val) => val.IsTransparent);
        System.Console.WriteLine("Random tile walkability: ");
        System.Console.WriteLine(transparentTile.IsWalkable);
    }
}

ApplyOverlay

Since ISettableMapView<T> allows setting of the values at positions via indexers, GoRogue provides the ApplyOverlay extension method that allows you to copy values from one IMapView<T> onto another of the same size:

ArrayMap<bool> map = new ArrayMap<bool>(10, 10);
RectangleMapGenerator.Generate(map);

// overlay has the center value set to false, unlike map
ArrayMap<bool> overlay = new ArrayMap<bool>(10, 10);
RectangleMapGenerator.Generate(overlay);
overlay[5, 5] = false;

// Values in map get set to values in overlay
map.ApplyOverlay(overlay);
System.Console.WriteLine("Original map: ");
// Prints "." for true values, "#" for false values.  See ExtendToString
// documentation for details
System.Console.WriteLine(map.ToString(v => v ? "." : "#"));

ExtendToString/ToString

Although it is possible to take the data from properties of an IMapView and stringify it using ToString methods, since IMapView is an interface, we cannot implement an override of object.ToString. Thus, functionality to stringify an IMapView in 2D-grid like manner is provided via the extension method IMapView<T>.ExtendToString. All built-in concrete implementations of IMapView do provide an appropriate ToString override that forwards to the ExtendToString function. They also provide ToString overloads that take optional fieldSize and elementStringifier parameters. Those parameters have the same meaning as they do in ExtendToString as defined below.

Basic Usage

The IMapView<T>.ExtendToString function takes many parameters and is highly customizable, however all the parameters are optional, so basic usage simply involves calling the function:

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap:");
System.Console.WriteLine(arrayMap.ExtendToString());

fieldSize Parameter

The fieldSize parameter (optional) performs String.Format like functions, wherein each value that is printed is given exactly fieldSize characters on screen, and padded with spaces to fill any unused spaces. This ensures that the values are lined up with each other properly. Notice how in the first output statement below, the numbers on each row do not line up properly since they are of different lengths. In the second and third examples, they line up neatly:

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap:");
System.Console.WriteLine(arrayMap.ExtendToString());

// We use field size of 4, because our arrayMap values range from 0 to 99 (max 2 characters long).
// This ensures that each one has enough space, plus some extra.  A positive number as specified
// here right-aligns the text  
System.Console.WriteLine("arrayMap right-align:");
System.Console.WriteLine(arrayMap.ExtendToString(fieldSize: 4));

// A negative number as specified here left-aligns the text  
System.Console.WriteLine("arrayMap left-align:");
System.Console.WriteLine(arrayMap.ExtendToString(fieldSize: -4));

begin Parameter

The begin parameter (optional) simply specifies text to insert before the actual data print-out:

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap with manual beginning text:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine();

System.Console.WriteLine(arrayMap.ExtendToString(begin: "arrayMap using begin:\n"));

beginRow Parameter

The beginRow parameter (optional) specifies text to insert before each row in the print-out:

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap with no beginning row text:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine();

System.Console.WriteLine("\narrayMap with row marker:");
System.Console.WriteLine(arrayMap.ExtendToString(beginRow: "row: "));

elementStringifier Parameter

The elementStringifier parameter (optional) allows you to specify a custom function to generate a string for each value. In the example below, we use a custom function to display true as . and false as #:

ArrayMap<bool> arrayMap = new ArrayMap<bool>(10, 10);
// See MapGeneration documentation for details, but sets myMap such that walls
// are false (unwalkable), and floors are true (walkable)
RectangleMapGenerator.Generate(arrayMap);

System.Console.WriteLine("arrayMap:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine();

// Here, we stringify an element as "." if it is true, and "#" if it is false
System.Console.WriteLine("arrayMap custom:");
System.Console.WriteLine(arrayMap.ExtendToString(elementStringifier: v => v ? "." : "#"));

rowSeparator Parameter

The rowSeparator parameter (optional) specifies text to be inserted at the end of each row except the last (so the text that separates each row). When not specified, it defaults to "\n" (newline):

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap with \\n ending row text:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine();

System.Console.WriteLine("\narrayMap with row separation marker:");
System.Console.WriteLine(arrayMap.ExtendToString(rowSeparator: ":row\n"));

elementSeparator Parameter

The elementSeparator parameter (optional) specifies text to be inserted at the end of each value except the last one in a row (so the text that separates each element from the next). When not specified, it defaults to " " (space):

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap with space ending element text:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine();

System.Console.WriteLine("\narrayMap with | ending element text:");
System.Console.WriteLine(arrayMap.ExtendToString(elementSeparator: "|"));

endRow Parameter

The endRow parameter (optional) specifies text to insert after each row in the print-out. This differs from the rowSeparator parameter; this text is inserted at the end of each row, whereas the rowSeparator is only placed between two rows.

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap with no ending row text:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine();

System.Console.WriteLine("\narrayMap with end of row marker:");
System.Console.WriteLine(arrayMap.ExtendToString(endRow: "endrow:"));

System.Console.WriteLine("\nThis differs from arrayMap with rowSeparator:");
System.Console.WriteLine(arrayMap.ExtendToString(rowSeparator: "rowsep:\n"));

System.Console.WriteLine("\nbeginRow, endRow, and rowSeparator make more sense when used in conjunction:");
System.Console.WriteLine(arrayMap.ExtendToString(beginRow: "[", endRow: "]", rowSeparator: ",\n"));

end Parameter

The end parameter (optional) simply specifies text to insert after the actual data print-out:

ArrayMap<int> arrayMap = new ArrayMap<int>(10, 10);
// Assign each value a unique integer for display purposes
foreach (var position in arrayMap.Positions())
    arrayMap[position] = position.ToIndex(arrayMap.Width);

System.Console.WriteLine("arrayMap with manual end text:");
System.Console.WriteLine(arrayMap.ExtendToString());
System.Console.WriteLine("My manual ending text");
System.Console.WriteLine();

System.Console.WriteLine("arrayMap with automatic ending text: ");
System.Console.WriteLine(arrayMap.ExtendToString(end: "\nmy automatic ending text:\n"));
Clone this wiki locally