用公式从逻辑上确定游戏结果

Logically determine game outcome with formula

我正在努力成为一名更好的编码员,其中包括摆脱我的 'hard-coding' 习惯,以保持我的程序动态且易于维护。

现在我正在写一个简单的剪刀石头布游戏作为练习:

import time

validoptions = ["rock", "paper", "scissors"]

u1input = input("User 1, do you want to choose rock, paper or scissors?").lower()
if not u1input in(validoptions):
    u1input = input("What the hell User 1, choose a valid option: rock, paper or scissors?").lower()
u2input = input("User 2, do you want to choose rock, paper or scissors?").lower()
if not u2input in(validoptions):
    u2input = input("What the hell User 2, choose a valid option: rock, paper or scissors?").lower()

u1 = validoptions.index(u1input)
u2 = validoptions.index(u2input)

if u1 - u2 == 0:
    result = "It's a draw! Both parties have proven to be of equal strength."


print("Stakes are high... The battle is on... Calculating losses...")
for i in range(1,4):
    time.sleep(1)
    print("...")

time.sleep(1)
print(result)

对于剪刀石头布这样的游戏,结果并不多(6 wins/losses 和 1 个平局结果,或 3^2 个单独结果)。我可以很容易地在我已经编码的平局结果旁边编码所有可能的结果。但是,如果游戏扩展了 3 个选项怎么办?说:Rock, Paper, Scissors, Pans, Swords, and Rifles:那将是 6^2 = 36 个结果!

随着此列表的扩展,潜在结果的数量变得越来越难以硬编码。我想使用公式或其他 'smart' 动态方法来确定游戏结果,其方式与 if u1 - u2 == 0: 行类似。

这有可能吗?

这真的很酷!所以,我想我会用字典来控制什么输给什么:

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

然后玩家做出选择,你只需要检查他们的选择是否在字典中:

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")

你可以用你得到的新对象来扩展字典,我不确定平底锅、剑和步枪是如何工作的,但你可以这样做:

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

如果纸输给浅滩,以此类推...

希望这对您有所帮助,如果您有任何 "data structure" 限制,请告诉我,我会尝试想一些不同的东西。

列表是个好主意。在你的情况下 validoptions = ["rock", "paper", "scissors"] 你可以看到,一切都击败了它之前的唯一一个("paper" 击败了 "rock""rock" 击败了 "scissors""scissors" 胜过 "paper"。所以如果你这样排序,它可以只使用索引来解决。如果你想增加选择,你可以,但要注意,只有奇数将提供公平的比赛。

一般来说,如果你做一个options的列表,长度为length,那么:

if u1 == u2:
    #it is a draw
elif u2input in validoptions[u1 - int((length-1)/2):u1]:
    #player1 has won
else:
    #player2 has won

由于规则没有明确定义,给出一个放之四海而皆准的解决方案并非易事。如果 "win/lose",我可能会假设有一些循环定义,给我模演算例如:

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

也许看看也很有趣:https://en.wikipedia.org/wiki/Rock%E2%80%93paper%E2%80%93scissors#Additional_weapons

我最初想到的Rock–Paper–S剪刀 (RPS) 规则:

  • 这是元素之间的循环关系,每个元素都击败了它之前的元素(Scissors beats (cuts) Paper
    • 第 1st 个元素(之前没有任何元素)击败最后一个元素(并且循环完成)
  • 添加更多的元素,只是为了让更多的用户玩(只要用户数少1 比元素计数),但现在我看到 它是错误的 因为在某些情况下结果可能是不确定的(实际上唯一有效的情况是没有 2 个玩家选择了相同的选项)

显然(感谢 [Wikipedia]: Rock–paper–scissors),对于 平衡 游戏(奇数个元素):

  • 每个元素都打败了其他一半(结果,输给了另一半)

    • 第(1st)个
    • 前三
    • 前5
    • ...
    • 到达列表开头时,跳转到结尾(环绕

    这是 3 元素 (RPS) 游戏的概括(也适用于 RPSLS

这是将上述规则放入代码后的样子(我还对其进行了重新设计以更正代码段中的一些错误)。所有的“魔法”都发生在 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.")

输出:

[cfati@CFATI-5510-0:e:\Work\Dev\Whosebug\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.