HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

Generate Buildings in City

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
citygeneratebuildings

Problem

I am developing a game, its map is based on a 2D city. The city is being generated as string, # are streets, O are buildings.

Here is an example:

#######
#OOOOO#
#OOOOO#
#######
#OOOOO#
#OOOOO#
#######


Now I am replacing the Os with other buildings, by replacing the character with an other. The other buildings are defined in a dictionary, where the identifying character is the key and the value is a number between 1 and 100 which is the propability of the building to replace an O.

Currently I am doing it like this:

// Turn city into charArray
char[] cityChars = renderedCity.ToCharArray();

// Cycle through all KeyValuePairs, ordered by Value ascending
foreach (KeyValuePair kvp in specialBuildings.OrderBy(key => key.Value))
{
    Random r = new Random(seed);
    // Do the following for all normal buildings in the city
    // If a random seeded number is smaller or equal to the propability of the spawn, change the building into the new one
    cityChars = cityChars.Select(c => c == 'O' ? (r.Next(0, 100) <= kvp.Value ? kvp.Key : c) : c).ToArray();
}


Is there a more elegant way to do this? In the best case, this would be achieved by using no loops at all, but I am struggling with LINQ a bit.

Solution

I think even the simplest solutions can and should be made modular and SOLID. Let's try to refactor your game to give you an idea how to begin...

We start with a city. A city can have different squares where a square is just a simple character/symbol.

class City : IEnumerable
{
    private readonly List _squares = new List();
    public void Add(Square square)
    {
        _squares.Add(square);
    }
    public IEnumerator GetEnumerator() => _squares.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}


Each square has a position and a symbol. It's abstract so we can derive more concrete types from it.

abstract class Square
{
    protected Square(string symbol, int x, int y)
    {
        Symbol = symbol;
        X = x;
        Y = y;
    }
    public string Symbol { get; }
    public int X { get; }
    public int Y { get; }
    public override string ToString() => Symbol;
}


You seem to have two types currently. A building and a street so here they are:

abstract class Square
{
    protected Square(string symbol, int x, int y)
    {
        Symbol = symbol;
        X = x;
        Y = y;
    }
    public string Symbol { get; }
    public int X { get; }
    public int Y { get; }
    public override string ToString() => Symbol;
}

interface IUpgradable 
{ 
    void Upgrade();
}

abstract class Building : Square, IUpgradable    
{
    protected Building(string symbol, int x, int y, int level)
    : base(symbol, x, y)
    {
        Level = level;
    }
    public int Level { get; private set; }
    public void Upgrade()
    {
        Level++;
    }
    public override string ToString() => "O";
}

class School : Building
{
    public School(int x, int y, int level) : base("H", x, y, level) {}
}

class Hospital : Building
{ 
    public Hospital(int x, int y, int level) : base("S", x, y, level) {}
}

class Street : Square
{
    public Street(int x, int y) : base("#", x, y) { }
    public override string ToString() => "#";
}


Buildings can be upgraded thus the Upgrade method which increases its level. Each upgradable type implements the IUpgradable interface then so you can even filer on that.

I don't know how you generate your map but for the sake of this example I did it by hand:

Here's a simple city:

// ###
// #H#
// ###
var city = new City 
{
    new Street(0, 0), new Street(1, 0),new Street(2, 0),
    new Street(0, 1), new Hospital(1, 1),new Street(2, 1),
    new Street(0, 2), new Street(1, 2),new Street(2, 2),
}


Next we want to upgrade some buildings (I might have the condition for upgrading wrong but it should just demostrate the general idea)

Random r = new Random(seed);

// helper encapsulating the can-upgrade logic
var canUpgradeBuilding = new Func(b => b.Level ().Where(canUpgradeBuilding))
{
    b.Upgrade();
}

Code Snippets

class City : IEnumerable<Square>
{
    private readonly List<Square> _squares = new List<Square>();
    public void Add(Square square)
    {
        _squares.Add(square);
    }
    public IEnumerator<Square> GetEnumerator() => _squares.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
abstract class Square
{
    protected Square(string symbol, int x, int y)
    {
        Symbol = symbol;
        X = x;
        Y = y;
    }
    public string Symbol { get; }
    public int X { get; }
    public int Y { get; }
    public override string ToString() => Symbol;
}
abstract class Square
{
    protected Square(string symbol, int x, int y)
    {
        Symbol = symbol;
        X = x;
        Y = y;
    }
    public string Symbol { get; }
    public int X { get; }
    public int Y { get; }
    public override string ToString() => Symbol;
}

interface IUpgradable 
{ 
    void Upgrade();
}

abstract class Building : Square, IUpgradable    
{
    protected Building(string symbol, int x, int y, int level)
    : base(symbol, x, y)
    {
        Level = level;
    }
    public int Level { get; private set; }
    public void Upgrade()
    {
        Level++;
    }
    public override string ToString() => "O";
}

class School : Building
{
    public School(int x, int y, int level) : base("H", x, y, level) {}
}

class Hospital : Building
{ 
    public Hospital(int x, int y, int level) : base("S", x, y, level) {}
}

class Street : Square
{
    public Street(int x, int y) : base("#", x, y) { }
    public override string ToString() => "#";
}
// ###
// #H#
// ###
var city = new City 
{
    new Street(0, 0), new Street(1, 0),new Street(2, 0),
    new Street(0, 1), new Hospital(1, 1),new Street(2, 1),
    new Street(0, 2), new Street(1, 2),new Street(2, 2),
}
Random r = new Random(seed);

// helper encapsulating the can-upgrade logic
var canUpgradeBuilding = new Func<Building, bool>(b => b.Level <= r.Next(0, 100));

// upgrading buildings
foreach (var b in city.OfType<Building>().Where(canUpgradeBuilding))
{
    b.Upgrade();
}

Context

StackExchange Code Review Q#138578, answer score: 2

Revisions (0)

No revisions yet.