Post by Pleonast on Aug 24, 2012 12:11:52 GMT -8
Here's the code I'm using to count votes. Quote the post to see the proper indentations.
# -- VoteCount.py -- 11 Sep 2012
"""
Copyright 2012 Pleonast on boards.straightdope.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
"""
class Day_t:
def __init__(Self,Name,Num,LastPost):
Self._Name=Name
Self._Num=Num
Self._LastPost=LastPost
class Player_t:
def __init__(Self,Name,Alias,Rank):
Self._Name=Name
Self._Alias=Alias
Self._Rank=Rank
Self._VotesAsSource=[]
Self._UnvotesAsSource=[]
Self._NetVotesAsSource=[]
Self._VotesAsTarget=[]
Self._UnvotesAsTarget=[]
Self._NetVotesAsTarget=[]
Self._Penalties=[]
Self._SortValue=(-float('inf'),)
def AddVoteAsSource(Self,Vote):
if Vote._Source!=Self._Alias:
return
# prevent voting if already have vote on target
for v in Self._NetVotesAsSource:
if v._Target==Vote._Target:
return
Self._VotesAsSource+=[Vote]
Self._NetVotesAsSource+=[Vote]
def AddVoteAsTarget(Self,Vote):
if Vote._Target!=Self._Alias:
return
# prevent voting if already have vote from source
for v in Self._NetVotesAsTarget:
if v._Source==Vote._Source:
return
Self._VotesAsTarget+=[Vote]
Self._NetVotesAsTarget+=[Vote]
def AddUnvoteAsSource(Self,Unvote):
if Unvote._Source!=Self._Alias:
return
# only unvote if vote is present
for v in Self._NetVotesAsSource[:]:
if v._Target==Unvote._Target:
Self._UnvotesAsSource+=[Unvote]
Self._NetVotesAsSource.remove(v)
return
def AddUnvoteAsTarget(Self,Unvote):
if Unvote._Target!=Self._Alias:
return
# only unvote if vote is present
for v in Self._NetVotesAsTarget[:]:
if v._Source==Unvote._Source:
Self._UnvotesAsTarget+=[Unvote]
Self._NetVotesAsTarget.remove(v)
return
def AddPenalty(Self,Penalty):
if Penalty._Target!=Self._Alias:
return
Self._Penalties+=[Penalty]
def SortValue(Self,Day):
numVotes=len(Self._NetVotesAsTarget)+len(Self._Penalties)
numPenalties=len(Self._Penalties)
numPenaltiesToday=0
mostRecentPenaltyDayNum=0
mostRecentPenaltyPost=0
for penalty in Self._Penalties:
if penalty._DayNum==Day._Num:
numPenaltiesToday+=1
if (penalty._DayNum>mostRecentPenaltyDayNum or
(penalty._DayNum==mostRecentPenaltyDayNum and
penalty._Post>mostRecentPenaltyPost)):
mostRecentPenaltyDayNum=penalty._DayNum
mostRecentPenaltyPost=penalty._Post
rank=Self._Rank
Self._SortValue=(numVotes,numPenalties,numPenaltiesToday,
mostRecentPenaltyDayNum,mostRecentPenaltyPost,-rank)
return Self._SortValue
def StrVotesAsSource(Self,Players):
output=''
output+='[b]'+Self._Name+'[/b] has '
numVotes=len(Self._NetVotesAsSource)
if not numVotes:
output+='not voted.\n'
return output
output+='voted for'
isFirst=True
unvotes=Self._UnvotesAsSource[:]
for vote in Self._VotesAsSource:
unvote=None
for u in unvotes:
if u._Target==vote._Target:
unvote=u
unvotes.remove(u)
break
if isFirst:
isFirst=False
else:
output+=','
output+=' '
if unvote:
output+='[del]'
output+=Players[vote._Target]._Name
output+=' ('+str(vote._Post)
if unvote:
output+='-'+str(unvote._Post)
output+=')'
if unvote:
output+='[/del]'
if isFirst:
output+=' not voted'
output+='.\n'
return output
def StrVotesAsTarget(Self,Players):
output=''
if Self._Alias!='no' and Self._Alias!='ham' and Self._Alias!='fil':
output+=str(Self._Rank)+'. '
output+='[b]'+Self._Name+'[/b] has '
numVotes=len(Self._NetVotesAsTarget)+len(Self._Penalties)
if not numVotes:
output+='no votes.\n'
return output
output+=str(numVotes)+' vote'
if numVotes>1:
output+='s'
if Self._Alias=='ham' or Self._Alias=='fil':
output+=' [of '+str((len(Players)-3+1)//2)+' needed]'
output+=' from'
isFirst=True
for penalty in Self._Penalties:
if isFirst:
isFirst=False
else:
output+=','
output+=' Penalty ('+GetDayNightStr(penalty._DayNum)+'.'
if penalty._Post==999999:
output+='Dusk*)'
else:
output+=str(penalty._Post)+')'
unvotes=Self._UnvotesAsTarget[:]
for vote in Self._VotesAsTarget:
unvote=None
for u in unvotes:
if u._Source==vote._Source:
unvote=u
unvotes.remove(u)
break
if isFirst:
isFirst=False
else:
output+=','
output+=' '
if unvote:
output+='[del]'
output+=Players[vote._Source]._Name
output+=' ('+str(vote._Post)
if unvote:
output+='-'+str(unvote._Post)
output+=')'
if unvote:
output+='[/del]'
if isFirst:
output+=' no votes'
output+='.\n'
return output
class Vote_t:
def __init__(Self,Source,Target,Post):
Self._Source=Source
Self._Target=Target
Self._Post=Post
class Penalty_t:
def __init__(Self,Target,Post,DayNum):
Self._Target=Target
Self._Post=Post
Self._DayNum=DayNum
def CheckAlias(Players,Alias):
if not Alias in Players:
raise ValueError('unknown alias '+Alias)
def ApplyVote(Players,Source,Target,Post):
CheckAlias(Players,Source)
CheckAlias(Players,Target)
vote=Vote_t(Source,Target,Post)
Players[Source].AddVoteAsSource(vote)
Players[Target].AddVoteAsTarget(vote)
def ApplyUnvote(Players,Source,Target,Post):
CheckAlias(Players,Source)
CheckAlias(Players,Target)
vote=Vote_t(Source,Target,Post)
Players[Source].AddUnvoteAsSource(vote)
Players[Target].AddUnvoteAsTarget(vote)
def ApplyUnvoteAll(Players,Source,Post):
CheckAlias(Players,Source)
for player in Players:
ApplyUnvote(Players,Source,player,Post)
def ApplyPenalty(Players,Target,Post,DayNum):
CheckAlias(Players,Target)
penalty=Penalty_t(Target,Post,DayNum)
Players[Target].AddPenalty(penalty)
def GetDayNightStr(Day):
dayInt=round(Day)
dayDiff=Day-dayInt
if abs(dayDiff)<0.1:
return 'D'+str(dayInt)
else:
return 'N'+str(int(Day))
def GetLynchOrder(Players,Day):
lynchOrder=[]
for alias in Players:
if alias=='ham' or alias=='fil':
continue
Players[alias].SortValue(Day)
lynchOrder+=[Players[alias]]
lynchOrder.sort(key=lambda Player:Player._SortValue,reverse=True)
return lynchOrder
def GetRankOrder(Players):
rankOrder=[]
for alias in Players:
if alias=='ham' or alias=='fil' or alias=='no':
continue
rankOrder+=[Players[alias]]
rankOrder.sort(key=lambda Player:Player._Rank)
return rankOrder
def Main():
# initialize
day=None
players={}
players['no']=Player_t('No Lynch','no',999)
players['ham']=Player_t('Hammer','ham',999)
players['fil']=Player_t('Filibuster','fil',999)
postMax=0
# read input
try:
lineCount=0
while True:
line=input()
lineCount+=1
if not line:
continue
words=line.split(',')
try:
if words[0].startswith('d'):
# d,DayName,DayNum,LastPost
# add day info
dayName=words[1]
dayNum=int(words[2])
lastPost=int(words[3])
day=Day_t(dayName,dayNum,lastPost)
elif words[0].startswith('p'):
# p,Name,Alias,Rank
# add player to list
name=words[1]
alias=words[2]
rank=int(words[3])
players[alias]=Player_t(name,alias,rank)
elif words[0].startswith('v'):
# v,Post,SourceAlias,TargetAlias(es),...
# record vote
post=int(words[1])
source=words[2]
for idx in range(3,len(words)):
target=words[idx]
ApplyVote(players,source,target,post)
if post>postMax:
postMax=post
elif words[0].startswith('u'):
# u,Post,SourceAlias,TargetAlias(es),...
# record unvote
post=int(words[1])
source=words[2]
for idx in range(3,len(words)):
target=words[idx]
if target=='all':
ApplyUnvoteAll(players,source,post)
else:
ApplyUnvote(players,source,target,post)
if post>postMax:
postMax=post
elif words[0].startswith('x'):
# x,DayNum,Post,TargetAlias(es)
# record penalty
dayNum=float(words[1])
post=int(words[2])
for idx in range(3,len(words)):
target=words[idx]
ApplyPenalty(players,target,post,dayNum)
else:
raise ValueError('Unknown command '+words[0])
except (ValueError,IndexError):
print('Error on line',lineCount,':',line)
except EOFError:
pass
if not day:
raise ValueError('Day not defined')
if day._LastPost and postMax>day._LastPost:
day._LastPost=postMax
# add penalties to players without active votes
noVoteList=[]
if day._LastPost:
for alias,player in players.items():
if alias=='no' or alias=='ham' or alias=='fil':
continue
hasGoodVote=False
for vote in player._NetVotesAsSource:
if vote._Target!='ham' and vote._Target!='fil':
hasGoodVote=True
break
if not hasGoodVote:
ApplyPenalty(players,player._Alias,999999,day._Num)
noVoteList+=[player]
noVoteList.sort(key=lambda Player:Player._Rank)
# get lynch
lynchOrder=GetLynchOrder(players,day)
if lynchOrder[0]._Alias=='no':
lynch='no one'
else:
lynch='[b]'+lynchOrder[0]._Name+'[/b]'
# output results
output=''
output+='[u]'
if day._LastPost:
output+='Current'
else:
output+='Final'
output+=' Day '+day._Name+' Vote Count[/u]\n\n'
for player in lynchOrder:
output+=player.StrVotesAsTarget(players)
output+='\n'
if day._LastPost:
if players['ham']._NetVotesAsTarget:
output+=players['ham'].StrVotesAsTarget(players)
if players['fil']._NetVotesAsTarget:
output+=players['fil'].StrVotesAsTarget(players)
if (players['ham']._NetVotesAsTarget or
players['fil']._NetVotesAsTarget):
output+='\n'
output+=('Given these votes as of Post '+str(day._LastPost)+', '
+lynch+' will be lynched.\n')
if noVoteList:
output+='\n*The following player'
if len(noVoteList)>1:
output+='s'
output+=' will receive a penalty vote for not voting:'
for idx in range(len(noVoteList)):
if idx:
output+=','
output+=' [b]'+noVoteList[idx]._Name+'[/b]'
output+='.\n'
else:
output+='With these votes, '+lynch+' was lynched.\n'
output+='\n'
rankOrder=GetRankOrder(players)
for player in rankOrder:
output+=player.StrVotesAsSource(players)
print(output)
if __name__=='__main__':
# multiprocessing.freeze_support()
Main()
# -- VoteCount.py