forked from aigamedev/resistance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
player.py
209 lines (166 loc) · 7.84 KB
/
player.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import logging
import logging.handlers
import core
class Player(object):
"""A player in the game of resistance, identified by a unique index as the
position at the table (random), and a name that identifies this player
across multiple games (constant).
When you build a Bot for resistance, you'll be given lists of players to
manipulate in the form of instances of this Player class. You can use
it as follows:
for player in players:
print player.name, player.index
NOTE: You can ignore the implementation of this class and simply skip to
the details of the Bot class below if you want to write your own AI.
"""
def __init__(self, name, index):
# Setup the two member variables first, then continue...
self.name = name
self.index = index
# This line is necessary for bots using mods as mix-in classes.
super(Player, self).__init__()
def __repr__(self):
return "%i-%s" % (self.index, self.name)
def __eq__(self, other):
return self.index == other.index and self.name == other.name
def __ne__(self, other):
return self.index != other.index or self.name != other.name
def __hash__(self):
return hash(self.index) ^ hash(self.name)
class Bot(Player):
"""This is the base class for your AI in THE RESISTANCE. To get started:
1) Derive this class from a new file that will contain your AI. See
bots.py for simple stock AI examples.
2) Implement mandatory API functions below; you must re-implement
those that raise exceptions (i.e. vote, select, sabotage).
3) If you need any of the optional callback API functions, implement
them (i.e. all functions named on*() are callbacks).
Aside from parameters passed as arguments to the functions below, you
can also access the game state via the self.game variable, which contains
a State class defined in game.py.
For debugging, it's recommended you use the self.log variable, which
contains a python logging object on which you can call .info() .debug()
or warn() for instance. The output is stored in a file in the #/logs/
folder, named according to your bot.
"""
__metaclass__ = core.Observable
def onGameRevealed(self, players, spies):
"""This function will be called to list all the players, and if you're
a spy, the spies too -- including others and yourself.
@param players List of all players in the game including you.
@param spies List of players that are spies, or an empty list.
"""
pass
def onMissionAttempt(self, mission, tries, leader):
"""Callback function when a new turn begins, before the
players are selected.
@param mission Integer representing the mission number (1..5).
@param tries Integer count for its number of tries (1..5).
@param leader A Player representing who's in charge.
"""
pass
def select(self, players, count):
"""Pick a sub-group of players to go on the next mission.
@param players The list of all players in the game to pick from.
@param count The number of players you must now select.
@return list The players selected for the upcoming mission.
"""
raise NotImplemented
def onTeamSelected(self, leader, team):
"""Called immediately after the team is selected to go on a mission,
and before the voting happens.
@param leader The leader in charge for this mission.
@param team The team that was selected by the current leader.
"""
pass
def vote(self, team):
"""Given a selected team, decide whether the mission should proceed.
@param team List of players with index and name.
@return bool Answer Yes/No.
"""
raise NotImplemented
def onVoteComplete(self, votes):
"""Callback once the whole team has voted.
@param votes Boolean votes for each player (ordered).
"""
pass
def sabotage(self):
"""Decide what to do on the mission once it has been approved. This
function is only called if you're a spy, otherwise you have no choice.
@return bool Yes to shoot down a mission.
"""
raise NotImplemented
def onMissionComplete(self, sabotaged):
"""Callback once the players have been chosen.
@param selected List of players that participated in the mission.
@param sabotaged Integer how many times the mission was sabotaged.
"""
pass
def onMissionFailed(self, leader, team):
"""Callback once a vote did not reach majority, failing the mission.
@param leader The player responsible for selection.
@param team The list of players chosen for the mission.
"""
pass
def announce(self):
"""Publicly state beliefs about the game's state by announcing spy
probabilities for any combination of players in the game. This is
done after each mission completes, and takes the form of a mapping from
player to float. Not all players must be specified, and of course this
can be innacurate!
@return dict[Player, float] Mapping of player to spy probability.
"""
return {}
def onAnnouncement(self, source, announcement):
"""Callback if another player decides to announce beliefs about the
game. This is passed as a potentially incomplete mapping from player
to spy probability.
@param source Player making the announcement.
@param announcement Dictionnary mapping players to spy probabilities.
"""
pass
def say(self, message):
"""Helper function to print a message in the global game chat, visible
by all the other players.
@param message String containing free-form text.
"""
self.log.info(message)
def onMessage(self, source, message):
"""Callback if another player sends a general free-form message to the
channel. This is passed in as a generic string that needs to be parsed.
@param source Player sending the message.
@param announcement Arbitrary string for the message sent.
"""
pass
def onGameComplete(self, win, spies):
"""Callback once the game is complete, and everything is revealed.
@param win Boolean true if the Resistance won.
@param spies List of only the spies in the game.
"""
pass
def others(self):
"""Helper function to list players in the game that are not your bot."""
return [p for p in self.game.players if p != self]
def __init__(self, game, index, spy):
"""Constructor called before a game starts. It's recommended you don't
override this function and instead use onGameRevealed() to perform
setup for your AI.
@param name The public name of your bot.
@param index Your own index in the player list.
@param spy Are you supposed to play as a spy?
"""
super(Bot, self).__init__(self.__class__.__name__, index)
self.game = game
self.spy = spy
self.log = logging.getLogger(self.name)
if not self.log.handlers:
try:
output = logging.FileHandler(filename='logs/'+self.name+'.log')
self.log.addHandler(output)
self.log.setLevel(logging.DEBUG)
except IOError:
pass
def __repr__(self):
"""Built-in function to support pretty-printing."""
type = {True: "SPY", False: "RST"}
return "<%s #%i %s>" % (self.name, self.index, type[self.spy])