Skip to content

Commit 5bdc139

Browse files
committed
Rename directory to use underscore
1 parent 5e8b7c8 commit 5bdc139

File tree

6 files changed

+225
-0
lines changed

6 files changed

+225
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# The Gilded Traffic Light
2+
3+
Refactor a messy legacy traffic light system into a proper domain model.
4+
5+
You’ll start with a single-file procedural Ruby script that simulates an intersection with two directions of traffic lights and pedestrian signals. Your job is to gradually extract meaningful objects and ensure the behaviour doesn't change.
6+
7+
## Problem Description
8+
9+
Imagine a simplified but messy traffic controller. We have:
10+
11+
- Two directions of traffic (North-South and East-West).
12+
- Each direction cycles through Red → Green → Amber → Red.
13+
- Pedestrian signals can only display “WALK” when that direction is red.
14+
15+
In the provided code, everything (timers, states, and transitions) is lumped into one file and one giant method. Refactor it into a more object-oriented design, preserving functionality.
16+
17+
## Requirements and Constraints
18+
19+
### Requirements
20+
21+
- The system should prevent conflicting green lights and only allow pedestrians to cross on red.
22+
- The code must run forever unless interrupted.
23+
- Do not break the existing console output or sequence logic.
24+
- Preserve the timers for each colour transition.
25+
26+
### Constraints
27+
28+
- You must not remove or skip any stage of the light cycle (Red, Green, Amber).
29+
- Keep the pedestrian signals tied to the traffic light states.
30+
31+
## Examples and Test Cases
32+
33+
A sample test file has been provided. Ensure these tests continue to pass as you refactor and rearrange the code.
34+
35+
## Instructions
36+
37+
Open the single-file Ruby script that contains the messy procedural code.
38+
Run it directly to see the console output and confirm how the sequence changes over time.
39+
40+
Refactor the system one step at a time. Introduce appropriate classes to develop a deeper and more meaningful domain model, while preserving all the existing behaviour.
41+
42+
(Best to run the tests after each change to ensure everything still passes).
43+
44+
## Evaluation Criteria
45+
46+
- All tests pass for the final refactored solution.
47+
- Quality of design: Classes and methods have clear responsibilities.
48+
- Maintainability: Future changes (e.g. adding more directions or sensors) should be straightforward.
49+
50+
51+
---
52+
53+
## How to run your main file
54+
55+
```
56+
bundle exec ruby katas/4-gilded-traffic-light/bodacious/main.rb
57+
```
58+
File renamed without changes.
File renamed without changes.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# frozen_string_literal: true
2+
3+
require 'bundler/inline'
4+
5+
gemfile do
6+
source 'https://rubygems.org'
7+
8+
gem 'minitest'
9+
end
10+
11+
class TrafficLightSystem
12+
def initialize
13+
@lights = [
14+
{ direction: "North-South", state: "red", timer: 10 },
15+
{ direction: "East-West", state: "green", timer: 8 }
16+
]
17+
@pedestrian_signals = {
18+
"North-South" => false,
19+
"East-West" => false
20+
}
21+
end
22+
23+
# Advances the system by one second: prints current status, decrements timers,
24+
# and updates states as needed, all in one giant procedural block.
25+
def run
26+
# Clear the screen each time (so this is consistent with the original request).
27+
# You could skip this if you don't want console clearing in tests, but it's here for completeness.
28+
system("clear") || system("cls")
29+
30+
# Print traffic lights
31+
@lights.each do |light|
32+
# Quick inline way to colour states, done in a messy way rather than a dedicated method.
33+
state_colour = case light[:state]
34+
when "red" then "\e[31m#{light[:state]}\e[0m"
35+
when "green" then "\e[32m#{light[:state]}\e[0m"
36+
when "amber" then "\e[33m#{light[:state]}\e[0m"
37+
else light[:state]
38+
end
39+
40+
puts "Direction: #{light[:direction]}, State: #{state_colour}, Time left: #{light[:timer]}s"
41+
end
42+
43+
# Update timers and transition states in one big chunk
44+
@lights.each do |light|
45+
light[:timer] -= 1
46+
if light[:timer] <= 0
47+
# Big case statement for state transitions
48+
case light[:state]
49+
when "red"
50+
# Switch from red -> green
51+
light[:state] = "green"
52+
light[:timer] = 8
53+
@pedestrian_signals[light[:direction]] = false
54+
# Force the opposite light red if it's green or amber
55+
opposite = @lights.find { |l| l[:direction] != light[:direction] }
56+
if %w[green amber].include?(opposite[:state])
57+
opposite[:state] = "red"
58+
opposite[:timer] = 10
59+
@pedestrian_signals[opposite[:direction]] = true
60+
end
61+
62+
when "green"
63+
# Switch from green -> amber
64+
light[:state] = "amber"
65+
light[:timer] = 3
66+
@pedestrian_signals[light[:direction]] = false
67+
68+
when "amber"
69+
# Switch from amber -> red
70+
light[:state] = "red"
71+
light[:timer] = 10
72+
@pedestrian_signals[light[:direction]] = true
73+
74+
else
75+
# Fallback
76+
light[:state] = "red"
77+
light[:timer] = 10
78+
@pedestrian_signals[light[:direction]] = true
79+
end
80+
end
81+
end
82+
83+
# Print pedestrian signals
84+
puts "\nPedestrian signals:"
85+
@pedestrian_signals.each do |direction, can_walk|
86+
signal_text = can_walk ? "\e[32mWALK\e[0m" : "\e[31mDON'T WALK\e[0m"
87+
puts " #{direction}: #{signal_text}"
88+
end
89+
90+
puts "---------------------------------"
91+
end
92+
end
93+
94+
# Only run the perpetual loop if this file is executed directly.
95+
# Each call to #run advances the system by one second. Tests can call #run manually.
96+
if $PROGRAM_NAME == __FILE__
97+
system = TrafficLightSystem.new
98+
loop do
99+
system.run
100+
sleep 1
101+
end
102+
end
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# traffic_light_system_test.rb
2+
3+
require_relative 'main'
4+
require "minitest/autorun"
5+
class TrafficLightSystemTest < Minitest::Test
6+
def test_initial_output
7+
system = TrafficLightSystem.new
8+
out, _err = capture_io do
9+
system.run
10+
end
11+
12+
assert_includes out, "Direction: North-South, State: \e[31mred\e[0m, Time left: 10s"
13+
assert_includes out, "Direction: East-West, State: \e[32mgreen\e[0m, Time left: 8s"
14+
15+
assert_includes out, "North-South: \e[31mDON'T WALK\e[0m"
16+
assert_includes out, "East-West: \e[31mDON'T WALK\e[0m"
17+
end
18+
19+
def test_red_transitions_to_green
20+
system = TrafficLightSystem.new
21+
10.times { system.run }
22+
23+
out, _err = capture_io do
24+
system.run
25+
end
26+
27+
assert_includes out, "Direction: North-South, State: \e[32mgreen\e[0m, Time left: 8s"
28+
end
29+
30+
def test_green_transitions_to_amber
31+
system = TrafficLightSystem.new
32+
8.times { system.run }
33+
34+
out, _err = capture_io do
35+
system.run
36+
end
37+
38+
assert_includes out, "Direction: East-West, State: \e[33mamber\e[0m, Time left: 3s"
39+
end
40+
41+
def test_amber_transitions_to_red
42+
system = TrafficLightSystem.new
43+
11.times { system.run } # now East-West should be amber(3)
44+
45+
out, _err = capture_io do
46+
system.run
47+
end
48+
49+
assert_includes out, "Direction: East-West, State: \e[31mred\e[0m, Time left: 8s"
50+
assert_includes out, "East-West: \e[32mWALK\e[0m"
51+
end
52+
53+
def test_opposite_direction_forced_red
54+
system = TrafficLightSystem.new
55+
56+
10.times { system.run }
57+
out, _err = capture_io do
58+
system.run
59+
end
60+
61+
assert_includes out, "Direction: North-South, State: \e[32mgreen\e[0m, Time left: 8s"
62+
assert_includes out, "Direction: East-West, State: \e[31mred\e[0m, Time left: 9s"
63+
assert_includes out, "East-West: \e[32mWALK\e[0m"
64+
end
65+
end

0 commit comments

Comments
 (0)