Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@handcraftsman
Created July 6, 2011 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save handcraftsman/1067469 to your computer and use it in GitHub Desktop.
Save handcraftsman/1067469 to your computer and use it in GitHub Desktop.
fills a 1000x1000 grid with 300 randomly shaped terrains based on 14 terrain types, inspired by http://stackoverflow.com/questions/6586338/randomly-and-efficiently-filling-space-with-shapes/6588235#6588235
// * **********************************************************************************
// * Copyright (c) Clinton Sheppard
// * This source code is subject to terms and conditions of the MIT License.
// * By using this source code in any fashion, you are agreeing to be bound by
// * the terms of the MIT License.
// * You must not remove this notice from this software.
// * **********************************************************************************
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using NUnit.Framework;
namespace Scratch.FillGrid
{
public class LandType
{
private static readonly List<LandType> _list = new List<LandType>();
public static readonly LandType AlluvialPlain = new LandType(Color.LightGray);
public static readonly LandType CoastalPlain = new LandType(Color.PaleGoldenrod);
public static readonly LandType CultivatedLand = new LandType(Color.IndianRed);
public static readonly LandType FloodPlain = new LandType(Color.Lavender);
public static readonly LandType Forest = new LandType(Color.ForestGreen);
public static readonly LandType ForestSwamp = new LandType(Color.BurlyWood);
public static readonly LandType GrassLand = new LandType(Color.DarkSeaGreen);
public static readonly LandType NeedleForest = new LandType(Color.DarkGreen);
public static readonly LandType Plateau = new LandType(Color.RosyBrown);
public static readonly LandType Sand = new LandType(Color.SandyBrown);
public static readonly LandType Savannah = new LandType(Color.SpringGreen);
public static readonly LandType SemiArid = new LandType(Color.PapayaWhip);
public static readonly LandType Swamp = new LandType(Color.CadetBlue);
public static readonly LandType TidalMarsh = new LandType(Color.CornflowerBlue);
// .. etc, for more land form names see:
// http://makingmaps.net/2008/04/03/map-symbols-landforms-terrain/
private LandType(Color color)
{
_list.Add(this);
Color = color;
}
public Color Color { get; set; }
public static IEnumerable<LandType> GetAll()
{
return _list;
}
}
public static class IEnumerableExtensions
{
private static readonly Random _random = new Random();
public static T PickRandom<T>(this IEnumerable<T> items)
{
var list = new List<T>();
foreach (var item in items)
{
if (_random.Next(1000) == 0)
{
return item;
}
list.Add(item);
}
if (list.Count > 0)
{
return list[_random.Next(list.Count)];
}
return default(T);
}
}
[TestFixture]
public class Demo
{
public static int Height = 1000;
public static int Width = 1000;
public static int XOffsetEast = 1;
public static int XOffsetWest = -1;
public static int YOffsetNorth = -1;
public static int YOffsetSouth = 1;
[Test]
public void FillGrid()
{
var bitmap = new Bitmap(Width, Height);
var defaultPixel = bitmap.GetPixel(0, 0);
var hasOpenNeighbors = new HashSet<Land>();
var random = new Random();
var landTypes = LandType.GetAll().ToList();
for (int i = 0; i < 300; i++)
{
int randX = random.Next(Width);
int randY = random.Next(Height);
var point = new Point(randX, randY);
if (bitmap.GetPixel(randX, randY) == defaultPixel)
{
var landType = landTypes[random.Next(landTypes.Count)];
var item = new Land
{
Location = point,
LandType = landType
};
bitmap.SetPixel(randX, randY, landType.Color);
hasOpenNeighbors.Add(item);
}
}
while (hasOpenNeighbors.Any())
{
var toRemove = new List<Land>();
var toAdd = new List<Land>();
foreach (var item in hasOpenNeighbors)
{
var neighborLocation = GetNeighbors(item.Location)
.Where(x => bitmap.GetPixel(x.X, x.Y) == defaultPixel)
.PickRandom();
if (neighborLocation == default(Point))
{
toRemove.Add(item);
continue;
}
bitmap.SetPixel(neighborLocation.X, neighborLocation.Y, item.LandType.Color);
toAdd.Add(new Land
{
Location = neighborLocation,
LandType = item.LandType
});
}
foreach (var land in toRemove)
{
hasOpenNeighbors.Remove(land);
}
foreach (var land in toAdd)
{
hasOpenNeighbors.Add(land);
}
}
bitmap.Save("result.jpg");
}
public static Point CreatePoint(Point point, int xOffset, int yOffset)
{
return new Point(point.X + xOffset, point.Y + yOffset);
}
public static IEnumerable<Point> GetNeighbors(Point location)
{
return new Func<Point, Point>[]
{
GoNorth, GoNorthEast, GoEast, GoSouthEast,
GoSouth, GoSouthWest, GoWest, GoNorthWest
}
.Select(direction => direction(location))
.Where(IsOnTheBoard);
}
public static Point GoEast(Point point)
{
return CreatePoint(point, XOffsetEast, 0);
}
public static Point GoNorth(Point point)
{
return CreatePoint(point, 0, YOffsetNorth);
}
public static Point GoNorthEast(Point point)
{
return CreatePoint(point, XOffsetEast, YOffsetNorth);
}
public static Point GoNorthWest(Point point)
{
return CreatePoint(point, XOffsetWest, YOffsetNorth);
}
public static Point GoSouth(Point point)
{
return CreatePoint(point, 0, YOffsetSouth);
}
public static Point GoSouthEast(Point point)
{
return CreatePoint(point, XOffsetEast, YOffsetSouth);
}
public static Point GoSouthWest(Point point)
{
return CreatePoint(point, XOffsetWest, YOffsetSouth);
}
public static Point GoWest(Point point)
{
return CreatePoint(point, XOffsetWest, 0);
}
public static bool IsOnTheBoard(Point point)
{
return point.X >= 0 && point.X < Width && point.Y >= 0 && point.Y < Height;
}
public class Land
{
public LandType LandType { get; set; }
public Point Location { get; set; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment