how light works traffic simulation in python
Question:
I’m trying to build a light for traffic simulation system
I’m writing a light class which is represents a traffic signal like this
- period, i.e. the number of time steps between two changes from green
to red
- green-period, i.e. the number of time steps where the signal is
green.
Like this pic:it shows how the light works
so I wrote this
class Light:
"""Represents a traffic light"""
def __init__(self, period, green_period):
self.period = period
self.green_period = green_period
self.color = 'G'
def __str__(self):
return self.color
def __repr__(self):
pass
def step(self):
time = 0
while True:
time += 1
if time < self.green_period:
self.color = 'G'
else:
self.color = 'R'
if time == self.green_period:
break
def is_green(self):
if self.color == 'G':
return True
elif self.color == 'R':
return False
def demo_light():
"""Demonstrats the Light class"""
a_light = Light(7, 3)
for i in range(15):
print(i, a_light, a_light.is_green())
a_light.step()
def main():
"""Demonstrates the classes"""
print('nLight demonstrationn')
demo_light()
if __name__ == '__main__':
main()
I’m trying to get this
0 (G) True
1 (G) True
2 (G) True
3 (R) False
4 (R) False
5 (R) False
6 (R) False
7 (G) True
8 (G) True
9 (G) True
10 (R) False
11 (R) False
12 (R) False
13 (R) False
14 (G) True
but the result is:
0 G True
1 R False
2 R False
3 R False
4 R False
5 R False
6 R False
7 R False
8 R False
9 R False
10 R False
11 R False
12 R False
13 R False
14 R False
Answers:
Names matter.
Your period
identifier is perfect.
Please rename green-period
to green-interval
,
as it denotes a (non-recurring) length of time
that happens after a recurring periodic transition.
Authors will sometimes express this concept with
a fractional "duty cycle", but that seems inconvenient here.
Keep it in units of update time steps, that makes perfect sense.
There is a for
loop within def step
which doesn’t seem to belong there.
The caller is already looping, calling .step()
repeatedly.
After enough calls, the light will change color, then change back again.
Rather than
def step(self):
time = 0
...
consider moving it to a self.time = 0
assignment inside the __init__
constructor,
so time shows state of the world rather than number of
steps into the current cycle.
Retrieve "steps into current cycle" with modulo, with self.time % self.period
.
You wrote
if time == self.green_period:
break
That is a good comparison, as far as it goes.
But it is only solves half the battle, and
it doesn’t take care of some important housekeeping chores.
The step
method needs to detect both transitions,
from R -> G as above, and also G -> R.
When it detects a transition, it must flip the color!
It must assign a new value to self.color
.
That is the crux of your complaint — step
is not yet doing that.
Your str
self.color
representation is perfectly sensible, I can
certainly see why you initially chose it.
But given the "update" responsibility that step
has,
you might possibly find this a more convenient representation,
using an int
index rather than a str
:
def __init__ ...
self.color = 0
self.colors = 'GR'
...
# advance the color to next-in-sequence
self.color = (self.color + 1) % len(self.colors)
# display current color
print(self.colors[self.color])
One big motivation for this is looking ahead
to when you have a requirement for displaying three states:
self.colors = 'GYR'
def step(self):
time = 0
while True:
time += 1
if time < self.green_period:
self.color = 'G'
else:
self.color = 'R'
if time == self.green_period:
break
So, this is the Light::step
function, and what we know is that the problem comes from here, since it’s supposed to turn the light from green to red, but it seems to always give red.
a_light = Light(7, 3)
This is our instance of Light
, it has a period
of 7
and a green_period
of 3
.
The image above is how you explained it’s supposed to work.
The problem is in the first line of the method step
, because time
is always set to zero, and so the value "returned" (there’s no return but I think you got it) will be always the same since the only "variable" always starts from the same value.
def __init__(self, period, green_period):
self.period = period
self.green_period = green_period
self.color = 'G'
self.time = 0 # This variable will tell you which instant we're in
Now that you have time
as a variable attribute, you can re-write the step
function this way.
def step(self):
self.time += 1
# If time is on green phase...
if time%self.period < self.green_period: # [For example 9%7 < 3]
# ...then turn it to green (doesn't matter if it's already green)
self.color = 'G'
else: # ...else, if the time isn't on green phase it must be on red phase
self.color = 'R'
This should work, I haven’t tested it but I think it’s very close to what you need.
I’m trying to build a light for traffic simulation system
I’m writing a light class which is represents a traffic signal like this
- period, i.e. the number of time steps between two changes from green
to red - green-period, i.e. the number of time steps where the signal is
green.
Like this pic:it shows how the light works
so I wrote this
class Light:
"""Represents a traffic light"""
def __init__(self, period, green_period):
self.period = period
self.green_period = green_period
self.color = 'G'
def __str__(self):
return self.color
def __repr__(self):
pass
def step(self):
time = 0
while True:
time += 1
if time < self.green_period:
self.color = 'G'
else:
self.color = 'R'
if time == self.green_period:
break
def is_green(self):
if self.color == 'G':
return True
elif self.color == 'R':
return False
def demo_light():
"""Demonstrats the Light class"""
a_light = Light(7, 3)
for i in range(15):
print(i, a_light, a_light.is_green())
a_light.step()
def main():
"""Demonstrates the classes"""
print('nLight demonstrationn')
demo_light()
if __name__ == '__main__':
main()
I’m trying to get this
0 (G) True
1 (G) True
2 (G) True
3 (R) False
4 (R) False
5 (R) False
6 (R) False
7 (G) True
8 (G) True
9 (G) True
10 (R) False
11 (R) False
12 (R) False
13 (R) False
14 (G) True
but the result is:
0 G True
1 R False
2 R False
3 R False
4 R False
5 R False
6 R False
7 R False
8 R False
9 R False
10 R False
11 R False
12 R False
13 R False
14 R False
Names matter.
Your period
identifier is perfect.
Please rename green-period
to green-interval
,
as it denotes a (non-recurring) length of time
that happens after a recurring periodic transition.
Authors will sometimes express this concept with
a fractional "duty cycle", but that seems inconvenient here.
Keep it in units of update time steps, that makes perfect sense.
There is a for
loop within def step
which doesn’t seem to belong there.
The caller is already looping, calling .step()
repeatedly.
After enough calls, the light will change color, then change back again.
Rather than
def step(self):
time = 0
...
consider moving it to a self.time = 0
assignment inside the __init__
constructor,
so time shows state of the world rather than number of
steps into the current cycle.
Retrieve "steps into current cycle" with modulo, with self.time % self.period
.
You wrote
if time == self.green_period:
break
That is a good comparison, as far as it goes.
But it is only solves half the battle, and
it doesn’t take care of some important housekeeping chores.
The step
method needs to detect both transitions,
from R -> G as above, and also G -> R.
When it detects a transition, it must flip the color!
It must assign a new value to self.color
.
That is the crux of your complaint — step
is not yet doing that.
Your str
self.color
representation is perfectly sensible, I can
certainly see why you initially chose it.
But given the "update" responsibility that step
has,
you might possibly find this a more convenient representation,
using an int
index rather than a str
:
def __init__ ...
self.color = 0
self.colors = 'GR'
...
# advance the color to next-in-sequence
self.color = (self.color + 1) % len(self.colors)
# display current color
print(self.colors[self.color])
One big motivation for this is looking ahead
to when you have a requirement for displaying three states:
self.colors = 'GYR'
def step(self):
time = 0
while True:
time += 1
if time < self.green_period:
self.color = 'G'
else:
self.color = 'R'
if time == self.green_period:
break
So, this is the Light::step
function, and what we know is that the problem comes from here, since it’s supposed to turn the light from green to red, but it seems to always give red.
a_light = Light(7, 3)
This is our instance of Light
, it has a period
of 7
and a green_period
of 3
.
The image above is how you explained it’s supposed to work.
The problem is in the first line of the method step
, because time
is always set to zero, and so the value "returned" (there’s no return but I think you got it) will be always the same since the only "variable" always starts from the same value.
def __init__(self, period, green_period):
self.period = period
self.green_period = green_period
self.color = 'G'
self.time = 0 # This variable will tell you which instant we're in
Now that you have time
as a variable attribute, you can re-write the step
function this way.
def step(self):
self.time += 1
# If time is on green phase...
if time%self.period < self.green_period: # [For example 9%7 < 3]
# ...then turn it to green (doesn't matter if it's already green)
self.color = 'G'
else: # ...else, if the time isn't on green phase it must be on red phase
self.color = 'R'
This should work, I haven’t tested it but I think it’s very close to what you need.