-
Notifications
You must be signed in to change notification settings - Fork 0
/
makePlan.py
executable file
·298 lines (254 loc) · 10.5 KB
/
makePlan.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
Ingress Maxfield - makePlan.py
usage: makePlan.py [-h] [-v] [-n NUM_AGENTS] [-s SAMPLES]
input_file
Ingress Maxfield - Maximize the number of links and fields, and thus AP, for a
collection of portals in the game Ingress.
positional arguments:
input_file Input semi-colon delimited portal file
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-n NUM_AGENTS, --num_agents NUM_AGENTS
Number of agents. Default: 1
-s SAMPLES, --samples SAMPLES
Number of iterations to perform. More iterations may
improve results, but will take longer to process.
Default: 50
Original version by jpeterbaker
22 July 2014 - tvw updates csv file format
15 August 2014 - tvw updates with google API, adds -s,
switchted to ; delimited file
29 Sept 2014 - tvw V2.0 major update to new version
21 April 2015 - tvw V2.1 force data read to be string
19 Dec 2015 - gbs V3.0 Python 3 compatibility enhancements, color output Resistance Blue
"""
import sys
import os
import argparse
import networkx as nx
import numpy as np
import pandas as pd
from lib import maxfield,PlanPrinterMap,geometry,agentOrder
import pickle
import matplotlib.pyplot as plt
try:
unicode = unicode
except NameError:
# 'unicode' is undefined, must be Python 3
str = str
unicode = str
bytes = bytes
basestring = (str,bytes)
else:
# 'unicode' exists, must be Python 2
str = str
unicode = unicode
bytes = str
basestring = basestring
# version number
_V_ = '3.0'
# max portals allowed
_MAX_PORTALS_ = 1000
def main():
description=("Ingress Maxfield - Maximize the number of links "
"and fields, and thus AP, for a collection of "
"portals in the game Ingress.")
parser = argparse.ArgumentParser(description=description,
prog="makePlan.py")
parser.add_argument('-v','--version',action='version',
version="Ingress Maxfield v{0}".format(_V_))
parser.add_argument('-g','--google',action='store_true',
help='Make maps with google maps API. Default: False')
parser.add_argument('-a','--api_key',default=None,
help='Google API key for Google maps. Default: None')
parser.add_argument('-n','--num_agents',type=int,default='1',
help='Number of agents. Default: 1')
parser.add_argument('-s','--samples',type=int,default=50,
help="Number of iterations to "
"perform. More iterations may improve "
"results, but will take longer to process. "
"Default: 50")
parser.add_argument('input_file',
help="Input semi-colon delimited portal file")
args = vars(parser.parse_args())
# Number of iterations to complete since last improvement
EXTRA_SAMPLES = args["samples"]
GREEN = '#3BF256' # Actual faction text colors in the app
BLUE = '#2ABBFF'
# Use google?
useGoogle = True
api_key = args['api_key']
input_file = args['input_file']
output_directory = os.path.expanduser('~') + "/Ingress/Fielding/{}".format(os.path.split(args['input_file'])[1][:-4])
print(output_directory)
# add ending separator
if output_directory[-1] != os.sep:
output_directory += os.sep
# create directory if doesn't exist
if not os.path.exists(output_directory):
os.makedirs(output_directory)
output_file = (os.path.split(args['input_file'])[1][:-4])
print(output_file)
if output_file[-4:] != '.pkl':
output_file += ".pkl"
nagents = args["num_agents"]
if nagents < 0:
sys.exit("Number of agents should be positive")
EXTRA_SAMPLES = args["samples"]
if EXTRA_SAMPLES < 0:
sys.exit("Number of extra samples should be positive")
elif EXTRA_SAMPLES > 100:
sys.exit("Extra samples may not be more than 100")
if input_file[-3:] != 'pkl':
# If the input file is a portal list, let's set things up
a = nx.DiGraph() # network tool
locs = [] # portal coordinates
# each line should be name;intel_link;keys
portals = pd.read_table(input_file,sep=';',
comment='#',index_col=False,
names=['name','link','keys'],dtype=str)
portals = np.array(portals)
portals = np.array([portal for portal in portals if (isinstance(portal[0], basestring) and isinstance(portal[1], basestring))])
print ("Found {0} portals in portal list.".format(len(portals)))
if len(portals) < 3:
sys.exit("Error: Must have more than 2 portals!")
if len(portals) > _MAX_PORTALS_:
sys.exit("Error: Portal limit is {0}".\
format(_MAX_PORTALS_))
for num,portal in enumerate(portals):
if len(portal) < 3:
print ("Error! Portal ",portal[0]," has a formatting problem.")
sys.exit()
a.add_node(num)
a.node[num]['name'] = portal[0]
coords = (portal[1].split('pll='))
if len(coords) < 2:
print ("Error! Portal ",portal[0]," has a formatting problem.")
sys.exit()
coord_parts = coords[1].split(',')
lat = int(float(coord_parts[0]) * 1.e6)
lon = int(float(coord_parts[1]) * 1.e6)
locs.append(np.array([lat,lon],dtype=float))
try:
keys = int(portal[2])
a.node[num]['keys'] = keys
except ValueError:
a.node[num]['keys'] = 0
n = a.order() # number of nodes
locs = np.array(locs,dtype=float)
# Convert coords to radians, then to cartesian, then to
# gnomonic projection
locs = geometry.e6LLtoRads(locs)
xyz = geometry.radstoxyz(locs)
xy = geometry.gnomonicProj(locs,xyz)
for i in range(n):
a.node[i]['geo'] = locs[i]
a.node[i]['xyz'] = xyz[i]
a.node[i]['xy' ] = xy[i]
# EXTRA_SAMPLES attempts to get graph with few missing keys
# Try to minimuze TK + 2*MK where
# TK is the total number of missing keys
# MK is the maximum number of missing keys for any single
# portal
bestgraph = None
bestlack = np.inf
bestTK = np.inf
bestMK = np.inf
allTK = []
allMK = []
allWeights = []
sinceImprove = 0
while sinceImprove<EXTRA_SAMPLES:
b = a.copy()
sinceImprove += 1
if not maxfield.maxFields(b):
print ('Randomization failure\nThe program may work if you try again. It is more likely to work if you remove some portals.')
continue
TK = 0
MK = 0
for j in range(n):
keylack = max(b.in_degree(j)-b.node[j]['keys'],0)
TK += keylack
if keylack > MK:
MK = keylack
weightedlack = TK+2*MK
allTK.append(TK)
allMK.append(MK)
allWeights.append(weightedlack)
if weightedlack < bestlack:
sinceImprove = 0
print ('IMPROVEMENT:\n\ttotal: %s\n\tmax: %s\n\tweighted: %s'%\
(TK,MK,weightedlack))
bestgraph = b
bestlack = weightedlack
bestTK = TK
bestMK = MK
else:
print ('this time:\n\ttotal: %s\n\tmax: %s\n\tweighted: %s'%\
(TK,MK,weightedlack))
if weightedlack <= 0:
print ('KEY PERFECTION')
bestlack = weightedlack
bestTK = TK
bestMK = MK
break
# if num agent keys is zero, this code isn't true...
# if all([ b.node[i]['keys'] <= b.out_degree(i) for i in xrange(n) ]):
# print 'All keys used. Improvement impossible'
# break
print ('%s tries since improvement'%sinceImprove)
if bestgraph == None:
print ('EXITING RANDOMIZATION LOOP WITHOUT SOLUTION!')
print ('')
exit()
print ('Choosing plan requiring %s additional keys, max of %s from single portal'%(bestTK,bestMK))
plt.clf()
plt.scatter(allTK,allMK,c=allWeights,marker='o')
plt.xlim(min(allTK)-1,max(allTK)+1)
plt.ylim(min(allMK)-1,max(allMK)+1)
plt.xlabel('Total keys required')
plt.ylabel('Max keys required for a single portal')
cbar = plt.colorbar()
cbar.set_label('Optimization Weighting (lower=better)')
plt.savefig(output_directory+'optimization.png')
a = bestgraph
# Attach to each edge a list of fields that it completes
# catch no triangulation (bad portal file?)
try:
for t in a.triangulation:
t.markEdgesWithFields()
except AttributeError:
print ("Error: problem with bestgraph... no triangulation...?")
agentOrder.improveEdgeOrder(a)
with open(output_directory+output_file,'wb') as fout:
pickle.dump(a,fout)
else:
with open(input_file,'r') as fin:
a = pickle.load(fin)
# agentOrder.improveEdgeOrder(a)
# with open(output_directory+output_file,'w') as fout:
# pickle.dump(a,fout)
PP = PlanPrinterMap.PlanPrinter(a,output_directory,nagents,color=BLUE,useGoogle=useGoogle,
api_key=api_key)
PP.keyPrep()
PP.agentKeys()
PP.planMap(useGoogle=useGoogle)
PP.agentLinks()
# These make step-by-step instructional images
PP.animate(useGoogle=useGoogle)
PP.split3instruct(useGoogle=useGoogle)
print ("Number of portals: {0}".format(PP.num_portals))
print ("Number of links: {0}".format(PP.num_links))
print ("Number of fields: {0}".format(PP.num_fields))
portal_ap = (125*8 + 500 + 250)*PP.num_portals
link_ap = 313 * PP.num_links
field_ap = 1250 * PP.num_fields
print ("AP from portals capture: {0}".format(portal_ap))
print ("AP from link creation: {0}".format(link_ap))
print ("AP from field creation: {0}".format(field_ap))
print ("Total AP: {0}".format(portal_ap+link_ap+field_ap))
if __name__ == "__main__":
main()