Skip to content

Instantly share code, notes, and snippets.

@jpelgrim
Last active December 12, 2021 00:04
Show Gist options
  • Save jpelgrim/ee72b5d43fb6455a24c465121565511f to your computer and use it in GitHub Desktop.
Save jpelgrim/ee72b5d43fb6455a24c465121565511f to your computer and use it in GitHub Desktop.
// Copyright 2019 the Dart project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.
import 'package:flutter/material.dart';
const initialMatrix = [
[4, 6, 5, 8, 1, 3, 7, 6, 3, 7],
[3, 2, 7, 7, 8, 7, 4, 3, 5, 5],
[4, 5, 2, 5, 6, 1, 1, 1, 8, 3],
[3, 1, 2, 8, 1, 2, 5, 8, 8, 8],
[8, 7, 3, 4, 8, 3, 2, 8, 3, 8],
[4, 1, 7, 5, 4, 6, 3, 2, 5, 7],
[8, 3, 2, 1, 4, 2, 3, 5, 5, 2],
[4, 8, 3, 2, 1, 4, 5, 2, 5, 3],
[8, 2, 8, 6, 8, 3, 4, 8, 5, 1],
[4, 8, 8, 5, 3, 2, 3, 1, 3, 8],
];
void main() {
runApp(const Day11());
}
class Day11 extends StatefulWidget {
const Day11({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _Day11State();
}
}
class _Day11State extends State<Day11> {
Matrix matrix = initialMatrix;
bool initialized = false;
int step = 0;
int popCount = 0;
bool stopped = true;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: Colors.black38,
),
home: Scaffold(
body: Container(
constraints: const BoxConstraints.expand(),
decoration: BoxDecoration(
border: Border.all(
color: Colors.transparent,
),
),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(step == 0 ? '' : 'Step $step'),
const SizedBox(height: 16.0),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.transparent,
),
),
child: MatrixGrid(matrix: matrix),
),
const SizedBox(height: 16.0),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
MaterialButton(
child: const Icon(
Icons.replay,
color: Colors.white,
size: 24.0,
),
onPressed: () {
_reset();
}),
MaterialButton(
child: const Icon(
Icons.stop,
color: Colors.white,
size: 24.0,
),
onPressed: () {
_stop();
}),
MaterialButton(
child: const Icon(
Icons.play_arrow,
color: Colors.white,
size: 24.0,
),
onPressed: () {
_go();
}),
MaterialButton(
child: const Icon(
Icons.plus_one_rounded,
color: Colors.white,
size: 24.0,
),
onPressed: () {
_plus1();
})
]),
const SizedBox(height: 16.0),
Text(step == 0 ? '' : 'Number of flashes: $popCount'),
],
),
),
),
),
);
}
void _reset() {
setState(() {
step = 0;
popCount = 0;
_initMatrix();
});
}
void _stop() {
setState(() {
stopped = true;
});
}
void _go() async {
if (!initialized) {
initialized = true;
_initMatrix();
}
stopped = false;
for (int i = step; i < 100; i++) {
if (stopped) break;
await Future.delayed(const Duration(milliseconds: 100));
await _takeStep();
}
}
void _plus1() => _takeStep();
Future<void> _takeStep() async {
final flashes = matrix.increaseEnergy();
if (flashes.isNotEmpty) {
final previousFlashes = Set<Point>.from(flashes);
do {
await Future.delayed(const Duration(milliseconds: 30));
final newFlashes = matrix.flashNeighbours(previousFlashes);
setState(() {});
if (newFlashes.isEmpty) {
break;
}
previousFlashes.clear();
previousFlashes.addAll(newFlashes);
flashes.addAll(newFlashes);
} while (true);
popCount += flashes.length;
}
setState(() {
step++;
});
}
void _initMatrix() {
matrix = List.empty(growable: true);
for (int y = 0; y < initialMatrix.length; y++) {
List<int> row = List.empty(growable: true);
for (int x = 0; x < initialMatrix[y].length; x++) {
row.add(initialMatrix[y][x]);
}
matrix.add(row);
}
}
}
typedef Matrix = List<MatrixRow>;
typedef MatrixRow = List<int>;
extension MatrixExtensions on Matrix {
Set<Point> increaseEnergy() {
final flashed = <Point>{};
for (int y = 0; y < length; y++) {
for (int x = 0; x < length; x++) {
var energy = this[y][x];
if (energy == 9) {
flashed.add(Point(x, y));
this[y][x] = 0;
} else {
this[y][x] = energy + 1;
}
}
}
return flashed;
}
Iterable<Point> flashNeighbours(Iterable<Point> flashed) {
final newFlashes = <Point>{};
for (var point in flashed) {
if (flash(Point(point.x - 1, point.y - 1))) {
newFlashes.add(Point(point.x - 1, point.y - 1));
}
if (flash(Point(point.x, point.y - 1))) {
newFlashes.add(Point(point.x, point.y - 1));
}
if (flash(Point(point.x + 1, point.y - 1))) {
newFlashes.add(Point(point.x + 1, point.y - 1));
}
if (flash(Point(point.x - 1, point.y))) {
newFlashes.add(Point(point.x - 1, point.y));
}
if (flash(Point(point.x + 1, point.y))) {
newFlashes.add(Point(point.x + 1, point.y));
}
if (flash(Point(point.x - 1, point.y + 1))) {
newFlashes.add(Point(point.x - 1, point.y + 1));
}
if (flash(Point(point.x, point.y + 1))) {
newFlashes.add(Point(point.x, point.y + 1));
}
if (flash(Point(point.x + 1, point.y + 1))) {
newFlashes.add(Point(point.x + 1, point.y + 1));
}
}
return newFlashes;
}
bool flash(Point point) {
if (point.x < 0 || point.x > 9 || point.y < 0 || point.y > 9) {
// This is a non-existing point in the matrix
return false;
}
var energy = this[point.y][point.x];
if (energy == 0) {
// This point already flashed
return false;
}
if (energy == 9) {
this[point.y][point.x] = 0;
return true;
} else {
this[point.y][point.x] = energy + 1;
}
return false;
}
bool allZeroes() => sum() == 0;
int sum() => fold<int>(0, (sum, row) => sum + row.sum());
List<Row> toGrid() {
final result = <Row>[];
for (int y = 0; y < length; y++) {
result.add(this[y].toRow());
}
return result;
}
}
const squidColors = [
Color.fromRGBO(255, 255, 255, 1.0),
Color.fromRGBO(25, 25, 255, 0.5),
Color.fromRGBO(50, 50, 255, 0.55),
Color.fromRGBO(75, 75, 255, 0.6),
Color.fromRGBO(100, 100, 255, 0.65),
Color.fromRGBO(125, 125, 255, 0.7),
Color.fromRGBO(150, 150, 255, 0.75),
Color.fromRGBO(175, 175, 255, 0.8),
Color.fromRGBO(200, 200, 255, 0.85),
Color.fromRGBO(225, 225, 255, 0.9),
];
const squidIcons = [
Icons.flash_on,
Icons.sentiment_dissatisfied_rounded,
Icons.sentiment_neutral_rounded,
Icons.sentiment_satisfied_rounded,
Icons.sentiment_very_satisfied_rounded,
];
extension MatrixRowExtensions on MatrixRow {
int sum() => fold<int>(0, (sum, element) => sum + element);
Row toRow() => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...map((e) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: Icon(
squidIcons[e~/2],
color: squidColors[e],
size: 24.0,
),
);
}),
],
);
}
class Point {
Point(this.x, this.y);
int x;
int y;
@override
bool operator ==(Object other) =>
other is Point && other.x == x && other.y == y;
@override
int get hashCode => 31 * x + y;
}
class MatrixGrid extends StatelessWidget {
const MatrixGrid({Key? key, required this.matrix}) : super(key: key);
final Matrix matrix;
@override
Widget build(BuildContext context) {
return Column(
children: [...matrix.toGrid()],
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment