Created
June 2, 2013 11:22
-
-
Save Kha/5693348 to your computer and use it in GitHub Desktop.
khaderp, the Rocket Scissors Spacegoo bruteforce bot
This file contains 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, json | |
import random, pprint | |
import sys | |
import math | |
import re | |
class arith_list(list): | |
def __add__(self, other): | |
return arith_list([x + y for x, y in zip(self, other)]) | |
def __sub__(self, other): | |
return arith_list([x - y for x, y in zip(self, other)]) | |
def __rmul__(self, other): | |
return arith_list([x * other for x in self]) | |
class Planet: | |
def __init__(self, json): | |
self.player_id = json['owner_id'] | |
self.ships = arith_list(json['ships']) | |
self.production = arith_list(json['production']) | |
self.x = json['x'] | |
self.y = json['y'] | |
self.id = json['id'] | |
self.incoming_fleets = [] | |
def __repr__(self): | |
return "<id {}, own {}, ships {}, prod {}>".format(self.id, self.player_id, self.ships, self.production) | |
def dist(self, other): | |
return int(math.ceil(math.sqrt((self.x-other.x)**2 + (self.y-other.y)**2))) | |
def add_incoming_fleet(self, fleet): | |
self.incoming_fleets.append(fleet) | |
def flyto(self, other, ships): | |
return ("send %s %s %d %d %d" % (self.id, other.id, ships[0], ships[1], ships[2])) | |
class Fleet: | |
def __init__(self, json, planets): | |
self.id = json['id'] | |
self.player_id = json['owner_id'] | |
target_string = json['target'] | |
origin_string = json['origin'] | |
self.target = [planet for planet in planets if planet.id == target_string][0] | |
self.origin = [planet for planet in planets if planet.id == origin_string][0] | |
self.ships = arith_list(json['ships']) | |
self.eta = json['eta'] | |
def can_intercept(self, origin_planet, current_round): | |
combined_ships = origin_planet.ships | |
for i in range(0, len(self.target.ships)): | |
combined_ships[i] += self.target.ships[i] | |
return (origin_planet.ships[0] > 0 or origin_planet.ships[1] > 0 or origin_planet.ships[2] > 0 ) and origin_planet.dist(self.target) < (self.eta - current_round) and self.battle(self.ships, combined_ships) | |
def will_conquer_target(self): | |
return sum(self.battle(self.ships, self.target.ships)[1]) == 0 | |
@staticmethod | |
def battle(s1,s2): | |
ships1 = s1[::] | |
ships2 = s2[::] | |
while sum(ships1) > 0 and sum(ships2) >0: | |
new1 = Fleet.battle_round(ships2,ships1) | |
ships2 = Fleet.battle_round(ships1,ships2) | |
ships1 = new1 | |
#print ships1,ships2 | |
ships1 = arith_list(map(int,ships1)) | |
ships2 = arith_list(map(int,ships2)) | |
#print ships1,ships2 | |
return ships1, ships2 | |
@staticmethod | |
def battle_round(attacker,defender): | |
#nur eine asymmetrische runde. das hier muss mal also zweimal aufrufen. | |
numships = len(attacker) | |
defender = defender[::] | |
for def_type in range(0,numships): | |
for att_type in range(0,numships): | |
multiplier = 0.1 | |
absolute = 1 | |
if (def_type-att_type)%numships == 1: | |
multiplier = 0.25 | |
absolute = 2 | |
if (def_type-att_type)%numships == numships-1: | |
multiplier = 0.01 | |
defender[def_type] -= (attacker[att_type]*multiplier) + (attacker[att_type] > 0) * absolute | |
defender[def_type] = max(0,defender[def_type]) | |
return defender | |
class State: | |
def __init__(self, json): | |
self.planets = [Planet(p) for p in json['planets']] | |
self.fleets = [Fleet(p, self.planets) for p in json['fleets']] | |
for f in self.fleets: | |
f.target.add_incoming_fleet(f) | |
self.player_id = json['player_id'] | |
self.round = json['round'] | |
def my(self, thing): | |
return thing.player_id == self.player_id | |
@property | |
def my_planets(self): | |
return [p for p in self.planets if self.my(p)] | |
@property | |
def neutral_planets(self): | |
return [p for p in self.planets if p.player_id == 0] | |
@property | |
def enemy_planets(self): | |
return [p for p in self.planets if p.player_id != 0 and not self.my(p)] | |
def play(user, password, ai): | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
s.connect(('spacegoo.gpn.entropia.de', 6000)) | |
io = s.makefile('rw') | |
def write(data): | |
io.write('%s\n' % (data,)) | |
io.flush() | |
write('login %s %s' % (user, password)) | |
elo_diff = 0 | |
while True: | |
data = io.readline().strip() | |
if not data: | |
return | |
if data[0] == "{": | |
state = json.loads(data) | |
if state['winner'] is not None: | |
break | |
s = State(state) | |
s.game_id = game_id | |
write(ai(s)) | |
print("\r[{}/{}] {} - {} - {} ".format(state['round'], state['max_rounds'], | |
sum([sum(planet.ships) for planet in s.my_planets]) + sum([sum(fleet.ships) for fleet in s.fleets if | |
fleet.player_id == s.player_id]), | |
sum([sum(planet.ships) for planet in s.neutral_planets]), | |
sum([sum(planet.ships) for planet in s.enemy_planets]) + sum([sum(fleet.ships) for fleet in s.fleets if | |
not fleet.player_id == s.player_id])), | |
end='') | |
elif re.match('command received|welcome|calculating|waiting for', data): | |
pass | |
elif re.match('game ended', data): | |
print() | |
msg = data.replace('player 1', 'YOU' if s.player_id == 1 else 'enemy') | |
msg = msg.replace('player 2', 'YOU' if s.player_id == 2 else 'enemy') | |
print(msg) | |
m = re.search('(-?\\d+(\\.\\d+)?) ELO', data) | |
return float(m.group(1)) if m else None | |
elif re.match('game (\\d+) starts', data): | |
m = re.match('game (\\d+) starts', data) | |
game_id = m.group(1) | |
print(data) | |
else: | |
print(data) |
This file contains 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
#! /usr/bin/env python3 | |
from client import * | |
import time | |
import os | |
# ==== Versionsverwaltung mit vim ==== | |
# 1. Commit: Kommentar im Header hinzufügen | |
# 2. Revert: Im vim-Undo-Tree nach dem Change suchen, der den Kommentar hunzugefügt hat | |
# 3. Profit | |
# 1.0!!!! | |
# 2.0!!!! | |
# 530!!!! | |
# 644! | |
# 700. | |
USERNAME = "khaderp" | |
PASSWORD = "balloonicorn" | |
f = None | |
def log(*args): | |
print(*args, file=f) | |
def maxby(xs): | |
m = (None,None) | |
for x in xs: | |
if m[0] is None or x[0] > m[0]: | |
m = x | |
return m[1] | |
def simulate(state, p, add, ships=None): | |
""" | |
Simulate production of and fights on 'p' including all incoming | |
fleets plus all fleets in 'add'. | |
Returns player's win to loss in ships minus the enemy's ship delta. | |
""" | |
incoming =\ | |
[(f.eta, f.ships, f.player_id) for f in p.incoming_fleets] +\ | |
[(state.round + q.dist(p), ships, state.player_id) for q, ships in add] | |
incoming.sort(key=lambda f: f[0]) | |
incoming.append((state.round + 50, None, None)) | |
now = state.round | |
owner = p.player_id | |
ships = ships or p.ships | |
delta = [0, 0, 0] | |
for eta, iships, iowner in incoming: | |
if owner != 0: | |
ships = ships + (eta - now) * p.production | |
delta[owner] += sum((eta - now) * p.production) | |
if iowner == None: | |
break | |
if owner == iowner: | |
ships = ships + iships | |
else: | |
ships2, iships2 = Fleet.battle(ships, iships) | |
delta[owner] -= sum(ships) - sum(ships2) | |
delta[iowner] -= sum(iships) - sum(iships2) | |
if sum(ships2) == 0: | |
ships = iships2 | |
owner = iowner | |
else: | |
ships = ships2 | |
now = eta | |
#log(delta) | |
return delta[state.player_id] - delta[1 if state.player_id == 2 else 2] | |
def shipments(ships): | |
""" Try some combinations of ships to be sent to attack """ | |
r = [0.0, 1.0] | |
for a in r: | |
for b in r: | |
for c in r: | |
yield arith_list([a* ships[0], b* ships[1], c* ships[2]]) | |
def round(state): | |
#global f | |
#if not os.path.exists('logs/{}'.format(state.game_id)): | |
# try: | |
# os.mkdir('logs/{}'.format(state.game_id)) | |
# except: pass | |
#f = open('logs/{}/{}'.format(state.game_id, state.round), 'w') | |
my_fleet = \ | |
sum([sum(planet.ships) for planet in state.my_planets]) + sum([sum(fleet.ships) for fleet in state.fleets if | |
fleet.player_id == state.player_id]) | |
your_fleet = \ | |
(sum([sum(planet.ships) for planet in state.enemy_planets]) + sum([sum(fleet.ships) for fleet in state.fleets if not fleet.player_id == state.player_id])) | |
def scores(): | |
""" for each possible move compute the score difference between executing it and nopping """ | |
old_ds = [(simulate(state, p, []), p) for p in state.my_planets] | |
old_as = [(simulate(state, q, []), q) for q in state.planets] | |
for old_d, p in old_ds: | |
for old_a, q in old_as: | |
if q != p: | |
#log(p, q) | |
#old_d = simulate(state, p, []) | |
#old_a = simulate(state, q, []) | |
for ships in shipments(p.ships): | |
new_d = simulate(state, p, [], p.ships - ships) | |
new_a = simulate(state, q, [(p, ships)]) | |
score = new_a - old_a + new_d - old_d | |
yield score, (p, q, ships) | |
sc = list(scores()) | |
#log("I AM", state.player_id) | |
#for s in sorted(sc, key=lambda s:s[0]): | |
#log(s) | |
if not sc: | |
#log("nop") | |
return "nop" | |
source, target, ships = maxby(sc) | |
#log("old_d:", simulate(state, source, [])) | |
#log("old_a:", simulate(state, target, [])) | |
#log("new_d:", simulate(state, source, [], source.ships - ships)) | |
#log("new_a:", simulate(state, target, [(source, ships)])) | |
#log(source.flyto(target, ships)) | |
return source.flyto(target, ships) | |
elo_diff = 0 | |
while True: | |
d2 = play(USERNAME, PASSWORD, round) | |
if d2: | |
elo_diff += d2 | |
print("elo_diff: {:.2f}".format(elo_diff)) | |
time.sleep(3) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment