|
| 1 | +""" Code example from Complexity and Computation, a book about |
| 2 | +exploring complexity science with Python. Available free from |
| 3 | +
|
| 4 | +http://greenteapress.com/complexity |
| 5 | +
|
| 6 | +Copyright 2011 Allen B. Downey. |
| 7 | +Distributed under the GNU General Public License at gnu.org/licenses/gpl.html. |
| 8 | +""" |
| 9 | + |
| 10 | +import math |
| 11 | +import random |
| 12 | + |
| 13 | +from swampy.TurtleWorld import TurtleWorld, Turtle |
| 14 | +import color_list |
| 15 | + |
| 16 | + |
| 17 | +class Highway(TurtleWorld): |
| 18 | + """A circular Highway with one lane that spirals down the canvas. |
| 19 | + |
| 20 | + Attributes: |
| 21 | +
|
| 22 | + rows: the number of rows that spiral down the canvas. |
| 23 | + delay: time between steps in seconds |
| 24 | + colors: list of RGB strings |
| 25 | + """ |
| 26 | + def __init__(self): |
| 27 | + TurtleWorld.__init__(self) |
| 28 | + self.rows = 2.0 |
| 29 | + self.delay = 0.01 |
| 30 | + self.colors = color_list.make_color_dict().values() |
| 31 | + |
| 32 | + def random_color(self): |
| 33 | + """Returns a random RGB string in #RRGGBB format.""" |
| 34 | + return random.choice(self.colors) |
| 35 | + |
| 36 | + def get_length(self): |
| 37 | + """Returns the total length of this highway.""" |
| 38 | + return self.ca_width * self.rows |
| 39 | + |
| 40 | + def project(self, turtle): |
| 41 | + """Project the turtle onto the highway. |
| 42 | +
|
| 43 | + Reads the Turtle's position position and returns its |
| 44 | + x,y coordinates. |
| 45 | + """ |
| 46 | + p = 1.0 * turtle.position % self.get_length() |
| 47 | + x = p % self.ca_width |
| 48 | + y = p / self.ca_width * self.ca_height / self.rows |
| 49 | + return x, y |
| 50 | + |
| 51 | + def lane_heading(self): |
| 52 | + """Returns the heading that aligns the turtle with the lane.""" |
| 53 | + x = self.ca_width |
| 54 | + y = 1.0 * self.ca_height / self.rows |
| 55 | + angle = math.atan2(y, x) |
| 56 | + heading = angle * 180 / math.pi |
| 57 | + return heading |
| 58 | + |
| 59 | + def step(self): |
| 60 | + """Performs one time step.""" |
| 61 | + TurtleWorld.step(self) |
| 62 | + |
| 63 | + # compute average turtle speed |
| 64 | + total = 0.0 |
| 65 | + for turtle in self.animals: |
| 66 | + total += turtle.speed |
| 67 | + |
| 68 | + print total / len(self.animals) |
| 69 | + |
| 70 | + def make_drivers(self, n, constructor): |
| 71 | + """Make drivers at random positions. |
| 72 | +
|
| 73 | + Args: |
| 74 | + n: number of Drivers |
| 75 | + constructor: function that returns a Driver |
| 76 | +
|
| 77 | + Returns: |
| 78 | + a list of Drivers. |
| 79 | + """ |
| 80 | + t = [] |
| 81 | + for i in range(n): |
| 82 | + turtle = constructor(self) |
| 83 | + t.append((turtle.position, turtle)) |
| 84 | + |
| 85 | + # link up the drivers so each has an attribute (next) that |
| 86 | + # refers to the driver in front |
| 87 | + t.sort() |
| 88 | + turtles = [t[1] for t in t] |
| 89 | + for i in range(n-1): |
| 90 | + turtles[i].next = turtles[i+1] |
| 91 | + turtles[-1].next = turtles[0] |
| 92 | + |
| 93 | + return turtles |
| 94 | + |
| 95 | + |
| 96 | +class Driver(Turtle): |
| 97 | + """A Turtle with a random position, speed and color.""" |
| 98 | + |
| 99 | + def __init__(self, *args, **kwds): |
| 100 | + Turtle.__init__(self, *args, **kwds) |
| 101 | + self.delay = 0 |
| 102 | + self.color = self.world.random_color() |
| 103 | + self.position = random.randrange(0, self.world.get_length()) |
| 104 | + |
| 105 | + self.speed_limit = 4 |
| 106 | + self.speed = random.randrange(0, self.speed_limit) |
| 107 | + self.safe_distance = 30 |
| 108 | + |
| 109 | + self.heading = self.world.lane_heading() |
| 110 | + self.next = None |
| 111 | + self.project() |
| 112 | + self.redraw() |
| 113 | + |
| 114 | + def project(self): |
| 115 | + """Sets x and y according to position.""" |
| 116 | + self.x, self.y = self.world.project(self) |
| 117 | + |
| 118 | + def accelerate(self, change): |
| 119 | + """Speeds up the Turtle by the given amount, within bounds.""" |
| 120 | + self.speed += change |
| 121 | + if self.speed < 0: |
| 122 | + self.speed = 0 |
| 123 | + |
| 124 | + if self.speed > self.speed_limit: |
| 125 | + self.speed = self.speed_limit |
| 126 | + |
| 127 | + def brake(self, change): |
| 128 | + """Slows down the Turtle by the given amount, within bounds.""" |
| 129 | + self.accelerate(-change) |
| 130 | + |
| 131 | + def step(self): |
| 132 | + """Checks the distance to the next driver, adjusts speed, and moves. |
| 133 | + |
| 134 | + This function enforces the rules for all drivers. |
| 135 | + |
| 136 | + Driver decision-making is in choose_acceleration(). |
| 137 | + """ |
| 138 | + dist = self.find_distance() |
| 139 | + |
| 140 | + # get acceleration |
| 141 | + change = self.choose_acceleration(dist) |
| 142 | + self.accelerate(change) |
| 143 | + |
| 144 | + # if the resulting speed would cause a collision, jam on the brakes |
| 145 | + if self.speed > dist: |
| 146 | + self.speed = 0 |
| 147 | + |
| 148 | + # move |
| 149 | + self.position += self.speed |
| 150 | + self.project() |
| 151 | + self.redraw() |
| 152 | + |
| 153 | + def find_distance(self): |
| 154 | + """Finds the distance between this Turtle and the next.""" |
| 155 | + dist = self.next.position - self.position |
| 156 | + |
| 157 | + # deal with wrap-around |
| 158 | + if dist < 0: |
| 159 | + dist += self.world.get_length() |
| 160 | + return dist |
| 161 | + |
| 162 | + def choose_acceleration(self, dist): |
| 163 | + """Adjusts the speed of the Driver.""" |
| 164 | + if dist < self.safe_distance: |
| 165 | + return -1 |
| 166 | + else: |
| 167 | + return 0.3 |
| 168 | + |
| 169 | + |
| 170 | +def make_highway(n, driver=Driver): |
| 171 | + """Make the highway and drivers, then run the simulation. |
| 172 | +
|
| 173 | + Args: |
| 174 | + n: number of Drivers |
| 175 | + driver: constructor |
| 176 | + """ |
| 177 | + |
| 178 | + # create the highway |
| 179 | + world = Highway() |
| 180 | + world.canvas.clear_transforms() |
| 181 | + world.setup_run() |
| 182 | + |
| 183 | + # create (n) drivers |
| 184 | + world.make_drivers(n, driver) |
| 185 | + |
| 186 | + # start the simulation, then wait for user events |
| 187 | + print len(world.animals) |
| 188 | + world.run() |
| 189 | + world.mainloop() |
| 190 | + |
| 191 | + |
| 192 | +def main(script, n=20): |
| 193 | + n = int(n) |
| 194 | + make_highway(n) |
| 195 | + |
| 196 | + |
| 197 | +if __name__ == '__main__': |
| 198 | + import sys |
| 199 | + main(*sys.argv) |
0 commit comments