-
Notifications
You must be signed in to change notification settings - Fork 4
/
boxlift_api.py
168 lines (150 loc) · 5.85 KB
/
boxlift_api.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
import json
# This is to keep this imports to a minimum and work on
# Python 2.x and 3.x
try:
import urllib.request as urllib2
except ImportError:
import urllib2
PYCON2016_EVENT_NAME = 'pycon2016'
class Command(object):
def __init__(self, id, direction, speed):
"""A command to an elevator
:param id:
The elevator id. This is returned in the state where all
elevators are identified. It is typically '0' for the first
elevator, '1' for the next, etc.
:type id:
`basestring`
:param direction:
1 to indicate up, -1 to indicate down. Even when stationary
an elevator has a direction. Passengers wishing to go up
will not board an elevator with a down indicator.
:type direction:
`int`
:param speed:
0 to halt on a floor, 1 to move. Sorry, no greater speeds
allowed!
:type speed:
`int`
"""
self.id = id
self.direction = direction
self.speed = speed
def __str__(self):
return "set car {} (direction={}, speed={})".format(
self.id, self.direction, self.speed)
class BoxLift(object):
HOST = 'http://codelift.org'
def __init__(self, bot_name, plan, email, registration_id,
event_name='', sandbox_mode=False):
"""An object that provides an interface to the Lift System.
:param bot_name:
The name for your entry, such as "Mega Elevator 3000"
:type bot_name:
`basestring`
:param plan:
The plan to run, such as "training_1"
:type plan:
`basestring`
:param email:
A contact email. This helps us contact you if there are
problems or to inform you if you have one of the top 10
finishers. Prizes will be distributed in the Expo Hall on
Sunday breakfast and lunch.
:type email:
`basestring`
:param registration_id:
A unique id for the event. Required for prize pickup.
e.g. your Pycon 2015 registration number.
:type registration_id:
`basestring`
:param event_name:
The name of the event being run. e.g. "pycon2015"
:type event_name:
`basestring`
:param sandbox_mode:
set to True to opt out of leader board, recent runs, and
competition. Useful for testing and for event organizers
who want to play, but don't want to be on the leader board.
Tokens will not expire, allowing you to step through code
slowly.
:type sandbox_mode:
`bool`
"""
self.email = email
initialization_data = {
'username': bot_name,
'email': email,
'plan': plan,
'sandbox': sandbox_mode,
}
if event_name:
initialization_data['event_name'] = event_name
if registration_id:
initialization_data['event_id'] = registration_id
state = self._get_world_state(initialization_data)
if state['status'] == 'error':
print(state['message'])
self.game_id = state['id']
self.token = state['token']
self.status = state['status']
self.building_url = state['building']
# fix building_url
self.building_url = self.url_root() + "/" + self.game_id
self.visualization_url = state['visualization']
print(state['message'])
print('building url: {}'.format(self.building_url))
print('visualization url: {}'.format(self.visualization_url))
def _get_world_state(self, initialization_data):
"""Initialize and gets the state of the world without sending
any commands to advance the clock"""
return self._post(self.url_root(), initialization_data)
def send_commands(self, commands=None):
"""Send commands to advance the clock. Returns the new state of
the world.
:param commands:
list of commands to elevators. If no commands are sent the
clock does not advance.
:type commands:
`iterable` of `Command`
:return:
The new state of the world
:rtype:
`dict`
"""
commands = commands or []
command_list = {}
for command in commands:
command_list[command.id] = {
'speed': command.speed, 'direction': command.direction
}
data = {'token': self.token, 'commands': command_list}
state = self._post(self.building_url, data)
print("status: {}".format(state['status']))
if state['status'].lower() == 'error':
print("message: {}".format(state['message']))
if 'token' in state:
self.token = state['token']
else:
print('no token this turn')
if 'requests' not in state:
state['requests'] = []
for elevator_data in state.get('elevators', []):
if 'buttons_pressed' not in elevator_data:
elevator_data['buttons_pressed'] = []
return state
@classmethod
def url_root(cls):
return cls.HOST + "/v1/buildings"
def _post(self, url, data):
"""wrapper to encode/decode our data and the return value"""
req = urllib2.Request(url, json.dumps(data).encode('utf-8'))
response = urllib2.urlopen(req)
res = response.read()
if isinstance(res, bytes):
res = res.decode('utf-8')
try:
return json.loads(res)
except ValueError:
# probably empty response so no JSON to decode
return res