snipe.py (6079B)
1 #!/usr/bin/python3 -u 2 3 import sys 4 from random import choice 5 from operator import attrgetter 6 import re 7 import pickle 8 9 def fmt_score(score): 10 return str(score) + " point" + ("s" if abs(score) != 1 else "") 11 12 sys.stdin = sys.stdin.detach() 13 14 num_missions = 3 15 ranking_size = 5 16 abort_penalty = 2 17 mission_file = "missions" 18 name = "arbitre" 19 dump_file = "game.dmp" 20 ignore = "bot" 21 22 help = [ 23 "Chaque joueur a une liste secrète de mots qui lui rapportent des points.", 24 "Lorsque quelqu'un les dit, il perd des points et le joueur en gagne.", 25 "Si un mot est trop dur, dis-le moi et je t'en donne un autre contre "+ 26 fmt_score(abort_penalty), 27 "Commandes : \"help\", \"info\", \"rank\""] 28 29 def parse(line): 30 m = re.match("\[([^]]*?)\] <([^>]*?)> (.*)", line) 31 chan = m.group(1) 32 speaker = m.group(2) 33 message = m.group(3).lstrip() 34 to_us = False 35 address = name+":" 36 if message.startswith(address): 37 to_us = True 38 message = message[len(address):].lstrip() 39 if chan == speaker or chan == name: 40 to_us = True 41 return chan, speaker, message.rstrip(), to_us 42 43 def split(message): 44 return re.split("\W+", message, flags=re.UNICODE) 45 46 def read_missions(name): 47 f = open(name) 48 result = [] 49 for line in f.readlines(): 50 mission = line.split(' ') 51 mission[1] = int(mission[1]) 52 result.append(mission) 53 f.close() 54 return result 55 56 57 def get_mission(missions, forbidden=[]): 58 return choice([mission for mission in missions if mission[0] not in 59 forbidden]) 60 61 def ranking(game): 62 players = sorted(game.values(), key=attrgetter('score')) 63 players.reverse() 64 result = [] 65 pos = 1 66 for player in players: 67 if pos > ranking_size: 68 break 69 result.append("%s. *%s* avec %s" % (str(pos), player.sayname, 70 fmt_score(player.score))) 71 pos += 1 72 return result 73 74 def say_to(chan, f, message): 75 if chan == name: 76 say(f, message) 77 else: 78 say(chan, message) 79 80 def say(chan, messages): 81 for message in messages: 82 print("[%s] %s" % (chan, message)) 83 84 class Player: 85 @property 86 def sayname(self): 87 return self.name[0] + '_' + self.name[1:] 88 89 def __init__(self, name): 90 self.name = name 91 self.score = 0 92 self.missions = [] 93 self.spawn_missions() 94 95 def spawn_missions(self): 96 while len(self.missions) < num_missions: 97 self.add_mission() 98 99 def add_mission(self): 100 self.missions.append(get_mission(missions, [mission[0] for mission in 101 self.missions])) 102 103 def private_message(self, messages): 104 say(self.name, messages) 105 106 def welcome(self): 107 self.private_message(["Bienvenue "+self.name+" !"]) 108 109 def report(self): 110 self.private_message( 111 ["Ton score est de " + fmt_score(self.score), 112 "Les mots à faire dire sont :"] + 113 ["-> \"%s\" pour %s" % (mission[0], fmt_score(mission[1])) for 114 mission in self.missions]) 115 116 def abort(self, word): 117 found = False 118 for i in range(len(self.missions)): 119 if self.missions[i][0] == word: 120 found = True 121 self.missions.pop(i) 122 self.score -= abort_penalty 123 break 124 self.spawn_missions() 125 return found 126 127 def succeed(self, word): 128 found = 0 129 for i in range(len(self.missions)): 130 if self.missions[i][0] == word: 131 mission = self.missions.pop(i) 132 found = mission[1] 133 self.score += found 134 break 135 # not until we talk again 136 # self.spawn_missions() 137 return found 138 139 def to_unicode(data, errors='replace'): 140 detection = chardet.detect(data) 141 encoding = detection.get('encoding') or 'utf-16' 142 return unicode(data, encoding, errors=errors) 143 144 missions = read_missions(mission_file) 145 146 game = {} 147 148 ok = False 149 try: 150 f = open(dump_file, 'rb') 151 ok = True 152 except Exception as e: 153 print("Could not open dump.", file=sys.stderr) 154 155 if ok: 156 game = pickle.load(f) 157 f.close() 158 159 try: 160 while True: 161 l = sys.stdin.readline() 162 try: 163 line = l.decode('utf8') 164 except UnicodeDecodeError: 165 line = l.decode('latin1') 166 if (not line): 167 raise ValueError 168 chan, speaker, message, to_us = parse(line) 169 if speaker.find(ignore) != -1: 170 continue 171 if speaker not in list(game.keys()): 172 game[speaker] = Player(speaker) 173 speaker = game[speaker] 174 speaker.spawn_missions() 175 if to_us: 176 if message == "help": 177 say_to(chan, speaker.name, help) 178 speaker.report() 179 elif message == "rank": 180 say_to(chan, speaker.name, ranking(game)) 181 elif message == "info": 182 speaker.report() 183 else: 184 if speaker.abort(message): 185 say_to(chan, speaker.name, 186 ["Tu as abandonné \""+message+"\" et perdu " + 187 fmt_score(abort_penalty)]) 188 speaker.report() 189 else: 190 say_to(chan, speaker.name, 191 ["Ce mot n'est pas dans ta liste. Pour de l'aide, fais " + 192 "\"help\"."]) 193 else: 194 words = split(message) 195 todo = [] 196 for word in words: 197 word = word.lower() 198 for player in game.values(): 199 if player.name != speaker.name: 200 score = player.succeed(word) 201 if score > 0: 202 lose = int(score / 2) 203 msg = ["*** %s ! %s gagne %s et %s perd %s." % ( 204 word, player.sayname, fmt_score(score), speaker.sayname, 205 fmt_score(lose))] 206 say_to(chan, speaker.name, msg) 207 todo.append((player.name, 208 "%s a dit \"%s\" et t'a fait gagner %s." % (speaker.name, 209 word, fmt_score(score)))) 210 todo.append((player.name, 211 "Un nouveau mot te sera proposé prochainement.")) 212 todo.append((speaker.name, 213 "Tu as dit \"%s\", tu as fait gagner %s à %s et tu as " 214 "perdu %s." % (word, fmt_score(score), player.name, 215 fmt_score(lose)))) 216 speaker.score -= lose 217 for (player, msg) in todo: 218 game[player].private_message([msg]) 219 for player in set([t[0] for t in todo]): 220 game[player].report() 221 222 finally: 223 f = open(dump_file, 'wb+') 224 pickle.dump(game, f) 225 f.close() 226