Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support new brightness controls #1

Closed
wants to merge 9 commits into from
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cSpell.words": ["powerable", "Powerable"]
}
4 changes: 2 additions & 2 deletions bin/main.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:math';

import 'package:christmas_lights_kata/grid.dart';
import 'package:christmas_lights_kata/grid/powerable_grid.dart';

void main(List<String> arguments) {
final grid = Grid(1000)
final grid = PowerableGrid(1000)
..turnOn(const Point(887, 9), const Point(959, 629))
..turnOn(const Point(454, 398), const Point(844, 448))
..turnOff(const Point(539, 243), const Point(559, 965))
Expand Down
8 changes: 8 additions & 0 deletions lib/grid/dimmable_grid.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'package:christmas_lights_kata/grid/grid.dart';
import 'package:christmas_lights_kata/led/dimmable_led.dart';

/// A grid of dimmable LEDs.
class DimmableGrid extends Grid<DimmableLed> {
/// Creates a new [Grid] of [DimmableLed]s.
DimmableGrid(int size) : super(size, DimmableLed.new);
}
21 changes: 8 additions & 13 deletions lib/grid.dart → lib/grid/grid.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import 'dart:math';

import 'package:christmas_lights_kata/led.dart';
import 'package:christmas_lights_kata/led/led.dart';

/// A squared grid of LEDs.
class Grid {
class Grid<T extends Led<dynamic>> {
/// Constructs a new [Grid] from [size].
Grid(this.size)
: assert(size > 0, 'Size must be positive'),
Grid(this.size, T Function() constructor)
: assert(size > 0, '"size" must be positive'),
leds = [
for (var x = 0; x < size; x++) [for (var y = 0; y < size; y++) Led()],
for (var x = 0; x < size; x++)
[for (var y = 0; y < size; y++) constructor()],
];

/// The size of this [Grid].
final int size;

/// The two-dimensional matrix of [Led]s.
late final List<List<Led>> leds;
final List<List<T>> leds;

/// Turns on all [Led]s from [start] to [end] positions.
void turnOn(Point<int> start, Point<int> end) =>
Expand All @@ -32,7 +33,7 @@ class Grid {
void _actOnLedsFrom(
Point<int> start,
Point<int> end,
void Function(Led led) action,
void Function(Led<dynamic> led) action,
) {
for (var x = start.x; x <= end.x; x++) {
for (var y = start.y; y <= end.y; y++) {
Expand All @@ -41,12 +42,6 @@ class Grid {
}
}

/// Returns the number of powered [Led]s in the [Grid].
int get poweredLedsCount => leds.fold<int>(
0,
(count, row) => count + row.where((led) => led.isPowered).length,
);

@override
String toString() => leds.map((row) => row.join()).join('\n');
}
14 changes: 14 additions & 0 deletions lib/grid/powerable_grid.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:christmas_lights_kata/grid/grid.dart';
import 'package:christmas_lights_kata/led/powerable_led.dart';

/// A grid of powerable LEDs.
class PowerableGrid extends Grid<PowerableLed> {
/// Creates a new [Grid] of [PowerableLed]s.
PowerableGrid(int size) : super(size, PowerableLed.new);

/// Returns the number of powered [PowerableLed]s in the [Grid].
int get poweredLedsCount => leds.fold<int>(
0,
(count, row) => count + row.where((led) => led.isPowered).length,
);
}
40 changes: 40 additions & 0 deletions lib/led/dimmable_led.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:christmas_lights_kata/led/led.dart';
import 'package:christmas_lights_kata/utils/map_int_v_extension.dart';

const _offLight = '⚫️';
const _brightnessLights = {
0: _offLight,
1: '🔅',
10: '🔆',
};

/// A dimmable LED.
class DimmableLed extends Led<int> {
/// Constructs a new [DimmableLed].
DimmableLed() : _brightness = 0;

int _brightness;

/// Whether this [DimmableLed] is turned on or off.
int get brightness => _brightness;

/// Increases the brightness of this [DimmableLed] by 1.
@override
int turnOn() => _brightness++;

/// Reduces the brightness of this [DimmableLed] by 1.
@override
int turnOff() {
if (_brightness > 0) _brightness--;

return _brightness;
}

/// Increases the brightness of this [DimmableLed] by 2.
@override
int toggle() => _brightness = _brightness + 2;

@override
String toString() =>
_brightnessLights.valueFromThreshold(_brightness) ?? _offLight;
}
11 changes: 11 additions & 0 deletions lib/led/led.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// An LED.
abstract class Led<T> {
/// Turns on this [Led].
T turnOn();

/// Turns off this [Led].
T turnOff();

/// Toggles this [Led].
T toggle();
}
21 changes: 13 additions & 8 deletions lib/led.dart → lib/led/powerable_led.dart
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import 'package:christmas_lights_kata/led/led.dart';
import 'package:christmas_lights_kata/utils/random_string_extension.dart';

const _poweredLights = '🔴🟡🟢🔵';
const _offLight = '⚫️';

/// An LED.
class Led {
/// Constructs a new [Led].
Led({bool isPowered = false})
/// A powerable LED.
class PowerableLed extends Led<bool> {
/// Constructs a new [PowerableLed].
PowerableLed({bool isPowered = false})
: _isPowered = isPowered,
displayLight = _poweredLights.randomChar;

bool _isPowered;

/// Whether this [Led] is turned on or off.
/// Whether this [PowerableLed] is turned on or off.
bool get isPowered => _isPowered;

/// String for displaying the powered light.
late final String displayLight;

/// Turns on this [Led].
/// Turns on this [PowerableLed].
@override
bool turnOn() => _isPowered = true;

/// Turns off this [Led].
/// Turns off this [PowerableLed].
@override
bool turnOff() => _isPowered = false;

/// Toggles the powered state of this [Led] and returns the new state.
/// Toggles the powered state of this [PowerableLed] and returns the new
/// state.
@override
bool toggle() => _isPowered = !_isPowered;

@override
Expand Down
15 changes: 15 additions & 0 deletions lib/utils/map_int_v_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// Map int to V extension.
extension MapIntVExtension<V> on Map<int, V> {
/// Returns the corresponding value [V], if any, when the [threshold] is
/// lower than or equal to one of this [Map] keys.
V? valueFromThreshold(int threshold) {
final value = this[threshold];
if (value != null) return value;

for (final entry in entries) {
if (threshold <= entry.key) return entry.value;
}

return null;
}
}
29 changes: 16 additions & 13 deletions test/grid_test.dart → test/powerable_grid_test.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'dart:math';

import 'package:christmas_lights_kata/grid.dart';
import 'package:christmas_lights_kata/grid/powerable_grid.dart';
import 'package:test/test.dart';

void main() {
group('Grid', () {
group('PowerableGrid', () {
group('(new)', () {
test('creates a squared grid of 10x10', () {
const gridSize = 10;
final grid = Grid(gridSize);
final grid = PowerableGrid(gridSize);
expect(grid.size, gridSize);
expect(grid.leds.length, gridSize);
expect(grid.leds.first.length, gridSize);
Expand All @@ -18,38 +18,40 @@ void main() {

group('.turnOn()', () {
test('turns on all LEDs', () {
final grid = Grid(10)..turnOn(const Point(0, 0), const Point(9, 9));
final grid = PowerableGrid(10)
..turnOn(const Point(0, 0), const Point(9, 9));
expect(grid.areAllPowered, isTrue);
print(grid);
});

test('turns on only selected LEDs', () {
const start = Point(1, 2);
const end = Point(5, 6);
final grid = Grid(10)..turnOn(start, end);
final grid = PowerableGrid(10)..turnOn(start, end);
expect(grid.isPoweredFrom(start, end), isTrue);
print(grid);
});

test('turns on only selected LEDs', () {
const start = Point(7, 2);
const end = Point(8, 3);
final grid = Grid(10)..turnOn(start, end);
final grid = PowerableGrid(10)..turnOn(start, end);
expect(grid.isPoweredFrom(start, end), isTrue);
print(grid);
});

test('turns on only selected LEDs', () {
const point = Point(1, 1);
final grid = Grid(3)..turnOn(point, point);
final grid = PowerableGrid(3)..turnOn(point, point);
expect(grid.isPoweredFrom(point), isTrue);
print(grid);
});
});

group('.turnOff()', () {
test('turns off all LEDs', () {
final grid = Grid(10)..turnOn(const Point(0, 0), const Point(9, 9));
final grid = PowerableGrid(10)
..turnOn(const Point(0, 0), const Point(9, 9));
print(grid);
grid.turnOff(const Point(0, 0), const Point(9, 9));
expect(grid.areNonePowered, isTrue);
Expand All @@ -59,7 +61,8 @@ void main() {

group('.toggle()', () {
test('toggles all LEDs', () {
final grid = Grid(10)..toggle(const Point(0, 0), const Point(9, 9));
final grid = PowerableGrid(10)
..toggle(const Point(0, 0), const Point(9, 9));
expect(grid.areAllPowered, isTrue);
print(grid);
grid.toggle(const Point(0, 0), const Point(9, 9));
Expand All @@ -70,7 +73,7 @@ void main() {

group('.poweredLedsCount', () {
test('returns the number of powered LEDs in the grid', () {
final grid = Grid(1000)
final grid = PowerableGrid(1000)
..turnOn(const Point(887, 9), const Point(959, 629))
..turnOn(const Point(454, 398), const Point(844, 448))
..turnOff(const Point(539, 243), const Point(559, 965))
Expand All @@ -87,12 +90,12 @@ void main() {
});
}

extension GridPoweredExtension on Grid {
/// Whether all LEDs in this [Grid] are powered.
extension GridPoweredExtension on PowerableGrid {
/// Whether all LEDs in this [PowerableGrid] are powered.
bool get areAllPowered =>
leds.every((row) => row.every((led) => led.isPowered));

/// Whether all LEDs in this [Grid] are not powered.
/// Whether all LEDs in this [PowerableGrid] are not powered.
bool get areNonePowered =>
leds.every((row) => row.every((led) => !led.isPowered));

Expand Down
14 changes: 7 additions & 7 deletions test/led_test.dart → test/powerable_led_test.dart
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import 'package:christmas_lights_kata/led.dart';
import 'package:christmas_lights_kata/led/powerable_led.dart';
import 'package:test/test.dart';

void main() {
group('Led', () {
group('PowerableLed', () {
group('(new)', () {
test('is not powered by default', () {
final led = Led();
final led = PowerableLed();
expect(led.isPowered, isFalse);
print(led);
});

test('uses the powered value on construction', () {
final led = Led(isPowered: true);
final led = PowerableLed(isPowered: true);
expect(led.isPowered, isTrue);
print(led);
});
});

group('.turnOn()', () {
test('turns on this LED', () {
final led = Led()..turnOn();
final led = PowerableLed()..turnOn();
expect(led.isPowered, isTrue);
print(led);
});
});

group('.turnOff()', () {
test('turns off this LED', () {
final led = Led(isPowered: true)..turnOff();
final led = PowerableLed(isPowered: true)..turnOff();
expect(led.isPowered, isFalse);
print(led);
});
});

group('.toggle()', () {
test('toggles the powered state of this LED', () {
final led = Led()..toggle();
final led = PowerableLed()..toggle();
expect(led.isPowered, isTrue);
print(led);
led.toggle();
Expand Down