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
Asked By: amer slaiman

||

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'
Answered By: J_H
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.

Answered By: FLAK-ZOSO
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.