Infinite loop unexplainable

Hello,

I’m still trying to make a Checkers game and this time I’m blocked with a problem of infinite loop. The part that creates the infinite loop is the ia for the computer turn, it’s called once the player has moved on of his pieces.

Here is all my code (I commented it the best way i can to help you) :

public void computerTurn () {

        bool continueSearching = true;
        int randomZ = Random.Range (6, 9);
        int randomX = Random.Range (0, 9);
       
        Tile[] tiles = FindObjectsOfType(typeof(Tile)) as Tile[];

        while(continueSearching == true) {

            // Browse all the tiles
       
            foreach (Tile a_tile in tiles) {

                // If the tile is equals to the new coordinates
           
                if(a_tile.coordinates == new Vector2(randomX, randomZ)){

                    // If its piece isn't null and its piece color is black

                    if(a_tile.piece != default(Piece) && a_tile.piece.color == new Color(0,0,0)) {

                        int diagonal1ID = a_tile.id - 11;
                        int diagonal2ID = a_tile.id + 9;

                        // If the diagonals are in the board

                        if(diagonal1ID > 0 && diagonal1ID <= 100 || diagonal2ID > 0 && diagonal2ID <= 100) {

                            // Browse a second time all the tiles

                            foreach(Tile a_second_tile in tiles) {

                                // If its id is equals to the first diagonal

                                if(a_second_tile.id == diagonal1ID){

                                    // If it hasn't any piece and it hasn't any children

                                    if(a_second_tile.piece == default(Piece) && a_second_tile.transform.childCount == 0){

                                        // Then move the piece to the first diagonal
                                           
                                        Piece randomPiece = a_tile.piece;
                                       
                                        randomPiece.transform.position = new Vector3(a_tile.transform.position.x - 1, 0.6f, a_tile.transform.position.z - 1); 
                                       
                                        foreach(Tile a_fourth_tile in tiles) {
                                           
                                            if(a_fourth_tile.transform.position == new Vector3(randomPiece.transform.position.x, 0, randomPiece.transform.position.z)) {
                                               
                                                a_fourth_tile.piece = randomPiece;
                                                randomPiece.transform.parent = a_fourth_tile.transform;
                                               
                                            }
                                           
                                        }
                                       
                                        a_tile.piece = default(Piece);
                                        continueSearching = false;

                                    // If the first diagonal isn't empty

                                    }else{

                                        // Then browse the tiles a third time

                                        foreach(Tile a_third_tile in tiles) {

                                            // If the tile id is equals to the second diagonal
                                           
                                            if(a_third_tile.id == diagonal2ID){

                                                // If the second diagonal hasn't any piece or children
                                               
                                                if(a_third_tile.piece == default(Piece) && a_third_tile.transform.childCount == 0) {

                                                    // Then move the piece to the second diagonal

                                                    Piece randomPiece = a_tile.piece;
                                                   
                                                    randomPiece.transform.position = new Vector3(a_tile.transform.position.x + 1, 0.6f, a_tile.transform.position.z - 1); 
                                                   
                                                    foreach(Tile a_fourth_tile in tiles) {
                                                       
                                                        if(a_fourth_tile.transform.position == new Vector3(randomPiece.transform.position.x, 0, randomPiece.transform.position.z)) {
                                                           
                                                            a_fourth_tile.piece = randomPiece;
                                                            randomPiece.transform.parent = a_fourth_tile.transform;
                                                           
                                                        }
                                                       
                                                    }
                                                   
                                                    a_tile.piece = default(Piece);
                                                    continueSearching = false;
                                                   
                                                }else{

                                                    // If the two diagonals have got a piece, find a new tile
                                                   
                                                    randomZ = Random.Range (6, 10);
                                                    randomX = Random.Range (0, 10);
                                                    continueSearching = true;
                                                    continue;
                                                   
                                                }
                                               
                                            }
                                           
                                        }
                                           
                                    }

                                }
   
                            }

                        }
                   
                    }else{

                        // If the tile hasn't any piece, find a new tile

                        randomZ = Random.Range (6, 9);
                        randomX = Random.Range (0, 9);
                        continueSearching = true;
                        continue;
                       
                    }
                   
                }
       
            }
           
        }
       
    }

Sometimes it works but sometimes, unity crashes and I need to restart it.
If you find where’s the problem, please tell me :slight_smile:

Thank you.

The code is depending on random numbers to determine whether to break out of the loop or to stay in the loop. So, yes, it will sometimes work and sometimes remain in the loop.

There is really no reason to be using random numbers here. You need to be evaluating all possible moves (or a sub-tree of all possible moves) and ranking the moves. Then play the move that will yields the highest rank.

Thank you for your answer. I want to use random numbers to begin because the three you said is difficult to create and use. But the thing I don’t understand, is that if the random number isn’t right, it will search another until it will be right now? And this way it shouldn’t be an infinite loop, should it?

In this kind of code, I always put iterator with iteration limiter (as high as needed - like 10 000 or more), to breake while loop in event of failure, to prevent hard restars. Also, remeber in Random 6,10 You will get only numbers 6,7,8,9. I had a smiliar problem, and it was setting wronlgy random numbers, so I would start debugging from checking random values.

Thank you but my range is good, my board contains 10x10 tiles and it begins at (0,0) and finishes at (9,9).

UP ! :slight_smile:

Have you tried debug.log(“something useful”) to see whitch of the continue statements (and under what conditions) the loop is caused?

Thank you for your answer but yes i already tried and i didn’t found what’s the problem. I’m out of ideas, so if someone sees the problem, please tell me what is it :slight_smile:

Quoted for truth - I almost never have a while loop without a loop breaker, just in case.

One thing I’ve noticed is that, at the end, you have an if statement that if true, breaks out of the while loop, or if false, re-initializes the random numbers. It seems to me, however, that it’s possible in a given loop for this if statement to NEVER be executed at all, if this:

(a_third_tile.id== diagonal2ID)

Happens to never be true.

I don’t fully follow your algorithm, but if it doesn’t break your logic, I would suggest moving the re-randomization of randomX and randomZ to the beginning of the while loop instead of doing it at particular points within the loop. That will protect you from a single malformed random combination of numbers ruining your loop for eternity.

If that DOES break your logic, I would put in a flag to ensure that one or the other of those if statements has indeed been executed, and if that flag is not tripped at the end of the loop, abort and throw up and error message.

Hello StarManta. Thank you very much for your answer, I just moved the rerandomization to the begining of the while and it works aswell. If you want, I’m trying to make like an IA for my checkers game and just found this algorithm for my IA (there’s a part of random so that’s no really an IA but it works aswell, and there isn’t many strategies in a checkers game) :

  • Find a random tile

  • Check the diagonals of the random tile that the algorithm as found

  • If the diagonal 1 is free, go there

  • Else, check diagonal 2

  • If it’s free, go there

  • If it isn’t search a new tile (rerandomize values)

Do you think a really IA is needed in a checkers game ? Thank you

Well, in terms of your overall algorithm… it seems really bizarre the way you’re doing randomization. You’re choosing a random spot on the board and then seeing if a piece is there… Why would you not choose a random piece instead?

Because i’d need to get the parent of the piece (the tile of the piece) to get the diagonals, and with this method, i define the piece at the end so i don’t need to get the parent at the begining. And by the way, the piece hasn’t coordinates or whatever, every information about its position on the board is stocked in its parent tile.

It seems to me like a piece ought to know its position. Like, if you can’t go from piece to its neighbors, you should reconsider your data structuring. It’s extremely common for child objects to have back-links to their parents for exactly this reason. Maintaining a reference from piece-to-tile is 10x easier than implementing an algorithm like this one and then trying to test against all the possible edge cases.

While we’re on the subject… let me double check this. You are looping through all the tiles in the board, until you find the one tile that matches the id. Is that correct?

Alright, let me make my advice simple: This algorithm should not have any of those foreach loops in it at all. All of those are symptoms of an incredibly incomplete data structure. It seems like you feel like you’re supposed to be storing as little data in your pieces and tiles as you can get away with; believe me, the opposite is true - these things should be directly linking to each other, liberally. Every piece should have a public Tile tileThisPieceIsOn, and every tile should have a Tile[ ] diagonalsAvailableFromThisTile array.

1 Like

Thank you but what does it changes if I use the structure you said?

It reduces your headaches lol.

You should reverse the way you handle things:

  • Have an array of active peices

  • Randomly (if you don’t want to do heuristics or something to determine a good peice to use given the situation) select a peice to have the AI (I saw you said IA a few times, but it’s AI, artificial intelligence) use

  • Check the diagonal tiles from that peices tile (which the peice keeps reference to its current tile, the tile keeps reference to its diagonal tiles it can reach).

  • Randomly select an available move!