Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 1061995
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 16, 20262026-05-16T18:33:26+00:00 2026-05-16T18:33:26+00:00

Let’s say you have the following structure in C#: struct Piece : IEquatable<Piece> {

  • 0

Let’s say you have the following structure in C#:

struct Piece : IEquatable<Piece> {
    public readonly int size;
    public readonly bool[,] data;

    public Piece(Piece p) {
        size = p.size;

        data = (bool[,])p.data.Clone();
    }
    public Piece(int s, bool[,] d) {
        size = s;
        if (d.GetLength(0) != s || d.GetLength(1) != s) throw new ArgumentException();

        data = (bool[,])d.Clone();
    }

    public bool Equals(Piece other) {
        if (size != other.size) return false;

        return (data.Equals(other.data));
    }
}

The idea is that it represents a sizexsize set of bits which represents a shape (a bit map, if you will).

Now, not all possible combinations of bits are valid. I have a few requirements:

  1. There must be only size bits total. (This is easy, I have already implemented this)
  2. All bits must be contiguous.

So, again assuming size==4, the following is good:

..#.
..#.
.##.
....

While the following is not:

...#
...#
#...
#...

How do I determine whether all the bits are contiguous or not?

Edit: Here is the full code, incorporating Eric Lippert’s answer. This can definitely be tightened up, performance-wise, but it’s very readable.

struct Point : IEquatable<Point> {
    public int X, Y;

    public Point(int x, int y) {
        X = x; Y = y;
    }

    public bool Equals(Point obj) {
        return X == obj.X && Y == obj.Y;
    }

    public override bool Equals(object obj) {
        if (obj == null) return false;

        if(obj is Point)
            return Equals((Point)obj);

        return false;
    }

    public override int GetHashCode() {
        return X ^ ~Y;
    }

    public static bool operator == (Point p1, Point p2) {
        return p1.X == p2.X && p1.Y == p2.Y;
    }

    public static bool operator !=(Point p1, Point p2) {
        return p1.X != p2.X || p1.Y != p2.Y;
    }

    public static readonly Point Empty = new Point(int.MinValue, int.MinValue);
}

struct Piece : IEquatable<Piece> {
    public readonly int size;
    public readonly bool[,] data;
    private bool valid;

    public Piece(Piece p) {
        size = p.size;
        valid = p.valid;
        data = (bool[,])p.data.Clone();
    }
    public Piece(int s, bool[,] d) {
        size = s;
        if (d.GetLength(0) != s || d.GetLength(1) != s) throw new ArgumentException();

        data = (bool[,])d.Clone();
        valid = false;

        CalcValidity();
    }

    public bool IsValid {
        get {
            return valid;
        }
    }


    private enum Square {
        White,
        Black,
        Red,
        Blue,
    }

    private int NumSquares(Square[,] map, Square q) {
        int ret = 0;
        for (int y = 0; y < size; y++) {
            for(int x = 0; x < size; x++) {
                if (map[x, y] == q) ret++;
            }
        }
        return ret;
    }

    private Point PickSquare(Square[,] map, Square q) {
        for (int y = 0; y < size; y++) {
            for (int x = 0; x < size; x++) {
                if (map[x, y] == q) return new Point(x, y);
            }
        }
        return Point.Empty;
    }

    private void MakeNeighboursRed(Square[,] map, Point p) {
        if (p.X > 0 && map[p.X - 1, p.Y] == Square.Black) map[p.X - 1, p.Y] = Square.Red;
        if (p.X < size - 1 && map[p.X + 1, p.Y] == Square.Black) map[p.X + 1, p.Y] = Square.Red;

        if (p.Y > 0 && map[p.X, p.Y - 1] == Square.Black) map[p.X, p.Y - 1] = Square.Red;
        if (p.Y < size - 1 && map[p.X, p.Y + 1] == Square.Black) map[p.X, p.Y + 1] = Square.Red;
    }

    private void CalcValidity() {
        Square[,] map = new Square[size, size];

        int count = 0;
        Point square = Point.Empty;


        for (int y = 0; y < size; y++) {
            for (int x = 0; x < size; x++) {
                if (data[x, y]) {
                    map[x, y] = Square.Black;
                    square = new Point(x, y);
                } else {
                    map[x, y] = Square.White;
                }
            }
        }

        if (square != Point.Empty) {
            map[square.X, square.Y] = Square.Red;
        }

        while (true) {
            if (NumSquares(map, Square.Red) == 0) {
                if (NumSquares(map, Square.Black) == 0) {
                    valid = count == size;
                    return;
                } else {
                    valid = false;
                    return;
                }
            } else {
                square = PickSquare(map, Square.Red);

                MakeNeighboursRed(map, square);
                map[square.X, square.Y] = Square.Blue;
                count++;
            }
        } 
    }

    #region IEquatable<Piece> Members

    public bool Equals(Piece other) {
        if (size != other.size) return false;

        for (int y = 0; y < size; y++) {
            for (int x = 0; x < size; x++) {
                if (data[x, y] != other.data[x, y]) return false;
            }
        }
        return true;
    }

    #endregion
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-16T18:33:27+00:00Added an answer on May 16, 2026 at 6:33 pm

    Here’s a way to think about the flood fill algorithm that does not use recursion.

    Start with every square set to either white (blank) or black (filled). The question is “are the black regions contiguous or not?”

    You can use this algorithm:

    1. If there are no black squares then it is true that there are no discontiguous black regions, so you’re done.
    2. Otherwise, there is at least one black square. Pick any black square and turn it red.
    3. If there are no red squares and no black squares then you’re done, and the black regions are contiguous.
    4. If there are no red squares but one or more black squares then you’re done. The black regions are not contiguous. The region that is still black is discontiguous from the region that is blue.
    5. Otherwise, there must be at least one red square. Pick any red square. Turn all its black neighbours to red, and then turn it blue.
    6. Go back to step 3.

    See how that works? The red squares are the “edges” of the region that is not flood-filled. The blue squares are the flooded region. If the blue floods all the black then it must have been contiguous.

    UPDATE: Re, your comment:

    Thank you so much! This is perfect. I love your blog, especially the articles on LINQ and “writing what you mean”, and I tried to apply those principles here

    Thanks for the kind words. If what you like is very “LINQ-y” solutions to these sorts of problems then I would not use the solution that I sketched out here. Notice that the algorithm is basically “mutate a data structure to learn facts about it”. That’s not a very LINQ-ish thing to do. LINQ is all about querying data structures without modifying them.

    If I wanted to make a more LINQ-like solution to your problem then I would take a very different approach. Here’s what I’d do.

    First off, do you know what an “equivalence class” or an “equivalence relation” is? I’ll briefly define them if you don’t know. A relation is a function that takes two things and returns a bool. For example, “less than”, “equal to”, “have the same last digit in base ten” and “sum to an even number” are all relations on integers. An equivalence relation (A~B) is a relation which is reflexive (X~X is always true), symmetric (X~Y and Y~X are the same) and transitive (if X~Y and Y~Z are both true then so is X~Z).

    In our examples, “less than” is transitive but not reflexive or symmetric. The other three are equivalence relations.

    An equivalence relation partitions the set into equivalence classes. For example, the equivalence relation “sum to an even number” partitions the integers into two equivalence classes: the even numbers and the odd numbers. Pick any two odd numbers and X~Y is true. Pick any two even numbers and X~Y is true. But an odd number and an even number, X~Y is false. All the even numbers are “equivalent” for the purposes of this relation.

    Now consider the relation “is a neighbour on this tile set” for one of your tile sets. Clearly that is not an equivalence relation; it is symmetric but not reflexive or transitive. But any relation can be extended to be an equivalence relation by defining a new relation that is the reflexive and transitive closure of the relation. This is the “is reachable from” relation.

    Your question is therefore essentially “how many equivalence classes are there for the equivalence relation of reachability”? If the answer is zero then there are no black regions. If the answer is one then there is a single contiguous region. If it is more than one then there must be discontiguous regions.

    Therefore another way to characterize your question is “given that there is at least one black tile, is the entire set of black tiles identical with the reflexive and transitive closure of the neighbour relation on an arbitrary tile?” If the answer is “yes” then there is a single contiguous region. If “no” then there must be a region that is not reachable.

    Since you have a way to count tiles, and since the numbers are finite integers, we can do even better than that. We can just ask “is the size of the reflexive and transitive closure of the neighbour relation on an arbitrary black tile identical with the count of all black tiles?” to solve your problem.

    So how to answer that question? Suppose you have a method that takes a tile and returns a sequence of its neighbours:

    public static IEnumerable<Tile> GetNeighbours(Tile tile)
    {
         ... yield return the north, south, east and west neighbours
         ... if they exist and they are on
    }
    

    Basically this method is “given a tile, give me all the tiles that have the neighbour relation with it”. This works great if you can compute which members have a relation with a given member, which clearly in this case you can do so cheaply.

    Now you can compute the transitive and reflexive closure of that relation using the code I posted here:

    http://blogs.msdn.com/b/ericlippert/archive/2010/02/08/making-the-code-read-like-the-spec.aspx

    And now your entire algorithm becomes very short indeed:

    bool HasExactlyOneRegion()
    {
        return (tilesTurnedOn.Count == 0) ? 
            false : 
            TransitiveAndReflexiveClosure(GetNeighbours, tilesTurnedOn.First()).Count == tilesTurnedOn.Count;
    }
    

    If you have the right tools at your disposal then the solution is a single statement!

    Notice that the two solutions I’ve given (and Albin’s sketch as well) are identical in their operation. In my second solution the “red tiles” of the first solution are the tiles in the “stack” data structure, and the “blue tiles” are the tiles in the “closure” data structure of the code in the link I gave.

    The difference between the solutions is only in how you think about the solution. I like to think mathematically, so I would prefer the second solution. It’s all about sets and relations and closures, very abstract ideas. If you’re more of a visual thinker then my first solution, where you can visualize a red-edged wave spreading into a black area until it is full, might be easier to understand and reason about.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Let say I have the following desire, to simplify the IConvertible's to allow me

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.