Help me squish my last bug... AKA ... Why isn't this stupid Array working right?

OK, programming peeps … here’s a puzzle for you. This is my “end of game doesn’t work” bug, but it’s a weird one. I would be thrilled if anyone had any insight at all to why this particularly odd bug is happening.

CODE AND EXPLANATION FOLLOW

Assume you’re playing with three players … player 1, 2 and 3; also known in the Array of players as 0, 1 and 2 (whoever made arrays like that should be shot, btw, just saying as a non-programmer). The goal of the game is to move your player into slot 10 (or higher). These terrainSlots are also = victory points, and you can move past 10 so player 2 could have 11, but player 3 or 4 on their turn could move past them to win, so the winner might have 13.

Script Works Correctly: If any single player gets to 10 or higher, game ends perfectly.

Example: If player 2 gets 12 points, but player 3 manages to roar past on his last turn and score 14, the game ends perfectly with player 3 declared the winner … that is, case 1 in the function declareWinnerEndGame () function. In other words, the Debug.Log (WhoIsWinning()); output in this case shows 2.

Example 2: If player 1 gets 10 points, and both players 2 and 3 fail to get to 10 (or more) on their final turns, the game ends perfectly with player 1 declared the winner … that is, case 1 in the function declareWinnerEndGame () function. In other words, the Debug.Log (WhoIsWinning()); output in this case shows 0.

Script Works Correctly: If players 2 and 3 both TIE and score 12, and player 1 doesn’t get to 10 or only gets to 11, the game ends perfectly with a tie declared between 2 and 3 … that is, case 2 in the function declareWinnerEndGame () function. In other words, the Debug.Log (WhoIsWinning()); output in this case shows 1,2.

PLAYER 1 TIE BUG: If player 1 is involved in a tie, either with player 2 or 3, or with both 2 and 3, player 1 is declared the single winner; case 1 in the function declareWinnerEndGame () function. In other words, the Debug.Log (WhoIsWinning()); output in this case shows only 0.

This is the bug and it’s causing me to tear the little hair I actually have remaining completely out.

So, here’s the two functions in the script that pushes the players into an Array and compares their scores to declare a winner.

Challenge? Please tell me what I am doing wrong and let me know (please!). Thank you!

3-2-1 … go!

function WhoIsWinning() : Array {
	
      // first check to see how many players reached the objective
      var numPlayersReachedObjective = 0;
      var whichPlayerReachedObjective = -1;

      for (var i = 0; i < GameController.instance.numberOfPlayers; ++i) {
              if (playersTerrainSlot[i] > 9) {
                      ++numPlayersReachedObjective;
                      whichPlayerReachedObjective = i;
              }
      }
     
      if (numPlayersReachedObjective == 1) {
		var result = new Array();
		result.Push(whichPlayerReachedObjective);
		return result;
	  }	

      else {
          // cycle through each player and find the player with the highest score
          	var winners : Array = new Array();
          	var highest = -1;
              for(var j = 0; j < GameController.instance.numberOfPlayers; ++j) {
                      if (currentScore[j] > highest) {
                              winners.Clear();
                              winners.Push(j);
                              highest = currentScore[j];
                      }
                      else if (currentScore[j] == highest) {
                              winners.Push(j);
                      }
              }
             
        return winners;

      }
}

// ----- declare winner after allowing each *remaining* player in sequence one final turn to also try to reach objective
function declareWinnerEndGame () {
	
	// AUGUST 11 TO TRY TO FIGURE OUT END GAME BUG
	Debug.Log ("## Printing the winners array to see if there is an error here, now in ---> function declareWinnerEndGame()");
	Debug.Log ("## Trying to see the result in the Array ---> function WhoIsWinning()");
	Debug.Log (WhoIsWinning());
	
	// grab the winner(s) from WhoIsWinning()
	var theWinnerTemp = WhoIsWinning();
	var theWinner : int[] = theWinnerTemp.ToBuiltin(int);
	
	messageBackground.active = true;

	switch (theWinner.length){
		case 1: 
		    gameplayMessage.text = "Player " + (theWinner[0] + 1) + " has reached the objective\nand won this battle with\nthe most Victory Points!";
		break;
		
		case 2: 
		    gameplayMessage.text = "The battle has ended in a tie\nbetween Player " + (theWinner[0] + 1) + " and Player " + (theWinner[1] + 1) + ".";
		break;
		
		case 3: 
		    gameplayMessage.text = "The battle has ended in a tie\nbetween Player " + (theWinner[0] + 1) + ", Player " + (theWinner[1] + 1) + ",\nand Player " + (theWinner[2] + 1) + ".";
		break;
		
		default : 
		    gameplayMessage.text = "The battle has ended in a four way tie!\nHeadquarters is proud of your\nextraordinarily valiant efforts, soldiers!";
	}
	
	yield WaitForSeconds (5);
	gameplayMessage.text = "";
	messageBackground.active = false;
    Application.LoadLevel ("missions");

}

The complete Gameplay.js if anyone needs more than this, is found here. OK, It is messy (I still haven’t removed comments I don’t need etc) and a little brute force, but all there. And yes, I now realize I should have made each player an object with attributes to easier track score, position, graphics etc (function Player (icon,score) {}) but I’m not a real programmer and people (Jedd, Rob D, Rob H, and Tim) helped and contributed as they could given my crude explanations.

I will make sure to credit, in game, anyone who can offer a solution to this little bug of mine.

Many many thanks.

wierd.

Use mono-develop and step through it :slight_smile: (Debug with breakpoints, watches etc).

Be careful using the pre-increment operator (++i) instead of the other one (i++). I’ve seen it lead to some hard-to-track bugs. The difference is that it is evaluated AFTER it’s incremented as opposed to before, as with i++. It doesn’t look like it would make any difference here, but you might want to try switching it just for kicks and giggles to see if it helps. Especially if you’re out of ideas.

Not sure that this will help, but it’s worth a shot, since it’s so easy to change (but I would do it on all of them, just to be sure).

I notice that you’re using playersTerrainSlot in the first loop and currentScore in the second. They might be out of sync if updated at different places, causing a bug. I would use one of them consistently. Also, you could skip the first part of the function and go directly to filling winners, there’s no need for the special one-winner check either. It would condense the function into something like this:

function WhoIsWinning() : Array {
	var winners : Array = new Array();
	var highest = -1;
	for(var i = 0; i < GameController.instance.numberOfPlayers; i++) {
		var score = currentScore[i];
		if(score >= 10) {
			if(score > highest) {
				highest = score;
				winners.Clear();
				winners.Push(i);
			}
			else if(score == highest) {
				winners.Push(i);
			}
		}
	}
	return winners;
}

Thanks all. I will look into VCC and Jasper’s suggestions when I get home. I can manage only a small amount of actual programming but am a pretty decent pseudo-programmer in that I can outline in comments what has to get programmed within a project; the syntax itself, not so much.

Hijacking the head programmer here at work from his real job to talk about my code, he thinks it’s difficult to manage and I haven’t very elegantly designed it. If I understood what he said, he’d have suggested instantiating a player object for each player … I guess sort of a …

Player (name, icon, currentScore, terrainSlot, moves, pins, hits)

so that I can more easily track what’s happening during moves etc and keep all of the scattered variables I have like terrainSlot, currentScore etc in one place, rather than all over in many functions. A terrain object as well that held a graphic, type, points for pins and hits, etc. Too late now.

But one thing he’s mentioned, like Jasper, is that somewhere maybe currentScore and playersTerrainSlot are being mixed up, so I’ll be adding

Debug.Log ("## These are the curent scores: " + currentScore);
Debug.Log ("## These are the terrain slots: " + playersTerrainSlot);

to the start of function WhoIsWinning () to output those and see if that is the issue. Otherwise, as Jasper said, it’s that loop.

I will report back as I discover more and want to thank everyone, very much, for the help so far.

I think Jas and your friend might have the right answer - and I do think that creating a Player class might help with that, since you can then validate any changes to it. You might want to give some thought to doing this in C# if you do the class thing, though. It’s more picky, it’s strongly typed, very annoying, but it’s easier to track down bugs because it’s annoying. And it handles classes better IMHO.

Doesn’t appear to be an error in the code you posted. Made a quick test case to verify:

var currentScore = [9, 10, 10];

function Start () {
	declareWinnerEndGame();
}

function WhoIsWinning() : Array {
	
      // first check to see how many players reached the objective
      var numPlayersReachedObjective = 0;
      var whichPlayerReachedObjective = -1;

      for (var i = 0; i < 3; ++i) {
              if (currentScore[i] > 9) {
                      ++numPlayersReachedObjective;
                      whichPlayerReachedObjective = i;
              }
      }
      
      Debug.Log("Number of players who reached objective:   " + numPlayersReachedObjective);
     
      if (numPlayersReachedObjective == 1) {
		var result = new Array();
		result.Push(whichPlayerReachedObjective);
		return result;
	  }	

      else {
          // cycle through each player and find the player with the highest score
          	var winners : Array = new Array();
          	var highest = -1;
              for(var j = 0; j < 3; ++j) {
                      if (currentScore[j] > highest) {
                              winners.Clear();
                              winners.Push(j);
                              highest = currentScore[j];
                      }
                      else if (currentScore[j] == highest) {
                              winners.Push(j);
                      }
              }
             
        return winners;

      }
}

// ----- declare winner after allowing each *remaining* player in sequence one final turn to also try to reach objective
function declareWinnerEndGame () {
	
	// AUGUST 11 TO TRY TO FIGURE OUT END GAME BUG
	Debug.Log ("## Printing the winners array to see if there is an error here, now in ---> function declareWinnerEndGame()");
	Debug.Log ("## Trying to see the result in the Array ---> function WhoIsWinning()");
	Debug.Log (WhoIsWinning());
	
	// grab the winner(s) from WhoIsWinning()
	var theWinnerTemp = WhoIsWinning();
	var theWinner : int[] = theWinnerTemp.ToBuiltin(int);
	
	var gameplayMessage = "";

	switch (theWinner.length){
		case 1: 
		    gameplayMessage = "Player " + (theWinner[0] + 1) + " has reached the objective\nand won this battle with\nthe most Victory Points!";
		break;
		
		case 2: 
		    gameplayMessage = "The battle has ended in a tie\nbetween Player " + (theWinner[0] + 1) + " and Player " + (theWinner[1] + 1) + ".";
		break;
		
		case 3: 
		    gameplayMessage = "The battle has ended in a tie\nbetween Player " + (theWinner[0] + 1) + ", Player " + (theWinner[1] + 1) + ",\nand Player " + (theWinner[2] + 1) + ".";
		break;
		
		default : 
		    gameplayMessage = "The battle has ended in a four way tie!\nHeadquarters is proud of your\nextraordinarily valiant efforts, soldiers!";
	}
	
	Debug.Log(gameplayMessage);
	
    Application.Quit();

}

Put that in any script, make an empty scene and attach that script to the main camera or an empty. You can adjust the current score array in the inspector and hit run, then view the debug log. All scenarios seem to work fine to me.

So if this script works fine, the problem is in the data that it is processing.

Thanks, niosop, it is indeed the case the everything works fine for me to, when forcing the currentScore in the manner. Thanks for the script to test.

I will now have to look back through each of the other functions and find out where the currentScore is getting messed up for the players. Hence, my sudden and keen understanding of why I’d want to simply use a Player object that held, as one of it’s attributes, currentScore.

Odd, it’s telling me that …

-----> These are the current scores: System.Int32[ ]
-----> These are the terrain slots: System.Int32[ ]

Does that mean that my scores are not being converted to ints somewhere???

The default string conversion of an object is simply the name of its type. In this case the log tells you, correctly, that they’re 32-bit integer arrays. Use a method like Join to get some meaningful output.

Debug.Log ("-----> These are the current scores: " + currentScore.Join(", "));
Debug.Log ("-----> These are the terrain slots: " + playersTerrainSlot.Join(", "));

That throws an error in Unity … ‘Join’ is not a member of ‘int[ ]’. Here’s where my non-programmer comes out shining!

Try:

String.Join(",", currentScore)

Not sure if UnityScript will auto convert the ints to strings though, but might work.

My bad, I incorrectly treated them as JavaScript Array objects, which do have Join, instead of regular arrays. I never use JavaScript and here it shows. I think the quickest solution is to just create an Array on the spot. It’s not nice and efficient, but it’s for debugging anyway.

Debug.Log ("-----> These are the current scores: " + new Array(currentScore).Join(", "));
Debug.Log ("-----> These are the terrain slots: " + new Array(playersTerrainSlot).Join(", ")

Maybe it needs some extra parentheses to work.

No errors now, testing and will post.

OK … currentScore and playersTerrainSlot are, indeed, out of sync. Oddly enough, the difference is usually either 3 or 4 depending (i.e., playersterrainSlot = 10, but their currentScore will show as either 13 or 14). Usually. Other times, it is correct. And I can see no pattern to this.

But, playersTerrainSlot is always correct. So all I think I need to do to hack this together for v1.0 (before my re-write of v1.1 into objects) is to use the variable playersTerrainSlot to determine winner at the end of the game. Highest one(s) win and/or tie.

I will let you know how this all goes and I want to thank the Unity community once again for it’s generous help.

WORKING!!!

I’VE USED JASPER’S ARRAY AND MADE ONE SMALL TWEAK …

function WhoIsWinning() : Array {
	var winners : Array = new Array();
	var highest = -1;
	for(var i = 0; i < GameController.instance.numberOfPlayers; i++) {
		var score = [B]playersTerrainSlot[/B][i];
		if(score >= 10) {
			if(score > highest) {
				highest = score;
				winners.Clear();
				winners.Push(i);
			}
			else if(score == highest) {
				winners.Push(i);
			}
		}
	}
	return winners;
}

and that makes it all work.

The odd thing is that I can find no consistent reason why the currentScore gets out of whack; and i’t not only 3 or 4, I’ve now seen cases of 1 or not at all. It’s not number of turns a player taken added to their real currentScore, or any other variable I can think of and the results are never consistent.

Many thanks to everyone who helped out.