'How to reduce the amount of lines needed for this logic?

I have a simple Python game where two players deal some damage to each other in turns until one player has 0 or less than 0 HP. I don't like how much I've repeated myself but I'm unsure how to fix this. I thought about using a list of players but then I'm unsure how to index the next player when it's their turn, without running into the list index out of range error.

If this kind of question is not allowed please let me know and I will remove it.

Below is my game logic to determine when a player has won. I may not have pasted in the exact code I have but it runs how it's expected locally.

def check_win_state(current_player):
  if current_player.get_player_health() > 0:
    return True
  elif current_player.get_player_health() <= 0:
    return False


def main():
  player1 = player.Player("Me")
  player2 = player.Player("You")

  while True:
    if check_win_state(player1):
      take_turn(player1, player2)
    else:
      print(f"\n{player2.get_player_name()} ({player2.get_player_health()} HP) wins! {player1.get_player_name()} has {player1.get_player_health()} HP left.\n")
      break
        
    if check_win_state(player2):
      take_turn(player2, player1)
    else:
      print(f"\n{player1.get_player_name()} ({player1.get_player_health()} HP) wins! {player2.get_player_name()} has {player2.get_player_health()} HP left.\n")
      break


Solution 1:[1]

The easiest approach to reduce code duplication in situations like these is to use a secondary variable to hold the primary variable. Here, instead of having different code for player1 and player2, we instead have just one code, and use the variables current_player and opposing_player to hold player1 and player2, and swap with every iteration.

def main():
  player1 = player.Player("Me")
  player2 = player.Player("You")

  current_player = player1
  opposing_player = player2

  while True:
    if check_win_state(current_player):
      take_turn(current_player, opposing_player)
    else:
      print(f"\n{opposing_player.get_player_name()} ({opposing_player.get_player_health()} HP) wins! {current_player.get_player_name()} has {current_player.get_player_health()} HP left.\n")
      break
    current_player, opposing_player = opposing_player, current_player

If you have more than two players, then a more extensible approach might be to have a list of players, and have an index rotate through the list with each iteration to specify which player is the 'current' one.


also, you can simplify check_win_state():

def check_win_state(current_player):
    return current_player.get_player_health() > 0

because the check a > b returns a boolean anyway.

Solution 2:[2]

You can make the first function short using this logic:-

def check_win_state(current_player):
  return current_player.get_player_health() > 0:

And now you can create another function to do the work of inside while loop

def work_in_while(pl, another_pl):
  
  if check_win_state(pl):
    take_turn(pl, another_pl)
  else:  
     print(f"\n{another_pl.get_player_name()} ({another_pl.get_player_health()} HP) wins! {pl.get_player_name()} has {pl.get_player_health()} HP left.\n")
     return "kill"

And use it like:-

while True:
  if work_in_while(player1, player2) == "kill":
     break
  if work_in_while(player2, player1) == "kill":
     break

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Green Cloak Guy
Solution 2 I-am-developer-9