排行榜
154 Calculator
144 Taxman
138 Statistician
137 Solver
137 RandoAggroLawyer
136 Gambler
134 Turncoat
119 Lawyer
119 BloodyMurder
113 Bandit
79 Challenger
74 Mask
64 Random
Brilliand的计算器是赢家!他的回答已被接受,但这并不意味着挑战已经结束。随时提交新条目或编辑您当前的条目,并尝试将他从王位上撤下。我将在本月底向领导奖赏。
比赛规则
Coup是一款专为2-6位玩家设计的纸牌游戏,我们将与两名玩家一起玩。它由一个金库(对于我们而言是无限的)和一副15张卡片组组成,每张卡片包含以下3种类型:大使,刺客,上尉,孔戴萨,杜克。游戏开始时,每位玩家将获得一枚硬币,并随机发两张牌,直到必要时,他们都会对其保密。目标是成为手中拿着牌的最后一个玩家。
在轮到自己时,玩家可以不考虑其牌而采取以下操作之一:
- 收入:从金库中取出1个硬币。不可阻挡且不可挑战。
- 外国援助:从金库中取出2个硬币。可以被玩家与杜克封锁。勇往直前。
- 政变:从游戏中移除您选择的一个对手的卡牌。花费7个硬币。受害人可以选择丢弃哪张卡。如果玩家在回合开始时拥有10个或更多硬币,则他们必须发动政变。不可阻挡且不可挑战。
根据自己的牌,玩家还可以在回合时采取以下其中一项操作:
- 交换:带大使的玩家可以从套牌中拿出两张牌。然后,他们可以从手牌和抽牌中选择与原来数量一样多的牌。(也就是说,如果他们只有一张卡,则可以将其交换为一张已抽出的卡或保留它;如果他们有两张卡,则可以选择四张卡中的任意两张。)两张不需要的卡将退回到卡组中。不可阻挡,但具有挑战性。
- 暗杀:具有刺客的玩家可以花费3个硬币从游戏中删除对手的卡牌。受害人可以选择丢弃哪张卡。可以由一名持孔戴莎游戏的玩家阻止,在这种情况下,不会退还硬币。具有挑战性,在这种情况下,硬币将被退回。
- 偷:拥有队长的玩家可以从对手那里拿走两枚硬币。如果对手拥有一枚硬币,他们将拿走一枚硬币。如果对手的硬币为零,则他们可能不会偷。可能会被一名带大使或队长的球员阻挡。具有挑战性。
- 税:拥有杜克大学的球员可以从国库中取出3个硬币。不可阻挡,但具有挑战性。
政变的棘手部分是允许玩家撒谎他们所拥有的卡!不需要卡就可以尝试执行与之相关的操作或阻止。
当玩家执行纸牌动作时,任何对手(甚至没有受到该动作伤害的对手)都可以挑战演员并说他们不相信自己拥有该纸牌。如果挑战者是正确的,则该动作将被取消,并且参与者必须丢弃他们选择的一张卡(如果适用,则收回他们花费的所有硬币)。如果不是,则采取该动作,演员将被挑战的卡退还给甲板并抽出一张新卡,挑战者必须丢弃其中一张卡。玩家必须诚实面对挑战时所持的牌。
从暗杀,政变和失败的挑战中被淘汰的卡牌不会返回到副牌组,但作为获胜挑战的一部分而显示的卡牌会被返回到副牌组。
障碍可能像行动一样受到挑战。例如,如果玩家A要求外国援助,而玩家B说“我有杜克大学,而我阻止了您的外国援助”,则A可能会说“我不相信您有杜克大学”。如果这个说法是正确的,则B因被骗而丢了一张卡,A拿了2个硬币;如果不是这样,则A会丢失一张牌,并且不会获得硬币,B则必须将其公爵归还给甲板并抽取一张新卡。
必须充实与刺客合作的障碍和挑战方式。假设玩家A说“我有一个刺客,而我暗杀了玩家B”。如果B不尝试挑战或阻止A,那么暗杀将继续进行:B丢了一张牌,A支付了3个硬币。
另外,B可以通过说“我不相信你有刺客”来挑战。如果是这样,则A丢弃一张卡并返回其硬币,而B不受影响,并且A的回合结束。如果B的信念是错误的,并且A持有刺客,那么B会丢掉他们的所有牌并失败,一张是针对不正确的挑战,另一张是来自暗杀。
B不用挑战,而可以说“我有一个伯爵夫人,我阻止了暗杀者”。如果A相信B,那么A的回合结束并且他们的硬币不退还。但是A可以挑战障碍并说“我不相信您有伯爵夫人”。如果B实际上确实持有Contessa,则A会因错误的挑战而丢掉一张牌。但是,如果B不这样做,则B会因为被撒谎而丢掉一张卡,而从暗杀中又损失一张。
与上述解释类似的逻辑也适用于机长的偷窃能力,在这种情况下可以挑战动作或障碍。
如果您没有成功地暗杀一名暗杀者,或者被误认为声称自己有一名孔戴莎来阻止暗杀,则有可能丢掉您的两张牌并在一个回合中被淘汰。您从挑战中丢失了一张卡,而在暗杀中丢失了一张卡。
挑战
您的任务是编写一个播放政变的程序。它将作为其命令行参数给出:
- 包含到目前为止文件及其对手行为列表的文件名。
- 从0到12的整数,指示对手的硬币数。
- 从0到12的整数,表示其硬币计数。
- 一到四个字符的字符串,表示其卡。通常,这只是您的程序拥有的一两张卡,但是如果您的程序刚在交易所成功,则它将为n + 2个字符长,其中n是您剩余的卡数。然后,您的程序必须将希望保留的n张卡输出到STDOUT。(除此目的外,程序不得读取或访问STDOUT -如果您希望产生调试输出,请写信给STDERR。)
- 一个或多个论据表明它可能采取的法律行动。
(示例调用:yourprogram file.txt 1 7 '~!' a c p q
,意思是“您的对手有1个硬币。您有7个硬币,1个大使和1个Contessa。根据游戏历史和当前游戏状态,将a,c,p或q的选择写入file.txt。 ”)
您的程序必须在提供的文件中附加一个或(在两种特定情况下)两个字符,以指示其动作。它不得以其他方式更改文件的现有内容。它可以创建希望的任何新文件,但只能在运行它的目录中。请提供所有必要的命令来编译和运行程序。
我在下面提供了两个用Go语言编写的示例竞争对手。
输出格式为:
I\n
:收入。法律回应:任何回合行动(假设有人拥有“暗杀/政变”硬币)。F
:外援。法律回应:(d
以杜克身份阻止),p
(让其通过)。C
:妙招。法律对策:取的_
,'
,<
,=
,0
是在你的手。E
:交流。法律对策:q
(挑战,不相信球员有一个大使)p
。T
:税。法律对策:q
(挑战,不相信玩家拥有杜克大学)p
。A
:暗杀 法律对策:s
(块作为伯爵夫人),q
(挑战),并取的_
,'
,<
,=
,0
是在你的手。S
:偷。法律对策:a
(块作为大使),c
(块作为队长),q
(挑战,不相信球员有一个队长)p
。d
:阻止外国援助作为公爵。法律回应:(\n
接受封锁),q
(挑战,不相信玩家拥有公爵)。a
:阻止窃取为大使。法律回应:(\n
接受封锁),q
(挑战,不相信玩家有大使)。c
:阻止作为队长的抢劫。\n
(接受方块),q
(挑战,不相信玩家拥有队长)。s
:阻止一名暗杀者担任伯爵夫人 法律回应:(\n
接受封锁),q
(挑战,不相信玩家拥有《伯爵夫人》。)p
:在轮到您时通过挑战交换/税收/偷窃。不用于A
; 拒绝向暗杀挑战_'<=0
。法律响应:(\n
结束您的回合),如果您刚刚在交易所成功,请将您希望保留的卡从第四个命令行参数写入STDOUT。q
:挑战最近的动作或封锁。法律回应:如果您拥有提出异议的证件,无论哪种方式~^*!$
。如果不这样做,那么无论_'<=0
您希望放弃哪一个,都必须放弃,然后在且仅当该轮到您时,再换行。~
,^
,*
,!
,$
:表明你讲述在Exchange控股,分别同比大使,刺客,一个队长,一个伯爵夫人和公爵(也用来表示在命令行参数,这些卡和STDOUT输出的真相)。法律对策:取其_
,'
,<
,=
,0
你有你的手。_
,'
,<
,=
,0
:分别放弃作为惩罚,大使,和刺客,一个队长,一个伯爵夫人和公爵,因为你失去了一个挑战,或者被暗杀/ Couped。法律回应:\n
。\n
:结束回合,如果可能的话,拒绝挑战一个障碍。法律回应:任何大写字母的动作(假设一个人拥有用于暗杀/政变的硬币,而对手则拥有用于窃取的硬币)。
该格式具有以下有用的属性:
- 转弯以大写字母开头。
- 行遵循以下模式:大写字母,小写字母,可选的标点符号或显示卡的0,换行符。
- 以换行符结尾的文件或空文件表示这是程序开始的时刻,必须选择大写字母动作。
- 允许您进行调用的法律行动通常由文件中的最后一个字符唯一地确定。例外是
q
,它将有一些逻辑关联。请参阅get_legal_actions
仲裁器中的函数以帮助理解这一点。或者,您可以只使用在命令行上给出的法律行动。 - 一行上偶数个字符表示该回合是您的,并且您的程序被要求选择一个动作,挑战一个障碍或结束其回合。
- 一行上的字符个数奇数表示转弯不属于您,并且您的程序被要求阻止,挑战或显示/交出卡。
我将举一个例子说明每一个动作。
I\n
是最容易理解的。一个程序获取一个收入硬币,然后结束轮回。这是程序必须打印两个字符的两种情况之一,因为“收入”是对手既不受影响又不能阻止或挑战的唯一动作。
Fp\n
表示某个程序接受了“外国援助”,那么其对手拒绝屏蔽(p
)。在下一次调用时,第一个程序注意到通过最后p
一行小写字母和/或该行上的偶数个字符,它完成了此回合,该回合尚未结束,因此它知道通过打印换行符来结束当前回合。
C=\n
意味着一个程序发起了政变。它的对手知道行号中的奇数个字母被叫作反应,因此放弃了《伯爵夫人》。同样,第一个程序知道这是它的下一次调用不完整,因为行中的字符数是偶数,所以它写了一个换行符以结束该行。
Eq~<\n
表示某个程序尝试进行Exchange(E
),而其对手则挑战(q
)。交换程序显示它确实有一位大使(~
),而挑战者放弃了一名船长作为惩罚(<
)。挑战者退出后,将以四个字符的字符串作为其第四命令行参数(如果只有一张卡,则为三个字符)再次调用交换程序。它将代表希望保留的卡的字符写入STDOUT,并将换行符写入文件。
Tq'\n
意味着一个程序企图进行不真实的税收,遭到挑战,并放弃了刺客。它说明了写两个字符的另一种情况:如果轮到您并且您被迫放弃一张牌–是由于对手的正确挑战(如此处)或由于您不正确的积木挑战–那么您必须同时写两个您放弃的卡片和换行符以结束您的回合。
Asq!'\n
则意味着玩家B试图暗杀玩家A(A
),但A声称有一名Contessa对其进行了封锁(s
)。B不相信A并向(q
)提出挑战。A透露他们确实有Contessa(!
)。B放弃了刺客的惩罚,丢掉了硬币,结束了回合('\n
),在这种特殊情况下写了两个字符。(如果A决定不阻止或挑战,它可能会写=
,然后其对手会看到转弯结束并写了换行符。然后A=\n
,该行会读起来,就像Coup示例一样。)
Sq*0\n
表示有一个程序尝试窃取;对手挑战,不相信小偷有队长;并且原始程序显示了一名船长,因此挑战未成功,挑战者放弃了杜克作为惩罚。(其对手的另一种选择是通过书写来接受窃取p
。其对手然后将检测其回合的结束并进行书写\n
,结果是一行Sp\n
。)
仲裁人
程序将通过此Python脚本调用。它进行十轮比赛,每个选手在第一和第二名的同时都面对其他选手。它跟踪卡和硬币计数,并通过第一个程序确定输家,以两次结束带有标点符号的行。以非零状态退出,修改文件,对文件进行非法移动或尝试进行非法Exchange的程序将自动丧失。如果每个玩家在没有获胜者的情况下执行100多个动作(包括阻止和挑战),那么这两个程序都会失败。获胜者获得1分。程序得分最高的玩家获胜。
我建议您阅读仲裁器的源代码,尤其是该get_legal_actions
函数。它可以帮助您了解规范并编写自己的程序。
import itertools
import os
import random
import subprocess
class Player:
def __init__(self, name, command):
self.name = name
self.command = command
self.score = 0
self.coins = 1
self.cards = ""
actions_dict = {
'E': '_', 'T': '0', 'A': "'", 'S': '<',
'd': '0', 'a': '_', 'c': '<', 's': '='
}
punishment_to_reveal = {'_': '~', "'": '^', '<': '*', '=': '!', '0': '$'}
reveal_to_punishment = {
punishment_to_reveal[k]: k for k in punishment_to_reveal
}
def get_legal_actions(history, player, opponent):
c = history[-1]
result = ""
# Our turn begins; choose an action.
if c == '\n':
if player.coins >= 10:
return ["C"]
ret = ['I\n'] + list("FET")
if player.coins >= 3:
ret.append("A")
if player.coins >= 7:
ret.append('C')
if opponent.coins > 0:
ret.append("S")
return ret
# Opponent attempted foreign aid; can pass or claim Duke to block.
elif c == 'F':
return list('dp')
# We have been Couped; must surrender a card.
elif c == 'C':
return player.cards
# We failed a challenge; must surrender a card and print a newline
# if it is our turn.
elif c in '~^*!$':
if history[-3] in 'acds':
return [card + '\n' for card in player.cards]
return player.cards
# Opponent attempted Exchange or Tax; can pass or challenge.
elif c == 'E' or c == 'T':
return list('pq')
# Opponent attempted an Assassination; can block, challenge, or give in.
elif c == 'A':
return list('sq') + player.cards
# Opponent attempted to Steal; can pass, block as Ambassador/Captain,
# or challenge.
elif c == 'S':
return list('acpq')
# Opponent blocked; can challenge or withdraw.
elif c in 'acds':
return list('q\n')
# Opponent passed on blocking Foreign Aid/Tax/Exchange or they gave up a
# card as punishment, must end turn.
elif c in "p_'<=0":
return ['\n']
# Opponent challenged us.
elif c == 'q':
challenged_action = history[-2]
# If we have the card they challenged us over, must reveal it.
necessary_card = actions_dict[challenged_action]
if necessary_card in player.cards:
return [punishment_to_reveal[necessary_card]]
# Otherwise, we can give up either of our cards, writing a newline
# if it is our turn.
if challenged_action in 'acds':
return list(player.cards)
else:
return [card + '\n' for card in player.cards]
else:
return None
deck = ['_', "'", '<', '=', '0'] * 3
random.shuffle(deck)
def determine_turn_effects(line, output, cards, current_player, opponent):
last_action = line[-2]
# Only operate if the opponent declined to challenge (p) or the
# program successfully challenged their block
if last_action in "p_'<=0":
primary_action = line[0]
# Foreign Aid
if primary_action == 'F':
print current_player.name, "received 2 coins of Foreign Aid"
current_player.coins += 2
# Tax
elif primary_action == 'T':
print current_player.name, "received 3 coins of Tax"
current_player.coins += 3
# Steal
elif primary_action == 'S':
stolen_coins = 1 if opponent.coins == 1 else 2
print current_player.name,\
"stole %d coins from %s" % (stolen_coins, opponent.name)
current_player.coins += stolen_coins
opponent.coins -= stolen_coins
# Exchange, store desired cards and replace undesired ones
elif primary_action == 'E':
print current_player.name, "tried to take %r" % output, "from", cards
legal_outputs = [''.join(p) for p in itertools.permutations(
cards, len(current_player.cards))]
if output not in legal_outputs:
print current_player.name, "forfeits by illegal exchange"
return opponent
current_player.cards = [
reveal_to_punishment[c] for c in output
]
undesired_cards = list(cards)
for c in output:
undesired_cards.remove(c)
for card in undesired_cards:
deck.append(reveal_to_punishment[card])
random.shuffle(deck)
# Coins are not returned from a successful Contessa block
elif last_action == 's':
print current_player.name, "lost 3 coins from a Contessa block"
current_player.coins -= 3
return None
def play_game(player1, player2, round_number, game_number):
outfilename = os.path.abspath(__file__)[:-len(__file__)] + '_'.join([
player1.name, player2.name, str(round_number), str(game_number)
]) + '.txt'
print outfilename
f = open(outfilename, 'w')
f.close()
players_list = [player1, player2]
player1.cards = [deck.pop(), deck.pop()]
player2.cards = [deck.pop(), deck.pop()]
current_player_index = 0
for i in range(200):
current_player = players_list[current_player_index]
opponent = players_list[(current_player_index+1) % 2]
legal_actions = []
original_contents = []
original_contents_joined = ""
with open(outfilename, 'r') as outfile:
original_contents = outfile.readlines()
original_contents_joined = ''.join(original_contents)
if len(original_contents) == 0:
legal_actions = ['I\n'] + list("FEST")
else:
legal_actions = get_legal_actions(
original_contents[-1], current_player, opponent)
if not legal_actions:
print "Error: file ended in invalid character"
return current_player
# Has the player completed an Exchange? Pass them new cards if so.
exchange_cards = ""
old_last_line = original_contents[-1] if len(original_contents) > 0 else '\n'
if old_last_line[-1] != '\n' and old_last_line[0] == 'E' and \
len(old_last_line) % 2 == 0 and old_last_line[-1] in "p_'<=0":
exchange_cards = punishment_to_reveal[deck.pop()] + \
punishment_to_reveal[deck.pop()]
cards = exchange_cards + ''.join(
punishment_to_reveal[card] for card in current_player.cards)
args = current_player.command + [
outfilename,
str(opponent.coins),
str(current_player.coins),
cards
] + legal_actions
print ' '.join(args)
output = ""
os.chdir(current_player.name)
try:
output = subprocess.check_output(args)
# Competitors that fail to execute must forfeit
except subprocess.CalledProcessError:
print current_player.name, "forfeits by non-zero exit status"
return opponent
finally:
os.chdir('..')
new_contents = []
new_contents_joined = ""
with open(outfilename, 'r') as outfile:
new_contents = outfile.readlines()
new_contents_joined = ''.join(new_contents)
if original_contents_joined != new_contents_joined[:-2] and \
original_contents_joined != new_contents_joined[:-1]:
print current_player.name, "forfeits by modifying the file"
print "old:", original_contents
print "new:", new_contents
return opponent
new_last_line = new_contents[-1]
the_move_made = ""
for action in legal_actions:
if new_last_line.endswith(action):
the_move_made = action
break
# Competitors that make an illegal move must forfeit
if not the_move_made:
print current_player.name, "forfeits with an illegal move,",\
"last line: %r" % new_last_line
print opponent.name, "wins!"
return opponent
print current_player.name, "played %r" % the_move_made
# Side effects of moves.
#
# Income, give the current player a coin.
if the_move_made == "I\n":
print current_player.name, "received 1 coin of income"
current_player.coins += 1
# The program surrendered a card on its turn; take it away.
elif len(the_move_made) == 2:
print current_player.name, "lost a card from being challenged"
current_player.cards.remove(the_move_made[0])
# Coins are not returned from a successful Contessa block
if new_last_line[-3] == '!':
print current_player.name, "lost 3 coins from a Contessa block"
current_player.coins -= 3
# The program surrendered a card when it was not its turn.
elif the_move_made in "_'<=0":
print current_player.name, "gave up a", the_move_made
current_player.cards.remove(the_move_made)
if new_last_line[0] == 'C':
opponent.coins -= 7
elif new_last_line[0] == 'A':
opponent.coins -= 3
# Did the program unsuccessfully challenge an Assassination
# (e.g. Aq^0\n)
# or get caught falsely blocking with a Contessa
# (e.g. Asq0\n)?
# If yes, it loses right away.
if new_last_line[0] == 'A' and new_last_line[1] in 'qs' and \
len(new_last_line) == 4:
print current_player.name, "lost both cards in the same turn."
print opponent.name, "wins!"
return opponent
elif the_move_made == 'S':
print current_player.name, "attempted Steal"
elif the_move_made == 'T':
print current_player.name, "attempted Tax"
elif the_move_made == 'A':
print current_player.name, "attempted Assassinate"
elif the_move_made == 'C':
print current_player.name, "launched a Coup"
elif the_move_made == 'F':
print current_player.name, "attempted Foreign Aid"
elif the_move_made == 'E':
print current_player.name, "attempted Exchange"
elif the_move_made == 'q':
print current_player.name, "challenged"
elif the_move_made == 'p':
print current_player.name, "passed"
elif the_move_made == 'a':
print current_player.name, "blocked with an Ambassador"
elif the_move_made == 'c':
print current_player.name, "blocked with a Captain"
elif the_move_made == 's':
print current_player.name, "blocked with a Contessa"
elif the_move_made == 'd':
print current_player.name, "blocked with a Duke"
# The program revealed a card from an opponent's unsuccessful challenge.
# Give it a new card.
# Special case: a program whose Exchange is unsuccessfully challenged
# may keep the Ambassador it revealed in the Exchange, so give a new
# card for a revealed Ambassador only if it was used to block a Steal.
elif the_move_made in '^*!$' or (the_move_made == '~' and
new_last_line[0] == 'S'):
p = reveal_to_punishment[the_move_made]
current_player.cards.remove(p)
current_player.cards.append(deck.pop())
deck.append(p)
random.shuffle(deck)
print current_player.name, "did have a", the_move_made
# The program ended its turn. We must examine the rest of the line to
# determine the side effects.
elif the_move_made == '\n':
potential_winner = determine_turn_effects(
new_last_line, output.strip(), cards, current_player,
opponent)
if potential_winner:
print potential_winner.name,\
"wins because their opponent made an illegal exchange!"
return potential_winner
# One player has lost all their cards. Victory for the opponent!
if current_player.cards == []:
print opponent.name, "wins by eliminating both opponent cards!"
return opponent
current_player_index += 1
current_player_index %= 2
return None
competitors = []
competitors.append(Player("Challenger", ["./challenger"]))
competitors.append(Player("Random", ["./random"]))
# ...More competitors here
for i in range(10):
print "-- Round", i
j = 0
for pairing in itertools.permutations(competitors, 2):
player1, player2 = pairing
print '--- Game', j, ':', player1.name, 'vs.', player2.name
winner = play_game(player1, player2, i, j)
if not winner:
j += 1
continue
winner.score += 1
player1.coins = 1
player1.cards = ""
player2.coins = 1
player2.cards = ""
deck = ['_', "'", '<', '=', '0'] * 3
random.shuffle(deck)
j += 1
competitors.sort(reverse=True, key=lambda player: player.score)
for player in competitors:
print '%5d %s' % (player.score, player.name)
杂
一个程序不能具有特定于另一程序的代码,并且程序不能互相帮助。(您可能有多个程序,但是它们无法以任何方式相互交互。)
如果您的程序在同一回合中丢失了其两张牌,则只需写入一张。仲裁器将检测到它已被消除。
程序可以(但不是必需)检查文件中游戏的历史记录,这是可能的,但不是必需的。通过这样做,他们可以确定对手声称拥有的牌,然后撒谎。
在Coup的真实游戏中,您可以挑战一个动作,然后尝试在同一回合中将其阻止。如果允许的话,我无法使规范生效,因此您可以挑战或阻止给定的操作,但不能同时挑战和阻止既定的操作。
我对@PeterTaylor表示歉意,在我上次发布此帖子时,我建议将其发布到沙箱中,并重新编写协议以在STDOUT / STDIN中来回传输输出。我尽了全力以赴,花了整整一天的时间(当我已经花了整整一天的时间写原始的挑战时)。但是事实证明,以这种方式实施交易所非常复杂,而且要求要求他们跟踪自己的硬币计数,这将增加提交的复杂性。因此,我已经或多或少张贴了最初的挑战。
S
,程序B通过编写阻塞c
,A拒绝通过编写挑战\n
。成功的抢劫挑战将是:A进行写作S
,B进行写作挑战q
,A 放弃进行写作挑战,例如_\n
,您每回合只能采取一项行动,包括交换。对Exchange的法律回应是通过和挑战。