# Each player keeps a record of what happened in their game. This
# class holds that information and prints it in a nice way.
from misc import *
class Record:
def __init__(self):
self.records = [] # a list of day and night records for a player
def __repr__(self):
return self.records.__repr__()
def new_record(self, day):
if self.records != [] and self.records[-1].is_day() == day:
raise ValueError, \
'Tried to add two records of the same type in a row'
if day:
self.records.append(DayRecord())
else: # night
self.records.append(NightRecord())
# Get most recently added DayRecord
def today(self):
if self.records == []: return None
if self.records[-1].is_night():
raise ValueError, 'Tried to access "today" during nightime (dawn)'
return self.records[-1]
# Get most recently added NightRecord
def tonight(self):
if self.records == []: return None
if self.records[-1].is_day():
raise ValueError, 'Tried to access "tonight" during daytime (dawn)'
return self.records[-1]
# Just get the most recent record regardles of day/night
def recent(self):
return self.records[-1]
# displays most recent day/night
def disp(self):
if self.records != []:
indented_table_begin()
self.records[-1].disp()
indented_table_end()
# Abstract superclass for DayRecord and NightRecord
class AnyRecord:
# To be overloaded
def __init__(self):
# dict: peon_name -> peon_status
self.peon_statuses = {}
def is_day(self): raise NotImplementedError
def is_night(self): return not self.is_day()
def disp(self, verbose): raise NotImplementedError
# Fine the way they are
def day_or_night(self): return ('Night','Day')[self.is_day()]
def __repr__(self): return self.day_or_night()
class DayRecord(AnyRecord):
def __init__(self):
AnyRecord.__init__(self)
self.votes = {} # dict: (alive) peon_name -> who_they_voted_for
self.zombie_votes = {} # dict: necro_name ->
# [zombie_names who voted like the necro]
self.victim = '' # name of person hung; can also be SLEEP
# Useful functions for distinguishing day records from night records
def is_day(self): return True
def is_night(self): return False
def add_zombie_vote(self, necro_name, zombie_name):
dict_add(self.zombie_votes, necro_name, zombie_name)
def disp(self, verbose = True):
# Affix verb to victim
if self.victim == SLEEP:
victim_str = 'Sleep prevails over bloodlust (so sad!)'
else:
victim_str = 'Townspeople hung ' + self.victim
print "Day's Outcome: %s" % victim_str
if not verbose: return # skip table
print '
'
print """
Player:%s | Voted for: |
""" % COL_SPACER
for voter, vote in self.votes.items():
print ' %s | %s |
' \
% (voter + COL_SPACER, vote)
if voter in self.zombie_votes:
for zombie in self.zombie_votes[voter]:
print '' + \
'"assisted" by zombie %s |
' \
% zombie
print '
'
class NightRecord(AnyRecord):
def __init__(self):
AnyRecord.__init__(self)
self.pointed = {} # dict: (alive) peon_name -> who_they_pinted at
self.mafia_votes = {} # dict: mafia_name -> who_they_want_to_kill
self.mafia_victim = '' # poor sap targeted by mafia (can be None)
self.mafia_succeed = False # Toggled True if they kill their victim
self.medic_votes = {} # dict: medic_name -> who_they_tried_to_save
self.necro_raises = {} # dict: necro_name -> (who_was_raised, success?)
self.priest_exorcises = {} # priest_name -> (who_exorcise, success?)
# Useful functions for distinguishing day records from night records
def is_day(self): return False
def is_night(self): return True
# Takes: list of medics (names, and list of medic targets (also names)
# (indices line up b/w the 2)
# Sets: medic_votes: medic_name -> who_they_tried_to_save
# (Basically combines two lists into a dictionary)
def add_medic_votes(self, medics, medic_targets):
if len(medics) != len(medic_targets):
raise ValueError, '# of medics (%s) != # of medic targets (%s)' \
% (len(medics), len(medic_targets))
for i in range(len(medics)):
self.medic_votes[medics[i]] = medic_targets[i]
def disp(self, verbose = True):
if self.mafia_victim == None: self.mafia_victim = NOBODY
# -- Mafia Results --
if self.mafia_succeed:
print 'Mafia killed:',
else:
print 'Mafia tried to kill:',
print '%s
' % self.mafia_victim
quick_table(self.mafia_votes, 'wanted to kill', verbose = verbose)
# -- Medic Results --
print 'Medics:',
# Did Medics save someone?
if (not self.mafia_succeed) \
and (self.mafia_victim in self.medic_votes.values()):
# Who saved him? (can be multiple medics)
first_medic = True
for medic_name, patient_name in self.medic_votes.items():
if patient_name == self.mafia_victim:
if first_medic:
first_medic = False
else:
print 'and',
print medic_name,
print 'saved %s from the mafia' % self.mafia_victim
elif self.mafia_victim == NOBODY:
print 'All quiet on the western front'
else:
print 'Failed to save %s' % self.mafia_victim
print '
'
quick_table(self.medic_votes, 'tried to save', verbose = verbose)
# -- Necro Results --
print 'Necromancers: Animated %d zombies
' \
% count_successes(self.necro_raises)
quick_table(self.necro_raises, 'animated', 'vainly chanted over', \
verbose = verbose)
# -- Priest Results --
print 'Priests: Exorcised %d zombies
' \
% count_successes(self.priest_exorcises)
quick_table(self.priest_exorcises, 'exorcised', \
'inappropriately moistened', verbose = verbose)
# Helper function for NightRecord.disp()
#
# Takes - dict: subject_name -> target_name
# or: subject_name -> (target_name, success?)
# - action: what the subjects did to the targets (on success)
# - alt_action: same but on failure
# - verbose: tables are only printed in verbose mode
def quick_table(subj_target_dict, action, alt_action = None, verbose = True):
if not verbose: return
indented_table_begin(spacer = LIST_SPACER)
for subj, poss_pair in subj_target_dict.items():
if alt_action == None:
target = poss_pair
success = True
else:
(target, success) = poss_pair
print """
%s |
%s |
%s%s |
%s |
""" % (COL_SPACER, subj + COL_SPACER, \
(alt_action, action)[success], COL_SPACER, target)
indented_table_end()
# Takes - dict: subj_name -> (target_name, success?)
def count_successes(subj_target_dict):
return len([True for (target_name, success) in subj_target_dict.values() \
if success])