Logically determine game outcome with formula

后端 未结 4 1596
终归单人心
终归单人心 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.
    

提交回复
热议问题