-
Notifications
You must be signed in to change notification settings - Fork 126
/
Copy pathgame.py
133 lines (116 loc) · 4.22 KB
/
game.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
import random
import logging
import os
from twisted.internet import reactor, task
from rose.common import actions, config, error, message, obstacles # NOQA
from . import track
from . import player
from . import score
log = logging.getLogger("game")
class Game(object):
"""
Implements the server for the car race
"""
def __init__(self, seed):
self.hub = None
self.rng = random.Random(seed)
self.track = track.Track(seed=seed)
self.looper = task.LoopingCall(self.loop)
self.players = {}
self.free_cars = set(range(config.number_of_cars))
self.free_lanes = set(range(config.max_players))
self._rate = config.game_rate
self.started = False
self.timeleft = config.game_duration
@property
def rate(self):
return self._rate
@rate.setter
def rate(self, value):
if value != self._rate:
log.info("change game rate to %d frames per second", value)
self._rate = value
if self.started:
self.looper.stop()
self.looper.start(1.0 / self._rate)
else:
self.update_clients()
def start(self):
if self.started:
raise error.GameAlreadyStarted()
if not self.players:
raise error.ActionForbidden("start a game with no players.")
self.track.reset()
for p in self.players.values():
p.reset()
self.timeleft = config.game_duration
self.started = True
self.looper.start(1.0 / self._rate)
def stop(self):
if not self.started:
raise error.GameNotStarted()
self.looper.stop()
self.started = False
self.update_clients()
self.print_stats()
def add_player(self, name):
if name in self.players:
raise error.PlayerExists(name)
if not self.free_cars:
raise error.TooManyPlayers()
car = self.rng.choice(tuple(self.free_cars))
self.free_cars.remove(car)
lane = self.rng.choice(tuple(self.free_lanes))
self.free_lanes.remove(lane)
log.info("add player: %r, lane: %r, car: %r", name, lane, car)
self.players[name] = player.Player(name, car, lane)
reactor.callLater(0, self.update_clients)
def remove_player(self, name):
if name not in self.players:
raise error.NoSuchPlayer(name)
player = self.players.pop(name)
self.free_cars.add(player.car)
self.free_lanes.add(player.lane)
log.info("remove player: %r, lane: %r, car: %r", name, player.lane, player.car)
if not self.players and self.started:
log.info("Stopping game. No players connected.")
self.stop()
else:
reactor.callLater(0, self.update_clients)
def drive_player(self, name, info):
log.info("drive_player: %r %r", name, info)
if name not in self.players:
raise error.NoSuchPlayer(name)
if "action" not in info:
raise error.InvalidMessage("action required")
action = info["action"]
if action not in actions.ALL:
raise error.InvalidMessage("invalid drive action %s" % action)
self.players[name].action = action
self.players[name].response_time = info.get("response_time", 1.0)
def print_stats(self):
lines = ["Stats:"]
top_scorers = sorted(self.players.values(), reverse=True)
for i, p in enumerate(top_scorers):
line = "%d %10s row:%d score:%d" % (i + 1, p.name, p.y, p.score)
lines.append(line)
log.info("%s", os.linesep.join(lines))
def loop(self):
self.track.update()
score.process(self.players, self.track)
if self.timeleft > 0:
self.update_clients()
self.timeleft -= 1
else:
self.stop()
def update_clients(self):
msg = message.Message("update", self.state())
self.hub.broadcast(msg)
def state(self):
return {
"started": self.started,
"track": self.track.state(),
"players": [p.state() for p in self.players.values()],
"timeleft": self.timeleft,
"rate": self.rate,
}