How can I optimize my code: multiple boolean checks

I’ve got a level select screen where the player can choose between 1 of the 7 levels of my game.
If he finishes a level a boolean (LEVELxFINISHED = true;) gets set and a check mark is drawn over the level in the level select screen and the player is only able to choose from the remaining levels.

If he finishes ALL level, for now I want all boolean for if the leve is finished to be reset.

I’m doing this with this code.

if (LEVEL1FINISHED && LEVEL2FINISHED &&
				LEVEL3FINISHED && LEVEL4FINISHED &&
				LEVEL5FINISHED && LEVEL6FINISHED &&
				LEVEL7FINISHED ) {resetLevels();}

I was wondering is there is a way to automate/optimize this code.
It works fine, but imagine i would have had like 40 levels…
Would be nice to know how to do this check with some kind of for loop.

Keep a counter?

if (levelsFinished == 7) {
    reset();
}

O yes, forgot about that, that’s a good way for sure.

But then I get to my resetLevels method:


	public void resetLevels() {
		LEVEL1FINISHED = false;
		LEVEL2FINISHED = false;
		LEVEL3FINISHED = false;
		LEVEL4FINISHED = false;
		LEVEL5FINISHED = false;
		LEVEL6FINISHED = false;
		LEVEL7FINISHED = false;
	}

And I’m still curious if it is possible to do this more efficiënt for say if it where 40 levels.

levelsFinished = 0;

Tip: If you ever have lots of bits of code that do more or less the same thing, that’s not good.

Well… levelsFinished =0; wouldnt suffice i think

Let me explain.

So i have a menuselect Screen where I draw all 7 levels as a pictogram on screen and the player can select one.
Once a level is finished the screen switches back to the menuSelect screen and depending on which LEVELFINISHED boolean is set to true, it draws a checkmark over the level Pictogram, so the player:

  1. sees which level(s) he has finished
  2. can’t select the level anymore.

The levels don’t need to be selected in chronological order.
So if the player wants to play lvl3 first and then 1 and after 6, etc. He/She can.

You say that

[quote]If you ever have lots of bits of code that do more or less the same thing, that’s not good.
[/quote]
which sound very plausible, but in this case i don’t see what else i can do to set a bit to true or false in order to check for checkmark-draw and whether or not the level can be played

Maybe create a separate object to track the number of completed levels, something like the following?


public class LevelTracker {
    private final boolean[] completed = new boolean[ NUMBER_OF_LEVELS ];

    public void completeLevel( int level ) {
        completed[ level ] = true;
    }

    public boolean isComplete( int level ) {
        return completed[ level ];
    }

    public int countCompletedLevels() {
        int count = 0;
        for( boolean b : completed ) {
            if( b ) ++count;
        }
        return count;
    }

    public void reset() {
        Arrays.fill( completed, false );
    }
}

No need for separate constants for each level.

Interesting!

That would work. I wondered if levels could be played non-sequentially.

Yip, thought from a standpoint of trying to optimize the time a player stays interested in your game, would be to give them more options in which level they want to play in stead of starting with the same levels over and over.

If you had more statuses than completed y/n, you could use an enum.


public enum LevelState {
   NOT_TRIED, ATTEMPTED, COMPLETED, COMPLETED_PERFECT_SCORE;
}

Then you could use an array of these enums to capture more nuanced state.

You could also just have an array of booleans.

boolean[] levelsFinished = new boolean[7];

And then to check if all levels are completed, just do something like this:


boolean allFinished = true;
for(boolean b : levelsFinished){
   if(!b){
      allFinished = false;
      break; //no need to check the rest
   }
}
if(allFinished){
   resetLevels();
}

Whoops, I just realized this is what StrideColossus is saying. But the point is that whenever you find yourself using a bunch of variables that mean almost the same thing, it’s time for an array or a new class.

I recommend using an enum for the levels (lets make that Levels) and EnumSet for the tracking.

EnumSet completedLevels = EnumSet.noneOf(Levels.class);

Just like all the other collections there are many familiar operations:

completedLevels.size() gives the count.

completedLevels.add(Levels.LEVEL_1);

You can get an iterator for enums in the set.

You can get a complement set via:

EnumSet unfinishedLevels = EnumSet.complementOf(completedLevels);

And lots more without having to write any custom tracking code like other solutions in this thread.

You can even create a EnumSet for special conditions and easily check against completedLevels:

EnumSet mediumDifficultyLevels = EnumSet.range(Levels.LEVEL_10, Levels.LEVEL_20);

if (completedLevels.containsAll(mediumDifficultlyLevels))
{
// etc etc etc.
}