Created
April 14, 2010 17:13
-
-
Save Arachnid/366068 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Aleut | 30835 | 28010 | 5 | |
---|---|---|---|---|
ALPHA 1 | 27699 | 32516 | 8 | |
ALPHA 2 | 32260 | 31402 | 1 | |
ALPHA 3 | 29747 | 33441 | 8 | |
ALPHA 4 | 31722 | 34644 | 7 | |
ALPHA 5 | 32234 | 27853 | 5 | |
ALPHA 6 | 31295 | 30871 | 3 | |
ALPHA 7 | 35735 | 33825 | 5 | |
ALPHA 8 | 33335 | 33005 | 3 | |
ALPHA 9 | 28490 | 33645 | 8 | |
ALPHA 10 | 31621 | 29265 | 4 | |
ALPHA 11 | 34408 | 33520 | 4 | |
ALPHA 12 | 32597 | 28499 | 6 | |
ALPHA 13 | 36557 | 30317 | 6 | |
ALPHA 14 | 27985 | 30145 | 8 | |
ALPHA 15 | 31032 | 35320 | 7 | |
ALPHA 16 | 35506 | 29610 | 6 | |
ALPHA 17 | 34007 | 28105 | 6 | |
Arcadia | 31621 | 32517 | 2 | |
Cidade | 32287 | 31704 | 0 | |
Earthbreach | 31360 | 30269 | 3 | |
Echo | 32034 | 31728 | 0 | |
Eltsina | 31859 | 31025 | 2 | |
Fuseli | 32771 | 32708 | 2 | |
Getty | 31947 | 36031 | 7 | |
Goldenrod | 34136 | 32543 | 3 | |
Gonk | 30259 | 29434 | 4 | |
Grottopolis | 27386 | 36631 | 9 | |
Isla di Pisa | 32259 | 32265 | 0 | |
Islo | 31609 | 31308 | 2 | |
Jordan | 32485 | 31976 | 1 | |
Juliet | 27635 | 31260 | 8 | |
Kadath | 28575 | 29071 | 6 | |
Leng | 36185 | 31318 | 5 | |
Lhasa | 32997 | 32320 | 2 | |
Luz | 32656 | 35664 | 7 | |
Midgard | 36210 | 32782 | 5 | |
New Hovlund | 32225 | 31937 | 0 | |
Olio | 34299 | 34357 | 4 | |
Phillipia | 34359 | 35141 | 6 | |
Romeo | 28496 | 31140 | 8 | |
Sharif | 35211 | 30941 | 5 | |
Shriebeck | 32586 | 32283 | 1 | |
Steppe | 32035 | 32037 | 0 | |
Tehras | 33637 | 35804 | 6 | |
Tinkspoit | 30921 | 29762 | 3 | |
Tortuga | 33285 | 33683 | 8 | |
Uurwerk | 36274 | 27863 | 9 | |
Valvia | 31634 | 31923 | 1 | |
Volstoy | 28735 | 30117 | 7 | |
Fuel XIII | 33450 | 35073 | 4 | |
Fuel XIV | 32612 | 34627 | 3 | |
Fuel XV | 28535 | 36003 | 8 | |
Fuel XVI | 29578 | 35527 | 7 | |
Fuel XVII | 28844 | 32642 | 8 | |
Fuel XVIII | 30844 | 33992 | 7 | |
Fuel XIX | 31959 | 31446 | 2 | |
Fuel XX | 33383 | 30903 | 4 | |
Fuel XXI | 33784 | 29541 | 5 | |
Fuel XXII | 32341 | 32085 | 0 | |
Fuel XXIII | 30522 | 36209 | 7 | |
Fuel XXIV | 35374 | 32307 | 5 | |
Fuel XXV | 33934 | 33721 | 3 | |
Fuel XXVI | 29771 | 30550 | 5 | |
Fuel XXVII | 33270 | 28662 | 5 | |
Fuel XXVIII | 35197 | 28375 | 6 | |
Fuel XXIX | 28884 | 31559 | 8 | |
Fuel XXX | 31885 | 35294 | 7 | |
Fuel XXXI | 33969 | 33070 | 5 | |
Fuel XXXII | 34594 | 30087 | 5 | |
Fuel XXXIII | 36060 | 31959 | 5 | |
Fuel XXXIV | 31770 | 32185 | 1 | |
Fuel XXXV | 30895 | 28951 | 4 | |
Fuel XXXVI | 32185 | 32083 | 0 | |
Fuel XXXVII | 28285 | 30704 | 8 | |
Fuel XXXVIII | 32335 | 31873 | 0 | |
Fuel XXXIX | 31962 | 28749 | 5 | |
Fuel XL | 36274 | 29163 | 6 | |
Fuel XLI | 33437 | 34347 | 4 | |
Fuel LII | 34600 | 30889 | 5 | |
Fuel LIII | 30550 | 30346 | 6 | |
Fuel LIV | 29575 | 28685 | 6 | |
Fuel LV | 32787 | 29806 | 5 | |
Fuel LVI | 34762 | 33950 | 4 | |
Fuel LVII | 29299 | 29608 | 6 | |
Fuel LVIII | 32161 | 31821 | 0 | |
Fuel LIX | 29987 | 32002 | 7 | |
Fuel LX | 35262 | 33108 | 5 | |
Fuel LXI | 28137 | 34916 | 8 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import csv | |
import heapq | |
import math | |
class Skyland(object): | |
def __init__(self, name, location): | |
self.name = name | |
self.location = location | |
self.neighbours = [] | |
def __repr__(self): | |
return "Skyland(%r, %r)" % (self.name, self.location) | |
MAX_EDGE_DISTANCE = 6000 | |
def distance(a, b): | |
return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) | |
def load_skylands(fh): | |
r = csv.reader(fh) | |
skylands = {} | |
for name, x, y in r: | |
skylands[name] = Skyland(name, (int(x), int(y))) | |
for src in skylands.values(): | |
for dest in skylands.values(): | |
if src == dest: continue | |
dist = distance(src.location, dest.location) | |
if dist <= MAX_EDGE_DISTANCE: | |
src.neighbours.append((dist, dest)) | |
src.neighbours.sort() | |
return skylands | |
def dijkstras(src, dest, max_dist): | |
final = {} # Maps vertices to (min cost, real cost, previous vertex) tuples | |
intermediate = {} # Maps vertices to (min cost, real cost, previous vertex) tuples | |
frontier = [] # Heap of (vertex, min cost, real cost) tuples | |
crow_distance = distance(src.location, dest.location) | |
heapq.heappush(frontier, (src, crow_distance, 0)) | |
intermediate[src] = (crow_distance, 0, None) | |
while frontier: | |
current, min_cost, real_cost = heapq.heappop(frontier) | |
if current in final: continue | |
final[current] = intermediate[current] | |
del intermediate[current] | |
if current == dest: break | |
for edge_dist, edge_dest in current.neighbours: | |
if edge_dest in final: continue | |
if edge_dist > max_dist: break | |
new_real_cost = real_cost + edge_dist | |
new_min_cost = new_real_cost + distance(edge_dest.location, dest.location) | |
if edge_dest not in intermediate or intermediate[edge_dest][0] > new_min_cost: | |
intermediate[edge_dest] = (new_min_cost, new_real_cost, current) | |
heapq.heappush(frontier, (edge_dest, new_min_cost, new_real_cost)) | |
if dest not in final: | |
return None, [] | |
path = [] | |
current = dest | |
while current: | |
path.append(current) | |
current = final[current][2] | |
path.reverse() | |
return final[dest][1], path | |
def all_points(graph, max_dist): | |
paths = dict(((src, dest), (dist, None)) for src in graph | |
for dist, dest in src.neighbours if dist <= max_dist) | |
for via in graph: | |
for src in graph: | |
for dest in graph: | |
a = paths.get((src, via)) | |
b = paths.get((via, dest)) | |
c = paths.get((src, dest)) | |
if a and b and (not c or a[0] + b[0] < c[0]): | |
paths[(src, dest)] = (a[0] + b[0], via) | |
return paths |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import struct | |
class DesyncException(Exception): pass | |
class UnknownMessageTypeException(Exception): pass | |
class InvalidStatusException(Exception): pass | |
def read_element(fmt): | |
"""Struct element reader factory. | |
Returns any primitive. | |
""" | |
size = struct.calcsize(fmt) | |
def _in(f): | |
return struct.unpack(fmt, f.read(size))[0] | |
return _in | |
def read_struct(fmt): | |
"""Struct reader factory. | |
Returns a tuple of primitives. | |
""" | |
size = struct.calcsize(fmt) | |
def _in(f): | |
return struct.unpack(fmt, f.read(size)) | |
return _in | |
def read_string(f): | |
"""String reader. | |
Returns str. | |
""" | |
data = f.read(1) | |
ret = '' | |
while data != '\0': | |
ret += data | |
data = f.read(1) | |
return ret | |
def read_composite(*converters): | |
"""Composite reader factory. | |
Returns tuple of any. | |
""" | |
def _in(f): | |
return tuple(converter(f) for converter in converters) | |
return _in | |
def read_array(length_func, data_func): | |
"""Array reader factory. | |
Returns array of any. | |
""" | |
def _in(f): | |
length = length_func(f) | |
return [data_func(f) for x in range(length)] | |
return _in | |
def assert_equal(reader, eq): | |
"""Equality assertion factory. | |
Returns the same type as its reader. | |
""" | |
def _in(f): | |
data = reader(f) | |
if data != eq: | |
raise InvalidDataException("Expected %r, got %r" % (eq, data)) | |
return data | |
return _in | |
# Returns (0x01, status_message) | |
read_status = read_composite( | |
assert_equal(read_element('B'), 0x01), | |
read_string | |
) | |
# Returns (0x20, response_type) | |
read_response_header = read_composite( | |
assert_equal(read_element('B'), 0x20), | |
read_element('B') | |
) | |
# Returns (status, _, [(header, status, _, island_id, island_name, _, | |
# [(resource_type, base_price, max_qty, current_price), | |
# ...]), ...]) | |
read_market_data = read_composite( | |
read_status, | |
read_element('B'), # Unknown byte | |
read_array(read_element('B'), read_composite( | |
assert_equal(read_response_header, (0x20, 0x45)), | |
read_status, | |
read_element('B'), # Unknown byte | |
read_element('B'), # Island ID | |
read_string, # Island name | |
read_struct('9B'), # Unknown data | |
read_array(read_element('B'), read_struct('!BIII')), # Market data | |
)), | |
) | |
response_handlers = { | |
0x44: read_market_data, | |
} | |
def read_response(f): | |
_, response_type = read_response_header(f) | |
if response_type not in response_handlers: | |
raise UnknownMessageTypeException(response_type) | |
return response_type, response_handlers[response_type](f) | |
class SocketReader(object): | |
def __init__(self, sock): | |
self.sock = sock | |
self.buf = '' | |
def read(self, count): | |
while len(self.buf) < count: | |
data = self.sock.recv(count - len(self.buf)) | |
if data == '': | |
raise EOFError() | |
self.buf += data | |
ret = self.buf[:count] | |
self.buf = self.buf[count:] | |
return ret | |
def write_element(fmt): | |
"""Struct element writer factory. | |
data = any primitive | |
""" | |
def _out(f, data): | |
f.write(struct.pack(fmt, data)) | |
return _out | |
def write_struct(fmt): | |
"""Struct writer factory. | |
data = tuple of any primitive | |
""" | |
def _out(f, data): | |
f.write(struct.pack(fmt, *data)) | |
return _out | |
def write_string(f, data): | |
"""String writer. | |
data = str | |
""" | |
f.write(data + '\0') | |
def write_composite(*converters): | |
"""Composite writer factory. | |
data = tuple of any | |
""" | |
def _out(f, data): | |
for converter, datum in zip(converters, data): | |
converter(f, datum) | |
return _out | |
def write_request(message_type, request_writer): | |
"""Request writer factory. | |
data = (session_id, any) | |
""" | |
def _out(f, data): | |
f.write(struct.pack('!BBi', 0xa0, message_type, data[0])) | |
request_writer(f, data[1]) | |
return _out | |
# data = (session_id, (0x00, 0x00)) | |
write_market_data_request = write_request(0x45, write_struct('BB')) | |
class SocketWriter(object): | |
def __init__(self, sock): | |
self.sock = sock | |
def write(self, data): | |
while len(data): | |
sent = self.sock.send(data) | |
data = data[sent:] | |
def get_market_data(address, port): | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
s.connect((address, port)) | |
reader = SocketReader(s) | |
writer = SocketWriter(s) | |
write_market_data_request(writer, (0, (0, 0))) | |
type, response = read_response(reader) | |
s.close() | |
data = {} | |
assert type == 0x44 | |
for header, status, _, island_id, island_name, _, cols in response[2]: | |
data[island_name] = cols | |
return data | |
def market_price(base_price, max_qty, current_qty): | |
return base_price + base_price * (1-(float(current_qty)/max_qty)) | |
market_prices = dict((k, [market_price(*x[1:]) for x in v]) for k, v in market.items()) | |
def find_best_trade(src, dest, num_resources): | |
best_profit = 0 | |
best_resource = None | |
for i, (src_price, dest_price) in enumerate(zip(src[:num_resources], dest[:num_resources])): | |
if dest_price - src_price > best_profit: | |
best_profit = dest_price - src_price | |
best_resource = i | |
return best_resource, best_profit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
class Tradefinder(object): | |
def __init__(self, skylands, distances, profits): | |
"""Constructor. | |
Args: | |
skylands: A list of permissible skylands. | |
distances: A dict mapping (src, dest) skyland tuples to (distance, via) tuples | |
profits: A dict mapping (src, dest) skyland tuples to (resource, profit) tuples | |
""" | |
self.distances = distances | |
self.profits = profits | |
self.skylands = skylands | |
def _get_next_state(self, state, sources): | |
next_state = {} | |
next_sources = [] | |
for dest in self.skylands: | |
best_route, best_profit_dist = None, None | |
for src in sources: | |
if src == dest: continue | |
src_dist, src_profit, src_path, src_resources = state[src] | |
new_dist = src_dist + self.distances[src, dest][0] | |
leg_cargo, leg_profit = self.profits[src, dest] | |
new_profit = src_profit + leg_profit | |
new_profit_dist = new_profit / new_dist | |
if not best_profit_dist or new_profit_dist > best_profit_dist: | |
best_profit_dist = new_profit_dist | |
best_route = (new_dist, new_profit, src_path + [src], src_resources + [leg_cargo]) | |
if best_route: | |
next_state[dest] = best_route | |
if dest not in best_route[2]: | |
next_sources.append(dest) | |
else: | |
next_state[dest] = state[dest] | |
next_sources.append(dest) | |
return next_state, next_sources | |
def find_best_route(self, start): | |
"""Finds the best route beginning at start and ending in a cycle. | |
Args: | |
start: The skyland to start at. | |
Returns: | |
(list of hops, list of resources to trade) | |
""" | |
# State maps skylands to (distance travelled, profit made, hops, resources) tuples | |
state = {} | |
state[start] = (0.0, 0.0, [], []) | |
sources = [start] | |
while sources: | |
state, sources = self._get_next_state(state, sources) | |
best_profit_dist = None | |
best_route = None | |
for dest, (dist, profit, hops, resources) in state.iteritems(): | |
if dist / profit > best_profit_dist: | |
best_profit_dist = dist / profit | |
best_route = (hops + [dest], resources) | |
return best_route |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment