Last active
December 12, 2021 00:04
-
-
Save jpelgrim/ee72b5d43fb6455a24c465121565511f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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