示例#1
0
    def test_garbage_collection_precondition(self):
        pc = PhysicsCache()
        pc.acquire(1)
        pc.release(1)
        pc.acquire(2)
        pc.release(2)

        # verify that game_loop_finish does not permit collection by itself,
        # but does in conjunction with graphics_loop_finish
        pc.game_loop_finish(0)
        pc._garbage_collect()
        self.assertEqual(pc.count, 3)
        self.assertEqual(pc.latest, 2)
        self.assertTrue(pc.has(0))
        pc.graphics_loop_finish(0)
        pc._garbage_collect()
        self.assertEqual(pc.count, 2)
        self.assertEqual(pc.latest, 2)
        self.assertTrue(not pc.has(0))

        # verify that graphics_loop_finish does not permit collection by
        # itself, but does in conjunction with game_loop_finish
        pc.graphics_loop_finish(1)
        pc._garbage_collect()
        self.assertEqual(pc.count, 2)
        self.assertEqual(pc.latest, 2)
        self.assertTrue(pc.has(1))
        pc.game_loop_finish(1)
        pc._garbage_collect()
        self.assertEqual(pc.count, 1)
        self.assertEqual(pc.latest, 2)
        self.assertTrue(not pc.has(1))
示例#2
0
 def test_latest(self):
     pc = PhysicsCache()
     self.assertEqual(pc.latest, 0)
     pc.acquire(3)
     pc.release(3)
     pc.acquire(2)
     pc.release(2)
     self.assertEqual(pc.latest, 3)
示例#3
0
 def test_has(self):
     pc = PhysicsCache()
     self.assertTrue(pc.has(0))
     self.assertTrue(not pc.has(3))
     pc.acquire(3)
     self.assertTrue(not pc.has(3))
     pc.release(3)
     self.assertTrue(pc.has(3))
示例#4
0
    def test_garbage_collection_protects_latest(self):
        pc = PhysicsCache()
        pc.acquire(1)
        pc.release(1)

        # verify that the latest snapshot will not permit itself to be deleted
        pc.graphics_loop_finish(1)
        pc.game_loop_finish(1)
        pc._garbage_collect(2)
        self.assertTrue(pc.has(1))
示例#5
0
 def test_count(self):
     pc = PhysicsCache()
     count = len([i for i in pc._snapshots if pc._snapshots[i].ready])
     self.assertEqual(count, 1)
     self.assertEqual(pc.count, count)
     pc.acquire(3)
     pc.release(3)
     pc.acquire(2)
     count = len([i for i in pc._snapshots if pc._snapshots[i].ready])
     self.assertEqual(count, 2)
     self.assertEqual(pc.count, count)
     pc.release(2)
     count = len([i for i in pc._snapshots if pc._snapshots[i].ready])
     self.assertEqual(count, 3)
     self.assertEqual(pc.count, count)
示例#6
0
    def test_garbage_collection_cascade(self):
        pc = PhysicsCache()
        pc.acquire(1)
        pc.release(1)
        pc.acquire(2)
        pc.release(2)

        # verify that a snapshot that's okay_to_delete also causes earlier
        # snapshots to be okay_to_delete
        pc.graphics_loop_finish(1)
        pc.game_loop_finish(1)
        pc._garbage_collect(2)
        self.assertEqual(pc.count, 1)
        self.assertEqual(pc.latest, 2)
        self.assertTrue(not pc.has(0))
        self.assertTrue(not pc.has(1))
        self.assertTrue(pc.has(2))
示例#7
0
class Universe:
    def __init__(self,generator=None,paused=True,G = 8.648208e-15):
        self.bodies = []
        self.time = 0
        # gravitational constant is in kilometers cubed per kilogram per turn squared
        # 1 turn = 6 minutes
        self.G = G
        self.view = None
        self._turn_left = 1
        self._paused = paused
        self.pause_lock = Lock()
        self.generator = generator
        self.sun = None
        self.physics_cache = PhysicsCache(self)
        self.next_turn = clock() + 360
        self._seconds_per_turn = 360
        self.running = False

    def get_seconds_per_turn(self):
        return self._seconds_per_turn
    def set_seconds_per_turn(self, seconds_per_turn):
        turn_left = (self.next_turn - clock()) / self._seconds_per_turn
        self._seconds_per_turn = seconds_per_turn
        self.next_turn = clock() + (turn_left * self._seconds_per_turn)
    seconds_per_turn = property(get_seconds_per_turn, set_seconds_per_turn)

    def get_turn_left(self):
        if self.paused:
            return self._turn_left
        return max(0, self.next_turn - clock()) / self.seconds_per_turn
    turn_left = property(get_turn_left)

    def get_paused(self):
        return self._paused
    def set_paused(self, paused):
        self.pause_lock.acquire()
        # if unpausing
        if self.paused and not paused:
            self.next_turn = clock() + (self._turn_left * self.seconds_per_turn)
            # self._turn_left only has meaning while paused
            del self._turn_left
        # else if pausing
        elif not self.paused and paused:
            self._turn_left = self.turn_left
        self._paused = paused
        self.pause_lock.release()
    paused = property(get_paused, set_paused)

    def get_center_of_mass(self):
        total_mass = sum(body.mass for body in self.bodies)
        if total_mass == 0:
            return Vector(0,0,0)
        return sum((body.get_position(self.time) * body.mass) for body in self.bodies)/total_mass
    center_of_mass = property(get_center_of_mass)

    def calculate_physics(self, turn):
        self.physics_cache.acquire(turn)
        if not self.physics_cache.has(turn):
            for body in self.bodies:
                gravity_sum = 0
                satellite_gravity_sum = 0
                for other in self.bodies:
                    if body.primary == other or body.primary == other.primary:
                        component = body.attraction(other, turn-1)
                        gravity_sum += component
                        satellite_gravity_sum += component
                    elif body == other.primary:
                        gravity_sum += body.attraction(other, turn-1)
                    # otherwise, do not calculate an interaction
                position = body.get_position(turn-1) + body.get_velocity(turn-1)
                velocity = body.get_velocity(turn-1) + (gravity_sum * self.G)
                sat_accel = satellite_gravity_sum * self.G
                self.physics_cache.record(turn, body, position, velocity, sat_accel)
        self.physics_cache.release(turn)

    def pass_turn(self):
        self.time += 1
        if self.generator:
            dev = self.generator.generate_development()
            if dev:
                print("Development: %d at t=%d" % (dev,self.time))
        # give the goahead from our side to delete this record
        self.physics_cache.game_loop_finish(self.time - 1)

    def travel_time(self,b1,b2,accel):
        velocity_diff = (b1.velocity - b2.velocity).magnitude
        distance = (b1.position - b2.position).magnitude
        return ceil((velocity_diff/accel)+(distance/sqrt(accel*distance/4)))

    def ui_loop(self):
        self.view = Interface(self)
        self.view.ui_loop()

    def physics_cache_loop(self):
        start_time = clock()
        while self.view:
            self.calculate_physics(self.physics_cache.latest + 1)
            if self.time_per_snapshot < self.seconds_per_turn or self.paused:
                self.physics_cache.garbage_collect(2)
            self.time_per_snapshot = (clock() - start_time + self.time_per_snapshot*9) / 10
            start_time = clock()

    def describe_system(self):
        plural = "s"
        if self.time == 1:
            plural = ""
        print("time = "+str(self.time)+" turn"+plural)
        sun = self.bodies[0]
        print(sun.name+": mass="+('%.2E' % sun.mass))
        for i in range(1,len(self.bodies)):
            bodyi = self.bodies[i]
            dist = (self.bodies[0].position - bodyi.position).magnitude
            orbit_speed = (self.bodies[0].velocity - bodyi.velocity).magnitude
            print(bodyi.name+": dist=("+('%.2E' % dist)+"), orbit speed=("+('%.2E' % orbit_speed)+"), mass="+('%.2E' % bodyi.mass))
        print()

    def make_system_tree(self, turn=None):
        sorted_bodies = sorted(self.bodies, key=lambda b: b.mass)
        for i in range(0,len(sorted_bodies)-1):
            bodyi = sorted_bodies[i]
            highest_influence = 0
            primary = None
            for j in range(i+1,len(sorted_bodies)):
                bodyj = sorted_bodies[j]
                dist = bodyi.distance(bodyj, turn)
                influence = bodyj.mass / dist / dist
                if influence > highest_influence:
                    highest_influence = influence
                    primary = bodyj
            bodyi.primary = primary

    def run(self, visible = True):
        self.start_time = clock()
        if visible:
            ui_t = Thread(target=self.ui_loop)
            ui_t.start()
            while not self.view:
                pass
        phys_t = Thread(target=self.physics_cache.loop)
        phys_t.start()
        self.next_turn = clock() + self.seconds_per_turn
        self.running = True
        while self.running:
            while (self.paused or clock() < self.next_turn) and self.view:
                pass
            if (self.physics_cache.latest <= self.time):
                self.paused = True
            else:
                self.pass_turn()
                self.next_turn += self.seconds_per_turn

    def stop(self):
        self.running = False
        self.physics_cache.running = False
        self.view = None