Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

The rules for this program are to:

  • Generate n X n grid.
  • Arbitrarily pick a point and grow a 'shape' based off that initial point.
  • Must have at least 3 points.
  • Strongly biased to not completely fill grid.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;

namespace ShapeTestInConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            Shape.Initialize(4, 4);
            Shape shape = new Shape();
            Draw(shape);
            Console.ReadLine();
        }

    private static void Draw(Shape shape)
    {
        for (var x = 0; x < Shape.Width; x++)
        {
            for (var y = 0; y < Shape.Height; y++)
            {
                Console.Write("|");
                Console.Write(" " + shape.ShapeDefinition[x, y].ToString() + " ");
                Console.Write("|");
            }
            Console.Write("\n");
            for (var i = 0; i < Shape.Width * 5; i++)
                Console.Write("_");
            Console.Write("\n");
        }
    }
}
public class Shape
{
    private static readonly Random _random;

    private static readonly Tuple<int, int>[] _adjacent = new[]
        {
            Tuple.Create(0,-1),
            Tuple.Create(1,0),
            Tuple.Create(0,1),
            Tuple.Create(-1,0)
        };

    public byte[,] ShapeDefinition { get; private set; }

    public static int Width { get; private set; }
    public static int Height { get; private set; }

    static Shape()
    {
        _random = new Random();
    }

    public Shape()
    {
        ShapeDefinition = new byte[Width, Height];

        //Place seed point
        ShapeDefinition[_random.Next(0, Width), _random.Next(0, Height)] = 1;
        var activePoints = 1;

        var starts = new List<Point>();
        var neighbors = new List<Point>();
        var neighborsToActivate = new List<Point>();

        Build(starts, neighbors, neighborsToActivate, activePoints, 0, 1);

    }

    public static void Initialize(int width, int height)
    {
        Width = width;
        Height = height;
    }

    private void Build(List<Point> starts, List<Point> neighbors, List<Point> neighborsToActivate,
        int activePoints, int runCount, int currentLevel)
    {                    
        // Ditch if...  Has bias for 4 X 4 grid
        if (activePoints >= (Width * Height) || runCount > 5 || currentLevel >= 5)
            return;
        // minimum 3 points desired
        if (activePoints >= 3 && activePoints < 6)
        {
            if (_random.Next(0, 9) == 2)
                return;
        }
        else if (activePoints >= 6 && activePoints < 8)
        {
            if (_random.Next(0, 6) == 2)
                return;
        }
        else if (activePoints >= 8)
        {
            if (_random.Next(0, 5) == 2)
                return;
        }

        starts.Clear();
        // Gather points 
        for (var x = 0; x < ShapeDefinition.GetLength(0); x++)
            for (var y = 0; y < ShapeDefinition.GetLength(1); y++)
            {
                if (ShapeDefinition[x,y] == currentLevel)
                    starts.Add(new Point(x,y));
            }

        neighbors.Clear();
        // with each start get adjacent usable point
        foreach (var currentStartPoint in starts)
        {
            for (var i = 0; i < _adjacent.Length; i++)
            {
                var neighborX = currentStartPoint.X + _adjacent[i].Item1;
                var neighborY = currentStartPoint.Y + _adjacent[i].Item2;

                if (InBounds(neighborX, neighborY))
                {
                    if (ShapeDefinition[neighborX, neighborY] == 0)
                        neighbors.Add(new Point(neighborX, neighborY));
                }
            }
            neighborsToActivate.Clear();
            // randomly pick which usable point(s) will be include with shape
            neighborsToActivate = neighbors.OrderBy(n => _random.Next())
                                           .Take(_random.Next(1, neighbors.Count + 1))
                                           .ToList();

            for (var j = 0; j < neighborsToActivate.Count; j++)
            {
                ShapeDefinition[neighborsToActivate[j].X, neighborsToActivate[j].Y] = (byte)(currentLevel + 1);
                activePoints++;
            }
        }

        runCount++;
        currentLevel++;
        Build(starts, neighbors, neighborsToActivate, activePoints, runCount, currentLevel);
    }

    private static bool InBounds(int x, int y)
    {
        return x >= 0 && x < Width &&
               y >= 0 && y < Height;
    }
}
}

This is a good facsimile of what I trying to accomplish now. My own thoughts on improvements would be that Shape, for how I would like to use it, should not have methods to create itself and should instead be created by a ShapeFactory or ShapeBuilder class. A Shape's only job is to be a shape.

share|improve this question
add comment

1 Answer

up vote 1 down vote accepted
  1. runCount and CurrentLevel seem redundant.

  2. I don't see why you pass start, neighbors and neighborsToActivate as parameters to the recursive function since you just clear them without using the data.

  3. activePoint might be better off being a member of Shape since that information might useful elsewhere too.

  4. I would have made Draw a method of Shape since it is so closely associated with Shape.

You are now storing the shape as "active" points in a grid. It would be possible also to store the shape as a list of points, e.g. {(2,3), (3,3), (3,4)}. I'm not saying it is better here, but it might be a better option for some applications, especially if you have a very large grid with very few active points.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.