0
\$\begingroup\$

I'm trying to make the pulse system in a "build the pipeline" puzzle game to check if tiles are connected to the starting tile.

There is a weird bug that prevents tiles on the right and tiles on the top of any powered up tile (including starting one) to change state, but it works fine with tiles on the left and bottom.

I tried to log this and it happened that the cycle is going trough these tiles, but their state doesn't change. Here is an example:

Screenshot of pipe game

Here is the code. If I remove this bit:

 else
              p.GetComponent<piece>().ConnectedToStart = false;

then it is changing the state as it should, but I have no idea how to disable tiles that should be unpowered in any other way. Also, the question is still there - why does it only work for the tiles on the left and bottom?

    public class Puzzle
    {

        public int winValue;
        public int curValue;

        public int width;
        public int height;
        public piece[,] pieces;

    }


        public Puzzle puzzle;

        GameObject[] pieces;
    void Start()
    {
        pieces = GameObject.FindGameObjectsWithTag("Piece");
    }

    void Update()
    {
        CheckForStartConnections();
    }

    public void CheckForStartConnections()
    {

        foreach (var p in pieces)
        {
            if (p.GetComponent<piece>().StartPiece)
                Pulse((int)p.transform.position.x, (int)p.transform.position.y);
            else
              p.GetComponent<piece>().ConnectedToStart = false;
        }
    }

    void Pulse(int w, int h)
    {
        puzzle.pieces[w, h].GetComponent<piece>().ConnectedToStart = true;
        //compares left
        if (w != 0)
            if (puzzle.pieces[w, h].values[3] == 1 && puzzle.pieces[w - 1, h].values[1] == 1)
            {
                if (!puzzle.pieces[w - 1, h].GetComponent<piece>().ConnectedToStart)
                    Pulse((int)puzzle.pieces[w - 1, h].transform.position.x, (int)puzzle.pieces[w - 1, h].transform.position.y);
            }
        //compares bottom
        if (h != 0)
            if (puzzle.pieces[w, h].values[2] == 1 && puzzle.pieces[w, h - 1].values[0] == 1)
            {
                if (!puzzle.pieces[w, h - 1].GetComponent<piece>().ConnectedToStart)
                    Pulse((int)puzzle.pieces[w, h - 1].transform.position.x, (int)puzzle.pieces[w, h - 1].transform.position.y);
            }
        //compares top
        if (h != puzzle.height - 1)
            if ((puzzle.pieces[w, h].values[0] == 1 && puzzle.pieces[w, h + 1].values[2] == 1))
            {
                if (!puzzle.pieces[w, h + 1].GetComponent<piece>().ConnectedToStart)
                {
                    Pulse((int)puzzle.pieces[w, h + 1].transform.position.x, (int)puzzle.pieces[w, h + 1].transform.position.y);
                }
            }
        //compares right
        if (w != puzzle.width - 1)
            if (puzzle.pieces[w, h].values[1] == 1 && puzzle.pieces[w + 1, h].values[3] == 1)
            {
                if (!puzzle.pieces[w + 1, h].GetComponent<piece>().ConnectedToStart)
                {
                    Pulse((int)puzzle.pieces[w + 1, h].transform.position.x, (int)puzzle.pieces[w + 1, h].transform.position.y);
                }
            }
}
```
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I think this is a problem of ordering. Let's look at this code

foreach (var p in pieces) { 
    if (p.GetComponent<piece>().StartPiece)
         Pulse((int)p.transform.position.x, (int)p.transform.position.y); 
    else
         p.GetComponent<piece>().ConnectedToStart = false; 
}

Imagine you have 5 pieces, all of them connected.

The first two pieces you visit are not the start piece, so mark them not connected to the start.

The third piece you visit is the start piece, so you pulse from there and recursively mark all the pieces as connected to the start.

Then your foreach loop keeps going.

It visits the fourth and fifth pieces, which are not the start piece, so it marks them not connected, overwriting the value you wrote in your pulse call!

So, what you want to do is separate your two steps:

  1. Clearing all previous ConnectedToStart markings, then

  2. Propagating new ConnectedToStart values with a call to Pulse for each start piece.

This might look like...

Stack<Piece> frontier = new Stack<Piece>();

void UpdateConnectedness() {
    foreach(var piece in pieces) {
        if(piece.StartPiece)
             frontier.Push(piece);
        else
             piece.ConnectedToStart = false;
    }

    while(frontier.Count > 0) {
         Pulse(frontier.Pop());
    }
}

(Here I propose we use the Piece type throughout, using FindObjectsOfType instead of tags to collect our pieces, and using a piece as the argument to Pulse, since we need to look it up in there anyway)

One handy thing with this approach: instead of recursing, Pulse can just mark the tile connected and then push it onto the stack. This will prevent you from overflowing the callstack, even on truly immense maps. Or you could trade the stack for a queue to iterate in breadth-first order, which would let you compute distance from the start at the same time — helpful if you ever want to draw animated water/electrical current flowing outward.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .