Passed
Pull Request — master (#185)
by Marek
01:36
created

ige.ospace.GalaxyGenerator.Test.__init__()   B

Complexity

Conditions 1

Size

Total Lines 28
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 22
nop 1
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Outer Space.
5
#
6
#  Outer Space is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Outer Space is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  You should have received a copy of the GNU General Public License
17
#  along with Outer Space; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
21
import math
22
import os
23
import random
24
import sys
25
import tempfile
26
27
import data
28
29
import ige.ospace.Const as Const
30
31
class GalaxyTemplate(object):
32
    def __init__(self):
33
        self.galaxyType = self.__class__.__name__
34
        self.scenario = Const.SCENARIO_NONE
35
        self.minPlanets = 0
36
        self.maxPlanets = 0
37
        self.startR = (0.0, 999.0)
38
        self.players = 0
39
        self.playerGroup = 0
40
        self.groupDist = 0
41
        self.minR = 0
42
        # format {maxRadius: density, nextCircleRadius: differentDensity}
43
        self.density = {1: 1, 2: 2}
44
        self.minSystemLoneliness = 1.5
45
        self.maxSystemLoneliness = 5
46
        self.resources = {
47
            # format resourceID : [(minDist, maxDist, number of resources)]
48
            #Const.SR_TL1A : [(0, 0, 0)]
49
        }
50
        self.diseases = {
51
            # format diseaseID : (minDist, maxDist, number of diseases)
52
            #Const.DISEASE_MUTANT : [(0, 0, 0)]
53
        }
54
55
    @property
56
    def center(self):
57
        return (self.radius, self.radius)
58
59
    @property
60
    def radius(self):
61
        # making radius a bit bigger, as that is used in minimaps and chronicler
62
        return max(self.density.keys()) + 2
63
64 View Code Duplication
class Circle1SP(GalaxyTemplate):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
65
    def __init__(self):
66
        super(Circle1SP, self).__init__()
67
68
        self.galaxyDescription = "Single player galaxy to enjoy freebuilding. Mutant is the only enemy. Endless game."
69
        self.scenario = Const.SCENARIO_SINGLE
70
        self.minPlanets = 100
71
        self.maxPlanets = 150
72
        self.startR = (9.0, 11.0)
73
        self.players = 1
74
        self.playerGroup = 1
75
        self.groupDist = 0
76
        self.minR = 2
77
        # format {minRadius: density, nextCircleRadius: differentDensity}
78
        self.density = {2: 4, 5: 4, 12: 4.5}
79
        self.resources = {
80
            # format resourceID : [(minDist, maxDist, number of resources)]
81
            Const.SR_TL1A : [(11, 13, 2)],
82
            Const.SR_TL1B : [(11, 13, 2)]
83
        }
84
        self.diseases = {
85
            # format diseaseID : (minDist, maxDist, number of diseases)
86
            Const.DISEASE_MUTANT : [(2, 5, 3)]
87
        }
88
89
90
class Circle3BP(Circle1SP):
91
    def __init__(self):
92
        super(Circle3BP, self).__init__()
93
94
        #self.galaxyDescription = "Tiny galaxy to brawl with two other commanders. No voting, no imperator. Conquest is the only solution."
95
        self.minPlanets *= 1.3
96
        self.maxPlanets *= 1.2
97
        self.scenario = Const.SCENARIO_BRAWL
98
        self.startR = (0.0, 0.0)
99
        self.players = 3
100
        self.playerGroup = 3
101
        self.groupDist = 10
102
        self.resources = {
103
            # format resourceID : [(minDist, maxDist, number of resources)]
104
            Const.SR_TL1A : [(11, 13, 3)],
105
            Const.SR_TL1B : [(11, 13, 3)]
106
        }
107
        self.diseases = {}
108
109
110 View Code Duplication
class Circle3SP(GalaxyTemplate):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
111
    def __init__(self):
112
        super(Circle3SP, self).__init__()
113
114
        #self.galaxyDescription = "More complex single player galaxy with classic starting group of three. Mutant is the only agressive enemy. Endless game."
115
        self.scenario = Const.SCENARIO_SINGLE
116
        self.minPlanets = 350
117
        self.maxPlanets = 400
118
        self.startR = (15.0, 17.0)
119
        self.players = 3
120
        self.playerGroup = 3
121
        self.groupDist = 3
122
        self.minR = 3
123
        # format {minRadius: density, nextCircleRadius: differentDensity}
124
        self.density = {3: 3.5, 5: 4.5, 10: 4.5, 20: 4.5, 25: 5}
125
        self.resources = {
126
            # format resourceID : [(minDist, maxDist, number of resources)]
127
            Const.SR_TL1A : [(15, 17, 3)],
128
            Const.SR_TL1B : [(15, 17, 3)]
129
        }
130
        self.diseases = {
131
            # format diseaseID : (minDist, maxDist, number of diseases)
132
            Const.DISEASE_MUTANT : [(2, 4, 3)]
133
        }
134
135
136
class Circle3CP(Circle3SP):
137
    def __init__(self):
138
        super(Circle3CP, self).__init__()
139
140
        #self.galaxyDescription = "Cooperative galaxy, where you and two other commanders fend of and defeat sprawling mutant menace. Cooperation is not enforced, but recommended. Galaxy ends when mutant player cease to exist."
141
        self.scenario = Const.SCENARIO_COOP
142
        self.diseases = {
143
            # format diseaseID : (minDist, maxDist, number of diseases)
144
            Const.DISEASE_MUTANT : [(2, 4, 5)]
145
        }
146
147
148
class Circle5BP(Circle3SP):
149
    def __init__(self):
150
        super(Circle5BP, self).__init__()
151
152
        #self.galaxyDescription = "Small galaxy to brawl with four other commanders. No voting, no imperator. Conquest is the only solution."
153
        self.scenario = Const.SCENARIO_BRAWL
154
        self.startR = (0.0, 0.0)
155
        self.players = 5
156
        self.playerGroup = 5
157
        self.groupDist = 10
158
        self.resources = {
159
            # format resourceID : [(minDist, maxDist, number of resources)]
160
            Const.SR_TL1A : [(15, 17, 5)],
161
            Const.SR_TL1B : [(15, 17, 5)]
162
        }
163
        self.diseases = {}
164
165
166 View Code Duplication
class Circle9P(GalaxyTemplate):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
167
    def __init__(self):
168
        super(Circle9P, self).__init__()
169
170
        #self.galaxyDescription = "Smaller galaxy for 9 players, without pirate or EDEN. Recommended for new players or those who seek more casual gameplay."
171
        self.scenario = Const.SCENARIO_OUTERSPACE
172
        self.minPlanets = 500
173
        self.maxPlanets = 600
174
        self.startR = (15.0, 18.0)
175
        self.players = 9
176
        self.playerGroup = 3
177
        self.groupDist = 4
178
        self.minR = 5
179
        # format {minRadius: density, nextCircleRadius: differentDensity}
180
        self.density = {5: 4, 10: 4.5, 20: 4.5, 26: 5}
181
        self.resources = {
182
            # format resourceID : [(minDist, maxDist, number of resources)]
183
            Const.SR_TL1A : [(20, 25, 6)],
184
            Const.SR_TL1B : [(20, 25, 6)]
185
        }
186
        self.diseases = {
187
            # format diseaseID : (minDist, maxDist, number of diseases)
188
            Const.DISEASE_MUTANT : [(13, 20, 6), (5, 9, 6)]
189
        }
190
191 View Code Duplication
class Circle42P(GalaxyTemplate):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
192
    def __init__(self):
193
        super(Circle42P, self).__init__()
194
195
        #self.galaxyDescription = "Original size galaxy for 42 players, place of epic battles, recommended only to the experienced players. May become time consuming."
196
        self.scenario = Const.SCENARIO_OUTERSPACE
197
        self.minPlanets = 1500
198
        self.maxPlanets = 1800
199
        self.startR = (32.0, 36.0)
200
        self.players = 42
201
        self.playerGroup = 3
202
        self.groupDist = 4
203
        self.minR = 7.5
204
        # format {minRadius: density, nextCircleRadius: differentDensity}
205
        self.density = {7.5: 3, 10: 4, 20: 5, 30: 5.5, 40: 6, 50: 6}
206
        self.resources = {
207
            # format resourceID : [(minDist, maxDist, number of resources)]
208
            Const.SR_TL1A : [(20, 45, 15)],
209
            Const.SR_TL1B : [(20, 45, 15)],
210
            Const.SR_TL3A : [(8, 15, 7)],
211
            Const.SR_TL3B : [(8, 15, 7)],
212
            Const.SR_TL3C : [(8, 15, 7)],
213
            Const.SR_TL5A : [(7.5, 9, 1)],
214
            Const.SR_TL5B : [(7.5, 9, 1)],
215
            Const.SR_TL5C : [(7.5, 9, 1)]
216
        }
217
        self.diseases = {
218
            # format diseaseID : (minDist, maxDist, number of diseases)
219
            Const.DISEASE_MUTANT : [(20, 45, 16), (5, 15, 12), (0, 5, 3)]
220
        }
221
222 View Code Duplication
class Circle65P(GalaxyTemplate):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
223
    def __init__(self):
224
        super(Circle65P, self).__init__()
225
226
        #self.galaxyDescription = "Majestic galaxy of unmatched size. Be prepared to work through diplomacy, as managing huge empire a conqueror needs would take all your time. Only for veteran players of many galaxies."
227
        self.scenario = Const.SCENARIO_OUTERSPACE
228
        self.minPlanets = 3200
229
        self.maxPlanets = 3500
230
        self.startR = (45.0, 52.5)
231
        self.players = 65
232
        self.playerGroup = 5
233
        self.groupDist = 8
234
        self.minR = 7.5
235
        # format {minRadius: density, nextCircleRadius: differentDensity}
236
        self.density = {7.5: 3, 10: 4, 20: 5, 30: 5.5, 60: 6, 75: 6}
237
        self.resources = {
238
            # format resourceID : [(minDist, maxDist, number of resources)]
239
            Const.SR_TL1A : [(20, 67.5, 45)],
240
            Const.SR_TL1B : [(20, 67.5, 45)],
241
            Const.SR_TL3A : [(10, 20, 10)],
242
            Const.SR_TL3B : [(10, 20, 10)],
243
            Const.SR_TL3C : [(10, 20, 10)],
244
            Const.SR_TL5A : [(7.5, 9, 2)],
245
            Const.SR_TL5B : [(7.5, 9, 2)],
246
            Const.SR_TL5C : [(7.5, 9, 2)]
247
        }
248
        self.diseases = {
249
            # format diseaseID : (minDist, maxDist, number of diseases)
250
            Const.DISEASE_MUTANT : [(20, 67.5, 32), (5, 15, 18), (0, 5, 6)]
251
        }
252
253
class Test(GalaxyTemplate):
254
    def __init__(self):
255
        super(Test, self).__init__()
256
257
        #self.galaxyDescription = "Original size galaxy for 42 players, place of epic battles, recommended only to the experienced players. May become time consuming."
258
        self.scenario = Const.SCENARIO_OUTERSPACE
259
        self.minPlanets = 0
260
        self.maxPlanets = 200
261
        self.startR = (1.0, 7.0)
262
        self.players = 1
263
        self.playerGroup = 1
264
        self.groupDist = 1
265
        self.minR = 1
266
        # format {minRadius: density, nextCircleRadius: differentDensity}
267
        self.density = {1:1, 7: 2}
268
        self.resources = {
269
            # format resourceID : [(minDist, maxDist, number of resources)]
270
            Const.SR_TL1A : [(1, 7, 1)],
271
            Const.SR_TL1B : [(1, 7, 1)],
272
            Const.SR_TL3A : [(1, 7, 1)],
273
            Const.SR_TL3B : [(1, 7, 1)],
274
            Const.SR_TL3C : [(1, 7, 1)],
275
            Const.SR_TL5A : [(1, 7, 1)],
276
            Const.SR_TL5B : [(1, 7, 1)],
277
            Const.SR_TL5C : [(1, 7, 1)]
278
        }
279
        self.diseases = {
280
            # format diseaseID : (minDist, maxDist, number of diseases)
281
            Const.DISEASE_MUTANT : [(1, 7, 2)]
282
        }
283
284
class GalaxyGenerator:
285
    def __init__(self):
286
        self.templates = {}
287
        # TODO: I guess we can autodetect this somehow, in a future
288
        for templateClass in [Circle1SP, Circle3BP, Circle3SP, Circle3CP, Circle5BP, Circle9P, Circle42P, Circle65P, Test]:
289
            templateInstance = templateClass()
290
            self.templates[templateInstance.galaxyType] = templateInstance
291
292
    def generateGalaxy(self, galaxyType):
293
        if not galaxyType in self.templates:
294
            return False
295
        while True:
296
            try:
297
                galaxy = generateGalaxy2(self.templates[galaxyType])
298
                break
299
            except IndexError:
300
                # this happens, if generator fails to place special
301
                # planet - easier than handling it inside is to roll
302
                # dice again
303
                continue
304
        self.shiftSystems(galaxy)
305
306
        return self.saveGalaxy(galaxy)
307
308
    def getGalaxyTypes(self):
309
        return self.templates.keys()
310
311
    def getGalaxyTemplate(self, galaxyType):
312
        return self.templates[galaxyType]
313
314
    def saveGalaxy(self, galaxy):
315
        """ saving galaxy - instance of Galaxy object into xml-formated file
316
317
            returns file name
318
        """
319
        # names
320
        loadSystemNames()
321
        fileHandle, galaxyFileName = tempfile.mkstemp(text = True)
322
        fh = os.fdopen(fileHandle, "w")
323
        # save
324
        print >>fh, '<?xml version="1.0" encoding="UTF-8"?>'
325
        print >>fh, '<universe>'
326
        print >>fh, '\t<galaxy galaxyType="%s" x="%.2f" y="%.2f">' % (
327
            galaxy.galaxyType, galaxy.centerX, galaxy.centerY
328
        )
329
        print >>fh, '\t\t<properties radius="%.2f" scenario="%s"/>' % (galaxy.radius, galaxy.scenario)
330
        for system in galaxy.systems:
331
            self.saveSystem(fh, system)
332
        print >>fh, '\t</galaxy>'
333
        print >>fh, '</universe>'
334
        fh.close()
335
        return galaxyFileName
336
337
    def saveSystem(self, fh, system):
338
        print >>fh, '\t\t<system x="%.2f" y="%.2f">' % (system.x, system.y)
339
        # name = 'SCN-%04d%04d' % (system.x * 10, system.y * 10)
340
        global systemNames
341
        name = random.choice(systemNames)
342
        systemNames.remove(name)
343
        print >>fh, '\t\t\t<properties starClass="%s%d" name="%s"/>' % \
344
            (system.starClass, system.starSubclass, name)
345
        for planet in system.planets:
346
            self.savePlanet(fh, planet)
347
        print >>fh, '\t\t</system>'
348
349
    def savePlanet(self, fh, planet):
350
        print >>fh, '\t\t\t<planet>'
351
        print >>fh, '\t\t\t\t<properties plType="%s" plMin="%d" plBio="%d" plEn="%d" plDiameter="%d" plSlots="%d" plMaxSlots="%d" plStratRes="%d" plDisease="%d" plStarting="%d"/>' % \
352
            (planet.type, planet.minerals, planet.environ, planet.energy, planet.diameter, planet.slots, planet.maxSlots, planet.strategicRes, planet.disease, planet.starting)
353
        if planet.starting:
354
            print >>fh, '\t\t\t\t<startingpoint/>'
355
        print >>fh, '\t\t\t</planet>'
356
357
    def shiftSystems(self, galaxy):
358
        """ makes sure no two systems are closer than _min and there are
359
            no systems further than _max parsecs from their closest three neighbours).
360
361
            Quite naive implementation."""
362
        MAX_STEP = 25
363
        DELTA = 0.25
364
        galaxyTemplate = self.templates[galaxy.galaxyType]
365
        _min = galaxyTemplate.minSystemLoneliness
366
        _max = galaxyTemplate.maxSystemLoneliness
367
        for i in xrange(MAX_STEP):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
368
            newMin, newMax = self._shiftSystems(galaxy, _min, _max, DELTA)
369
            if newMin >= _min and newMax <= _max:
370
                break
371
372
    def _shiftSystems(self, galaxy, _min, _max, delta):
373
        # _min and _max are squared, so dist frequently calculated later
374
        # doesn't have to be square rooted
375
        print 'Shifting...'
376
        _min = _min * _min
377
        _max = _max * _max
378
        minMinDist = 1000000
379
        maxMinDist = 0
380
        for system1 in galaxy.systems:
381
            if not system1._moveable:
382
                continue
383
            minDist = [1000000, 100000, 100000]
384
            closestSystems = [None, None, None]
385
            for system2 in galaxy.systems:
386
                if system1 == system2 or not system2._moveable:
387
                    continue
388
                dist = (system1.x - system2.x) ** 2 + (system1.y - system2.y) ** 2
389
                if dist < minDist[0]:
390
                    minDist.pop()
391
                    minDist.insert(0, dist)
392
                    closestSystems.pop()
393
                    closestSystems.insert(0, system2)
394
                elif dist < minDist[1]:
395
                    minDist.pop()
396
                    minDist.insert(1, dist)
397
                    closestSystems.pop()
398
                    closestSystems.insert(1, system2)
399
                elif dist < minDist[2]:
400
                    minDist.pop()
401
                    minDist.insert(2, dist)
402
                    closestSystems.pop()
403
                    closestSystems.insert(2, system2)
404
            system1._closest = closestSystems
405
            for closestSystem in closestSystems:
406
                if not closestSystem:
407
                    continue
408
                # this has to be calculated again, even though we had it before in minDist
409
                # because every move can alter others
410
                dist = (system1.x - closestSystem.x) ** 2 + (system1.y - closestSystem.y) ** 2
411
                if dist < _min and closestSystem:
412
                    # move system away
413
                    if system1.x > closestSystem.x:
414
                        system1.x += random.uniform(0, delta)
415
                        closestSystem.x -= random.uniform(0, delta)
416
                    else:
417
                        system1.x -= random.uniform(0, delta)
418
                        closestSystem.x += random.uniform(0, delta)
419
                    if system1.y > closestSystem.y:
420
                        system1.y += random.uniform(0, delta)
421
                        closestSystem.y -= random.uniform(0, delta)
422
                    else:
423
                        system1.y -= random.uniform(0, delta)
424
                        closestSystem.y += random.uniform(0, delta)
425
                elif dist > _max and closestSystem:
426
                    # move systems closer
427
                    if system1.x < closestSystem.x:
428
                        system1.x += random.uniform(0, delta)
429
                        closestSystem.x -= random.uniform(0, delta)
430
                    else:
431
                        system1.x -= random.uniform(0, delta)
432
                        closestSystem.x += random.uniform(0, delta)
433
                    if system1.y < closestSystem.y:
434
                        system1.y += random.uniform(0, delta)
435
                        closestSystem.y -= random.uniform(0, delta)
436
                    else:
437
                        system1.y -= random.uniform(0, delta)
438
                        closestSystem.y += random.uniform(0, delta)
439
                if dist < minMinDist: minMinDist = dist
440
                if dist > maxMinDist: maxMinDist = dist
441
        print 'Finished [min. dist = <%.2f; %.2f>]' % (math.sqrt(minMinDist), math.sqrt(maxMinDist))
442
        return math.sqrt(minMinDist), math.sqrt(maxMinDist)
443
444
class Galaxy:
445
446
    def __init__(self):
447
        self.systems = []
448
        self.centerX = 0.0
449
        self.centerY = 0.0
450
        self.radius = 0.0
451
452
class System:
453
454
    def __init__(self):
455
        self.x = 0.0
456
        self.y = 0.0
457
        self.name = '?'
458
        self.compOf = None
459
        self.starClass = '?'
460
        self.starSubclass = 0
461
        self.planets = []
462
        self._closest = []
463
        self.hasSR = 0
464
        self.hasDisease = 0
465
        self._moveable = 1
466
467
class Planet:
468
469
    def __init__(self):
470
        self.compOf = None
471
        self.type = '?'
472
        self.diameter = 0
473
        self.minerals = 0
474
        self.environ = 0
475
        self.energy = 0
476
        self.slots = 0
477
        self.maxSlots = 0
478
        self.starting = 0
479
        self.strategicRes = 0
480
        self.disease = 0
481
482
def generateGalaxy2(galaxyTemplate):
483
    galaxy = Galaxy()
484
    stats = galaxyTemplate
485
    galaxy.galaxyType = stats.galaxyType
486
    galaxy.centerX, galaxy.centerY = stats.center
487
    galaxy.radius = stats.radius
488
    galaxy.scenario = stats.scenario
489
    r = stats.minR + random.uniform(0, 0.5)
490
    dkeys = stats.density.keys()
491
    dkeys.sort()
492
    dkeys.reverse()
493
    prevR = 5
494
    while r <= galaxy.radius:
495
        for key in dkeys:
496
            if key <= r:
497
                density = stats.density[key]
498
                break
499
        d = 2 * math.pi * r
500
        aoff = random.uniform(0, math.pi * 2)
501
        dangle = density / d * math.pi * 0.9
0 ignored issues
show
introduced by
The variable density does not seem to be defined for all execution paths.
Loading history...
502
        for i in range(0, int(d / density)):
503
            angle = aoff + i * density / d * math.pi * 2
504
            angle += random.uniform(-dangle, dangle)
505
            tr = random.uniform(prevR + 0.1, r)
506
            while 1:
507
                acceptable = 0
508
                system = System()
509
                generateSystem(system)
510
                # check requirements
511
                for planet in system.planets:
512
                    if planet.type in ('D', 'R', 'C', 'H', 'M', 'E') and \
513
                        planet.slots > 0:
514
                        acceptable = 1
515
                        break
516
                if acceptable:
517
                    break
518
            galaxy.systems.append(system)
519
            system.x = math.cos(angle) * tr + galaxy.centerX
520
            system.y = math.sin(angle) * tr + galaxy.centerY
521
            system.compOf = galaxy
522
            system.dist = tr
523
            system.angle = angle
524
        prevR = r
525
        r += random.uniform(2, 4)
526
    # generate central black hole
527
    system = System()
528
    system.x = galaxy.centerX
529
    system.y = galaxy.centerY
530
    system.starClass = "b-"
531
    system.starSubclass = 7
532
    system.compOf = galaxy
533
    system._moveable = 0
534
    galaxy.systems.append(system)
535
    # generate starting systems
536
    if stats.players:
537
        r = (stats.startR[0] + stats.startR[1]) / 2
538
        d = 2 * math.pi * r
539
        print "Player distance:", d / stats.players
540
        gaoff = random.uniform(0, math.pi * 2)
541
        for i in range(0, stats.players / stats.playerGroup):
542
            print "Placing group:", i + 1, "of", stats.players / stats.playerGroup
543
            angle = gaoff + i * math.pi * 2 / (stats.players / stats.playerGroup)
544
            tr = random.uniform(stats.startR[0], stats.startR[1])
545
            gx = math.cos(angle) * tr + galaxy.centerX
546
            gy = math.sin(angle) * tr + galaxy.centerY
547
            aoff = random.uniform(0, math.pi * 2)
548
            for j in range(0, stats.playerGroup):
549
                angle = aoff + j * math.pi * 2 / stats.playerGroup
550
                x = math.cos(angle) * stats.groupDist + gx
551
                y = math.sin(angle) * stats.groupDist + gy
552
                while 1:
553
                    system = System()
554
                    system.x = x
555
                    system.y = y
556
                    system.compOf = galaxy
557
                    generateSystem(system)
558
                    # check system properties
559
                    e = 0
560
                    h = 0
561
                    d = 0
562
                    ok = 1
563
                    for planet in system.planets:
564
                        if planet.type == 'E': e += 1; planet.starting = 1
565
                        elif planet.type in ('D', 'R', 'C'):
566
                            if planet.slots > 5: d += 1
567
                            else: ok = 0; break
568
                        elif planet.type == 'H': h += 1
569
                        elif planet.type == 'M': ok = 0; break
570
                    # fast rule
571
                    #if ok and e == 1:
572
                    #    break
573
                    # slow (better) rule
574
                    if ok and e == 1 and h == 1 and d == 1:
575
                        break
576
                galaxy.systems.append(system)
577
    # strategic resources
578
    keys = stats.resources.keys()
579
    keys.sort()
580
    keys.reverse()
581
    for key in keys:
582
      print "Placing resource", key
583
      for minR, maxR, count in stats.resources[key]:
584
        aoff = random.uniform(0, math.pi * 2)
585
        for i in range(0, count):
586
            angle = aoff + i * math.pi * 2 / count
587
            tr = random.uniform(minR, maxR)
588
            x = math.cos(angle) * tr + galaxy.centerX
589
            y = math.sin(angle) * tr + galaxy.centerY
590
            # find closest system
591
            closest = galaxy.systems[0]
592
            minDist = 99999 #(closest.x - x) ** 2 + (closest.y - y) ** 2
593
            for system in galaxy.systems:
594
                dist = (system.x - x) ** 2 + (system.y - y) ** 2
595
                if dist < minDist and system.hasSR == 0:
596
                    hasDRC = 0
597
                    starting = 0
598
                    # find suitable planet
599
                    for planet in system.planets:
600
                        if planet.starting:
601
                            starting = 1
602
                        if planet.type in ("D", "R", "C"):
603
                            hasDRC = 1
604
                    if not starting and hasDRC:
605
                        minDist = dist
606
                        closest = system
607
            print "    System", closest.x, closest.y, math.sqrt(minDist)
608
            # find planet on the closest system
609
            planets = []
610
            for planet in closest.planets:
611
                if planet.type in ("D", "R", "C"):
612
                    planets.append(planet)
613
            planet = random.choice(planets)
614
            # now make sure resources are placed on big enough planets
615
            # to promote more strategic and less tactical fights over them
616
            # and ensure some minimal barrier is there for player to
617
            # overcome
618
            planet.diameter = dice(1, 6, 12) * 1000
619
            planet.maxSlots = int(planet.diameter / 1000.)
620
            planet.slots = dice(1, 2, 7)
621
            planet.strategicRes = key
622
            system = planet.compOf
623
            system.hasSR = 1
624
            print "    Planet", planet.type
625
    # diseases
626
    keys = stats.diseases.keys()
627
    keys.sort()
628
    keys.reverse()
629
    for key in keys:
630
      print "Placing disease", key
631
      for minR, maxR, count in stats.diseases[key]:
632
        aoff = random.uniform(0, math.pi * 2)
633
        for i in range(0, count):
634
            angle = aoff + i * math.pi * 2 / count
635
            tr = random.uniform(minR, maxR)
636
            x = math.cos(angle) * tr + galaxy.centerX
637
            y = math.sin(angle) * tr + galaxy.centerY
638
            # find closest system
639
            closest = galaxy.systems[0]
640
            minDist = 99999 #(closest.x - x) ** 2 + (closest.y - y) ** 2
641
            for system in galaxy.systems:
642
                dist = (system.x - x) ** 2 + (system.y - y) ** 2
643
                if dist < minDist and system.hasDisease == 0:
644
                    hasHME = 0
645
                    starting = 0
646
                    # find suitable planet
647
                    for planet in system.planets:
648
                        if planet.starting:
649
                            starting = 1
650
                        if planet.type in ("M", "E"):
651
                            hasHME = 1
652
                    if not starting and hasHME:
653
                        minDist = dist
654
                        closest = system
655
            print "    System", closest.x, closest.y, math.sqrt(minDist)
656
            # find planet on the closest system
657
            planets = []
658
            for planet in closest.planets:
659
                if planet.type in ("M", "E"):
660
                    planets.append(planet)
661
            planet = random.choice(planets)
662
            planet.disease = key
663
            system = planet.compOf
664
            system.hasDisease = 1
665
            print "    Planet", planet.type
666
    # check if number of planets is in desired interval
667
    noOfPlanets = 0
668
    for system in galaxy.systems:
669
        noOfPlanets += len(system.planets)
670
    if noOfPlanets < stats.minPlanets or noOfPlanets > stats.maxPlanets:
671
        print 'There was {0} planets.\nStarting new generation...'.format(noOfPlanets)
672
        return generateGalaxy2(galaxyTemplate)
673
    else:
674
        return galaxy
675
676
def generateSystem(system, ranges = None):
677
    # system class and subclass
678
    # c -> supergiant
679
    # g -> giant
680
    # D -> dwarf
681
    # NS -> neutron star
682
    # BH -> black hole
683
    num = random.randrange(1, 1000000 + 1)
684
    system.starSubclass = random.randrange(0, 10)
685
    if   num < 10: system.starClass = 'cB'
686
    elif num < 20: system.starClass = 'cA'
687
    elif num < 40: system.starClass = 'cF'
688
    elif num < 60: system.starClass = 'cG'
689
    elif num < 80: system.starClass = 'cK'
690
    elif num < 100: system.starClass = 'cM'
691
    elif num < 500: system.starClass = 'gF'
692
    elif num < 1000: system.starClass = 'gG'
693
    elif num < 5500: system.starClass = 'gK'
694
    elif num < 10000: system.starClass = 'gM'
695
    elif num < 20000: system.starClass = 'mO'; system.starSubclass = random.randrange(5, 10)
696
    elif num < 30000: system.starClass = 'mB'
697
    elif num < 40000: system.starClass = 'mA'
698
    elif num < 120000: system.starClass = 'mF'
699
    elif num < 225000: system.starClass = 'mG'
700
    elif num < 465000: system.starClass = 'mK'
701
    elif num < 930000: system.starClass = 'mM'
702
    elif num < 940000: system.starClass = 'dB'
703
    elif num < 960000: system.starClass = 'dA'
704
    elif num < 980000: system.starClass = 'dF'
705
    elif num < 990000: system.starClass = 'dG'
706
    elif num < 999500: system.starClass = 'dK'
707
    elif num < 999995: system.starClass = 'n-'
708
    elif num < 1000000: system.starClass = 'b-'
709
    else: system.starClass = 'b-'
710
    # planets
711
    num = random.randrange(0, 100)
712
    planets = (0, 0, 0)
713
    mod = 1.0 / 2.0 # was 2 / 3
714
    if system.starClass[0] in ('c', 'g'):
715
        if num < 25:
716
            planets = distributePlanets(mod * random.randrange(1, 7))
717
    elif system.starClass[1] in ('O', 'B'):
718
        if num < 25:
719
            planets = distributePlanets(mod * random.randrange(1, 11))
720
    elif system.starClass[1] == 'A':
721
        if num < 75:
722
            planets = distributePlanets(mod * random.randrange(1, 11))
723
    elif system.starClass[1] == 'F' or system.starClass[1] == 'G':
724
        if num < 95:
725
            num = random.randrange(1, 7) + random.randrange(1, 7) + 3
726
            planets = distributePlanets(mod * num)
727
    elif system.starClass[1] == 'K':
728
        if num < 95:
729
            num = random.randrange(1, 7) + random.randrange(1, 7)
730
            planets = distributePlanets(mod * num)
731
    elif system.starClass[1] == 'M':
732
        if num < 95:
733
            num = random.randrange(1, 7)
734
            planets = distributePlanets(mod * num)
735
    elif system.starClass[0] == 'd':
736
        if num < 10:
737
            num = int(mod * random.randrange(1, 7) / 2)
738
            planets = (0, 0, num)
739
    elif system.starClass[0] == 'n' or system.starClass[0] == 'b':
740
        if num < 5:
741
            num = int(mod * random.randrange(1, 7) / 2)
742
            planets = (0, 0, num)
743
    # planets
744
    zone = 0
745
    for num in planets:
746
        for i in xrange(0, num):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
747
            planet = Planet()
748
            planet.compOf = system
749
            system.planets.append(planet)
750
            generatePlanet(zone, planet)
751
        zone += 1
752
    # sort planets by energy
753
    system.planets.sort(lambda a, b: cmp(b.energy, a.energy))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable cmp does not seem to be defined.
Loading history...
754
755
def distributePlanets(num):
756
    num = int(num)
757
    if num <= 3: return (0, 1, num - 1)
758
    elif num <= 5:    return (1, 1, num - 2)
759
    elif num <=7: return (1, 2, num - 3)
760
    elif num <=11: return (2, 2, num - 4)
761
    elif num <=15: return (2, 3, num - 5)
762
763
def generatePlanet(zone, planet):
764
    sc = planet.compOf.starClass
765
    if sc == 'mF' or sc == 'mG' or sc == 'mK': isFGK = 1
766
    else: isFGK = 0
767
    if sc[0] == 'd' or sc == 'n-' or sc == 'b-': isDNB = 1
768
    else: isDNB = 0
769
    # diameter and type of planet
770
    num = random.randrange(0, 100)
771
    if zone == 0: # Zone A
772
        if num < 5: planet.type = 'A'
773
        elif num < 10: planet.type = 'G'; planet.diameter = dice(3, 6, 0) * 10000
774
        elif num < 60: planet.type = 'R'; planet.diameter = dice(1, 10, 0) * 1000
775
        elif num < 70: planet.type = 'D'; planet.diameter = dice(2, 6, 2) * 1000
776
        elif num < 100: planet.type = 'H'; planet.diameter = dice(3, 6, 1) * 1000
777
    elif zone == 1: # Zone B
778
        if num < 10: planet.type = 'A'
779
        elif num < 15: planet.type = 'G'; planet.diameter = dice(3, 6, 0) * 10000
780
        elif num < 25: planet.type = 'R'; planet.diameter = dice(1, 10, 0) * 1000
781
        elif num < 45: planet.type = 'D'; planet.diameter = dice(2, 6, 2) * 1000
782
        elif num < 70: planet.type = 'H'; planet.diameter = dice(3, 6, 1) * 1000
783
        elif num < 90:
784
            if isFGK:
785
                planet.type = 'M'; planet.diameter = dice(2, 6, 5) * 1000
786
            else:
787
                planet.type = 'H'; planet.diameter = dice(3, 6, 1) * 1000
788
        elif num < 100:
789
            if isFGK:
790
                # planet.type = 'E'; planet.diameter = dice(2, 6, 5) * 1000
791
                planet.type = 'E'; planet.diameter = dice(1, 4, 13) * 1000
792
            else:
793
                planet.type = 'H'; planet.diameter = dice(3, 6, 1) * 1000
794
    elif zone == 2: # Zone C
795
        if num < 15: planet.type = 'A'
796
        elif num < 75: planet.type = 'G'; planet.diameter = dice(3, 6, 0) * 10000
797
        elif num < 80: planet.type = 'R'; planet.diameter = dice(1, 10, 0) * 1000
798
        elif num < 90: planet.type = 'C'; planet.diameter = dice(1, 10, 0) * 1000
799
        elif num < 95: planet.type = 'D'; planet.diameter = dice(2, 6, 2) * 1000
800
        elif num < 100:
801
            if isDNB:
802
                planet.type = 'C'; planet.diameter = dice(1, 10, 0) * 1000
803
            else:
804
                planet.type = 'H'; planet.diameter = dice(3, 6, 1) * 1000
805
    # energy
806
    planet.energy = random.randrange(100 - zone * 50, 150 - zone * 50)
807
    # minerals
808
    if planet.type[0] in ('R', 'D', 'H', 'M'):
809
        density = dice(1, 6, 0) / 2.0 + 3
810
        planet.minerals = int(((planet.diameter / 500.0) + density * 10.0 + random.randrange(1, 101) / 2.0 - 45) * 2)
811
    elif planet.type[0] == 'A':
812
        diameter = dice(1, 10, 0) * 1000 # rock planet
813
        density = dice(1, 6, 0) / 2.0 + 3
814
        planet.minerals = int(((diameter / 500.0) + density * 10.0 + random.randrange(1, 101) / 2.0 - 45) * 2)
815
    elif planet.type[0] == 'G':
816
        diameter = dice(3, 6, 1) * 1000 # earth like planet
817
        density = dice(1, 6, 0) / 2.0 + 3
818
        planet.minerals = int(((diameter / 500.0) + density * 10.0 + random.randrange(1, 101) / 2.0 - 45) * 2)
819
    elif planet.type == 'E':
820
        planet.minerals = 100
821
    else:
822
        planet.minerals = 0
823
    if planet.minerals < 0:
824
        planet.minerals = 0
825
    # environment
826
    if planet.type == 'E': planet.environ = 100
827
    elif planet.type == 'M': planet.environ = random.randrange(25, 51)
828
    elif planet.type == 'H': planet.environ = random.randrange(12, 26)
829
    elif planet.type == 'D': planet.environ = random.randrange(6, 13)
830
    elif planet.type == 'C': planet.environ = random.randrange(0, 7)
831
    elif planet.type == 'R': planet.environ = random.randrange(0, 7)
832
    else: planet.environ = 0
833
    # slots
834
    slotsMod = 0.67
835
    planet.maxSlots = int((planet.diameter / 1000) * 1.5 * slotsMod)
836
    if planet.type == 'E': planet.slots = 9 # planet.slots = int(planet.maxSlots * 0.50)
837
    elif planet.type == 'M': planet.slots = int(planet.maxSlots * 0.50)
838
    elif planet.type == 'H': planet.slots = int(planet.maxSlots * 0.50)
839
    elif planet.type == 'D': planet.slots = int(planet.maxSlots * 0.75)
840
    elif planet.type == 'C': planet.slots = int(planet.maxSlots * 0.75)
841
    elif planet.type == 'R': planet.slots = int(planet.maxSlots * 0.75)
842
    else: planet.slots = 0
843
    # make sure that all planets except A and G has at least one slot
844
    if planet.type in "EMHDCR" and planet.slots == 0:
845
        #@print "Fixing slots", planet.type, planet.slots, planet.maxSlots
846
        planet.maxSlots = max(1, planet.maxSlots)
847
        planet.slots = max(1, planet.slots)
848
    #print planet.type, planet.environ, planet.minerals
849
850
def dice(num, range, offset):
851
    result = offset
852
    for i in xrange(0, num):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
853
        result += random.randrange(1, range + 1)
854
    return result
855
856
857
## info
858
def getInfo(galaxy):
859
    starTypes = {}
860
    planetTypes = {}
861
    planets = 0
862
    maxPlanets = 0
863
    minPlanets = 999
864
    planetDist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
865
    for system in galaxy.systems:
866
        starTypes[system.starClass] = starTypes.get(system.starClass, 0) + 1
867
        for planet in system.planets:
868
            planetTypes[planet.type] = planetTypes.get(planet.type, 0) + 1
869
            planets += 1
870
        sysPlanets = len(system.planets)
871
        maxPlanets = max(maxPlanets, sysPlanets)
872
        minPlanets = min(minPlanets, sysPlanets)
873
        planetDist[sysPlanets] += 1
874
    stars = len(galaxy.systems)
875
    print 'Systems:', stars
876
    print starTypes
877
    print 'Planets per system:', planetDist
878
    print 'Planets:', planets
879
    print 'min %d, max %d, avg %.2f' % (minPlanets, maxPlanets, float(planets) / stars)
880
    print 'Types:', planetTypes
881
    return stars, starTypes, planets, planetTypes
882
883
884
## load names
885
systemNames = []
886
887
def loadSystemNames():
888
    global systemNames
889
    names = set([])
890
891
    with open(data.SYSTEM_NAMES_FILE) as names_file:
892
        for line in names_file:
893
            names.add(line.strip())
894
    systemNames = list(names)
895
896
if __name__ == '__main__':
897
    import sys
898
899
    galaxyType = sys.argv[1]
900
    targetFile = sys.argv[2]
901
    with open(targetFile, 'w') as fileHandle:
902
        GenerateGalaxy(galaxyType, fileHandle)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable GenerateGalaxy does not seem to be defined.
Loading history...
903