Question

I have a logic situation that is best described as two "Teams" trying to win a task. The outcome of this task could be a single winner, a tie (draw), or no winner (stalemate).

Currently, I'm using a nested if/else statement like so:

// using PHP, but the concept seems language agnostic.
if ($team_a->win()) {
    if ($team_b->win()) {
        //  this is a draw
    } else {
        //  team_a is the winner
    }
} else {
    if ($team_b->win()) { 
        //  team_b is the winner
    } else {
        //  This is a stalemate, no winner.
    }
}

This seems rather spaghetti-like and repetitive. Is there a more logical, DRY pattern I could use?

Was it helpful?

Solution

Another way would be if win(a) && win(b) then Draw, else if win(a), else if win(b).

Or:

if win(a) and win(b) then
   // Draw
else if win(a) then
   // a wins
else if win(b) then
   // b wins
else 
   // Stalemate

OTHER TIPS

I don't think it can be done much better than what you currently are doing.

One alternative is to use a switch expression:

switch (($team_a->win() << 1) + $team_b->win()) {
case 3:
    //  this is a draw
case 2:
    //  team_a is the winner
case 1:
    //  team_b is the winner
case 0:
    //  This is a stalemate, no winner.
}

However while it is more DRY, I don't think this improves readability. Note that in some languages instead of $team_x->win() you need to write ($team_x->win() ? 1 : 0).

if($TeamA->win() && $TeamB->win()){
  // Tie
}else if($TeamA->win()){
  // Team A wins
}else if($TeamB->win()){
  // Team B wins
}else{
  // No winner
}

Also, depending on what your win() method does, it may be more efficient to check it once outside the if...else so it only runs the check once:

$team_a = $TeamA->win();
$team_b = $TeamB->win();

if($team_a && $team_b){
  // Tie
}else if($team_a){
  // Team A wins
}else if($team_b){
  // Team B wins
}else{
  // No winner
}

as to simplify your nested statements:

if($team_a->win() and $team_b->win())
{
  // Draw
}
elseif($team_a->win())
}
  // Team A Won
}
elseif($team_b->win())
}
  // Team B Won
}
else
{
  // No Winner
}

Here is an expanded version of @Mark Byers version, aimed to make the logics behind it clearer.

$result_code = 0;
if ($team_a->win()) $result_code += 1;
if ($team_b->win()) $result_code += 2;
switch ($result_code) {
   case 0:
      //stalemate
   case 1:
      //a wins
   case 2:
      //b wins
   case 3:
      //draw
}

It's a very nice procedure, this one of numbers that don't add up to the same result in any combination, and I think you'll have lots of occasions to use it in the future. It's the same logic used in *nix permissions, and can work with < 2 operands too: if they were three you could use 4, 2 and 1 and so on.

In this particular case, it speeds up the processing as well, since you only call the team objects' methods once.

When this type of logic is encapsulated in a method, early returns can be more readable, than if {} else blocks:

function game_end($a,$b) {
    if ($team_a->win() && $team_b->win()) {
            //  this is a draw
            return;
    } 

    if ($team_a->win()) {
            //  team_a is the winner
            return ;
    }

    if ($team_b->win()) {
            //  team_b is the winner
            return ;
    }

    //  This is a stalemate, no winner.

}

Can't you use a more direct statement...

something like

IF(a AND b) THEN {a,b}
ELSE If (a AND NOT(b)) THEN {a}
ELSE If (not(a) AND NOT(b)) THEN { }
ELSE {b}

in contrast to some other posts this is very readable but not the most succinct.

Like I said in a comment, I think the win condition and the determining thereof is in the wrong place. What's wrong with (example quick-e-code):

if ($team_a->score() > $team_b->score()) return 'Team A wins!';
else if ($team_a->score() < $team_b->score()) return 'Team B wins!';
else return 'It's a tie!';

(syntax may be wrong, but you get the idea)

It's better to refactor than to try to think of a solution to a problem you created yourself.

If you're going to split into two groups of two, consider:

if (win(a) != win(b)) {
    // common code when there's a winner, regardless of who
    if (win(a)) {
        // code specific to a winning
    } else {
        // code specific to b winning
    }
} else {
    // common code for a non-result
    if (win(a)) {
        // code specific to a draw
    } else {
        // code specific to a stalemate
    }
}

Could be useful if you're in the sweet spot where there's some common code in one of the two positions, but it's not worthwhile, or not possible, to abstract into a function and call it from two of the four blocks in the if/else if and switch/case answers.

The question of looking for a DRY pattern is wrong here as the problem is neither complex enough to warrant refactoring/simplification, and its cyclomatic complexity is too low to worry about spaghettization.

This is an algorithmic problem, a boolean algebra to be precise... and a very simple one to boot. You must do 3 comparisons and 4 branches at least to make a decision.

IF (A ^ B) -> draw
ELSE (!A ^ !B) -> no win
ELSE A -> a wins
ELSE -> b wins

It's not possible to do the logic with anything less. The only redundancy here is the recomputation of $team_b->win() (simply compute the win for both teams and put them in local vars before the if statement).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top