Logically determine game outcome with formula

后端 未结 4 1564
终归单人心
终归单人心 2021-01-21 14:41

I am trying to become a better coder, which includes getting rid of my \'hard-coding\' habits to keep my programs dynamic and easy to maintain.

Right now I am writing a

相关标签:
4条回答
  • 2021-01-21 15:16

    What I initially thought about the Rock–Paper–Scissors (RPS) rules:

    • It's a cyclic relation between the elements where each element beats the one in before it (Scissors beats (cuts) Paper)
      • The 1st element (that doesn't have anything before) beats the last one (and the cycle is complete)
    • When adding more elements, it would only be to allow more users to play (as long as the user count is smaller by 1 than the element count), but now I see that it's wrong as there are cases when the outcome can be undefined (actually the only working case is when there are no 2 players that selected the same option)

    Apparently (thanks to [Wikipedia]: Rock–paper–scissors), for a balanced game (odd number of elements):

    • Each element beats half of the other ones (and as a consequence, loses to the other half)

      • The (1st) one before it
      • The 3rd one before it
      • The 5th one before it
      • ...
      • When reaching beginning of the list, jump to its end (wrap-around)

      This is a generalization of the 3 element (RPS) game (and also applies to RPSLS)

    Here's what the above rule looks like when put into code (I've also redesigned it to correct some errors in your snippet). All the "magic" happens in outcome.

    code00.py:

    #!/usr/bin/env python3
    
    import sys
    
    
    _elements_list = [
        ["Rock", "Paper", "Scissors"],
        ["Rock", "Paper", "Scissors", "Spock", "Lizard"],  # !!! The order is DIFFERENT (RPSSL) than the name of the game: RPSLS !!!
    ]
    
    elements_dict = {len(item): item for item in _elements_list}
    del _elements_list
    
    
    def get_users_choices(valid_options):
        ret = [-1] * 2
        for i in (0, 1):
            user_choice = None
            while user_choice not in valid_options:
                user_choice = input("Enter user {0:d} option (out of {1:}): ".format(i + 1, valid_options))
            ret[i] = valid_options.index(user_choice)
        return ret
    
    
    def outcome(idx0, idx1, count):  # Returns -1 when 1st player wins, 0 on draw and 1 when 2nd player wins
        if idx0 == idx1:
            return 0
        index_steps = [-i * 2 - 1 for i in range(count // 2)]  # Index steps (n // 2 items) from current index: {-1, -3, -5, ...} (negative values mean: before)
        idx0_beat_idxes = [(idx0 + i + count) % count for i in index_steps]  # Wrap around when reaching the beginning of the list
        if idx1 in idx0_beat_idxes:
            return -1
        return 1
    
    
    def main():
        element_count = 3  # Change it to 5 for RPSLS
        if element_count <= 2:
            raise ValueError("Can't play game")
        elements = elements_dict.get(element_count)
        if not elements:
            raise ValueError("Invalid option count")
        choices = get_users_choices(elements)
        res = outcome(*choices, element_count)
        if res == 0:
            print("'{0:s}' and '{1:s}' are DRAW.".format(elements[choices[0]], elements[choices[1]]))
        elif res < 0:
            print("'{0:s}' WINS over '{1:s}'.".format(elements[choices[0]], elements[choices[1]]))
        else:
            print("'{0:s}' LOSES to '{1:s}'.".format(elements[choices[0]], elements[choices[1]]))
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main()
        print("\nDone.")
    

    Output:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057491776]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Enter user 1 option (out of ['Rock', 'Paper', 'Scissors']): Rock
    Enter user 2 option (out of ['Rock', 'Paper', 'Scissors']): Scissors
    'Rock' WINS over 'Scissors'.
    
    Done.
    
    0 讨论(0)
  • 2021-01-21 15:20

    List is a good idea. In your case validoptions = ["rock", "paper", "scissors"] you can see, everything beats the only one before it (the "paper" beats the "rock", the "rock" beats the "scissors" and the "scissors" beats the "paper". So if you sort it that way, it is solvable with the use of the indexes only. If you want to increase the choices, you can, but tke care, only the odd numbers will provide fair game.

    In general if you make a list of options, with a length of length, then:

    if u1 == u2:
        #it is a draw
    elif u2input in validoptions[u1 - int((length-1)/2):u1]:
        #player1 has won
    else:
        #player2 has won
    
    0 讨论(0)
  • 2021-01-21 15:29

    This is really cool! So, I think I would use a dictionary to control what loses to what:

    dict_loss = dict()
    dict_loss['paper']='scissors'
    dict_loss['scissors']='rock'
    dict_loss['rock']='paper'
    

    Then the players make a choice and you just check if their choices fall into the dictionaries:

    player_1='paper'
    player_2='rock'
    
    if player_2 in dict_loss[player_1]:
        print("Player 2 Wins")
    else:
        if player_1 in dict_loss[player_2]:
            print("Player 1 Wins")
        else:
            print("DRAW")
    

    You can extend the dictionary with the new objects you get, I'm not sure how Pans, swords and riffles work, but you can do:

    dict_loss['paper']=['scissors', 'riffle']
    

    if paper loses to riffles, and so on...

    Hope this helps, if you have any "data structure" restrictions let me know and I will try to think of something different.

    0 讨论(0)
  • 2021-01-21 15:31

    As the rules are not clearly defined, it is not trivial to give a one-size-fits-all-solution. I would probably assume there is some cyclic definition if "win/lose", giving me modulo-calculus such as e.g.:

    winner = ["None", "Player 1", "Player 2"]
    win_index = (u1 - u2) % len(validoptions)
    print("Winner: " + winner[win_index])
    

    Perhaps it is also interesting to take a look at: https://en.wikipedia.org/wiki/Rock%E2%80%93paper%E2%80%93scissors#Additional_weapons.

    0 讨论(0)
提交回复
热议问题