Skip to content

Instantly share code, notes, and snippets.

@deanbot
Last active December 12, 2020 19:18
Show Gist options
  • Save deanbot/6159f11ee9089a2af110e9db2ec07a42 to your computer and use it in GitHub Desktop.
Save deanbot/6159f11ee9089a2af110e9db2ec07a42 to your computer and use it in GitHub Desktop.
Advent Of Code 2020 Day 11 in dart
void main() {
final List<Object> solutions = [
SolutionA(),
SolutionB()
];
solutions.forEach((s) => print(s.toString()));
}
// Full inputs at https://adventofcode.com/2020/day/11/input
const inputsDay11 = """L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL""";
// dart doens't have [0..10] range syntax
Iterable<int> range(length) => Iterable<int>.generate(length).toList();
enum SpaceType { floor, empty, occupied }
enum Dir { NW, N, NE, E, SE, S, SW, W }
const spaceTypes = {
'.': SpaceType.floor,
'L': SpaceType.empty,
'#': SpaceType.occupied
};
const spaceSymbols = {
SpaceType.floor: '.',
SpaceType.empty: 'L',
SpaceType.occupied: '#',
};
List<List<SpaceType>> _inputs = inputsDay11
.split('\n')
.where((element) => element.isNotEmpty)
.map<List<SpaceType>>((e) => e.split('').map((c) => spaceTypes[c]).toList())
.toList();
class Space {
final int x;
final int y;
final List<List<int>> adjacent;
SpaceType type;
Space(this.x, this.y, this.adjacent, this.type);
@override
String toString() => spaceSymbols[type];
}
class Grid {
final SpaceType occupied = SpaceType.occupied;
final SpaceType empty = SpaceType.empty;
final SpaceType floor = SpaceType.floor;
final int rowLength;
final int colLength;
List<List<Space>> _rows;
Grid.fromSpaceType(List<List<SpaceType>> inputs, {bool byLineOfSight = false})
: colLength = inputs.length,
// row range is built from a sample row (assumption: all rows are same length)
rowLength = inputs.length > 0 ? inputs[0].length : 0 {
SpaceType inputAt(int x, int y) => inputs[y][x];
bool isSeat(loc) => inputAt(loc[0], loc[1]) != floor;
final List<int> colRange = range(colLength), rowRange = range(rowLength);
_rows = colRange.map<List<Space>>((y) {
return rowRange.map<Space>((x) {
// calculate adjacents (either by contact or sight)
final SpaceType type = inputs[y][x];
List<List<int>> adjacent = [];
if (type != floor) {
if (!byLineOfSight) {
// get adjacents
final int n = y - 1, s = y + 1;
final List<SpaceType> row = inputs[y],
rowN = n >= 0 ? inputs[n] : [],
rowS = s <= colLength - 1 ? inputs[s] : [];
adjacent = [
..._adjacentX(rowN, x, n, includeSelf: true),
..._adjacentX(row, x, y),
..._adjacentX(rowS, x, s, includeSelf: true),
].toList();
} else {
adjacent = Dir.values
.map<List<int>>((dir) => locationsInDirection([x, y], dir)
.firstWhere(isSeat, orElse: () => null))
.whereType<List<int>>()
.toList();
}
}
return Space(x, y, adjacent, type);
}).toList();
}).toList();
}
Iterable<List<int>> locationsInDirection(List<int> loc, Dir dir) sync* {
var rX = 0, rY = 0;
switch (dir) {
case Dir.NW:
rX = -1;
rY = -1;
break;
case Dir.N:
rX = 0;
rY = -1;
break;
case Dir.NE:
rX = 1;
rY = -1;
break;
case Dir.E:
rX = 1;
rY = 0;
break;
case Dir.SE:
rX = 1;
rY = 1;
break;
case Dir.S:
rX = 0;
rY = 1;
break;
case Dir.SW:
rX = -1;
rY = 1;
break;
case Dir.W:
rX = -1;
rY = 0;
}
int x = loc[0] + rX, y = loc[1] + rY;
bool edgeReached =
!(x >= 0 && x <= rowLength - 1 && y >= 0 && y <= colLength - 1);
if (!edgeReached) {
yield [x, y];
yield* locationsInDirection([x, y], dir);
}
}
int _itt = 0;
int _lastChanged = 0;
tick({int tolerance = 4}) {
// print('Itteration ${_itt++}');
// print(this.toString());
// get changes
bool isSeat(e) => e.type != floor;
var changes = []; // [Space space, SpaceType newType]
_rows.forEach((row) {
row.where(isSeat).forEach((seat) {
if (seat.type == occupied) {
// if adjacent where occupied length > tolerance then change to empty
var adjacentFilledCount = seat.adjacent.where((loc) {
final x = loc[0], y = loc[1];
return spaceAt(x, y).type == occupied;
}).length;
if (adjacentFilledCount > tolerance - 1) {
changes.add([seat, empty]);
}
} else {
// if every adjacent is not occupied then change to occupied
var adjacentEmpty = seat.adjacent.every((loc) {
final x = loc[0], y = loc[1];
return spaceAt(x, y).type != occupied;
});
if (seat.adjacent.isEmpty || adjacentEmpty) {
changes.add([seat, occupied]);
}
}
});
});
// apply changes
changes.forEach((change) {
final Space space = change[0];
final SpaceType newType = change[1];
spaceAt(space.x, space.y).type = newType;
});
if (_lastChanged != changes.length) {
_lastChanged = changes.length;
return tick(tolerance: tolerance);
} else {
return occupiedCount;
}
}
Space spaceAt(int x, int y) => _rows[y][x];
int get occupiedCount => _rows.fold(
0, (v, row) => v + row.where((s) => s.type == occupied).length);
List<List<int>> _adjacentX(List<SpaceType> seats, int x, int y,
{bool includeSelf = false}) {
if (seats.length == 0) {
return [];
}
final int e = x + 1, w = x - 1;
final bool eAdj = e <= seats.length - 1, wAdj = w >= 0;
List<List<int>> adjacent = [];
if (wAdj) {
if (seats[w] != floor) {
adjacent.add([w, y]);
}
}
if (includeSelf && seats[x] != floor) {
adjacent.add([x, y]);
}
if (eAdj) {
if (seats[e] != floor) {
adjacent.add([e, y]);
}
}
return adjacent;
}
@override
String toString() {
return _rows
.map<String>((r) => r.map((s) => s.toString()).join(''))
.join("\n");
}
}
abstract class AdventSolution {
final int day;
final String name;
AdventSolution(this.day, this.name);
// implement in subclass
String getSolution();
@override
String toString() {
return "Advent Of Code, "
"Day $day "
"${name.isNotEmpty ? name : ''} "
"solution: ${getSolution()}";
}
}
abstract class _Day11Solution extends AdventSolution {
_Day11Solution(name) : super(11, name);
}
class SolutionA extends _Day11Solution {
SolutionA() : super('A');
String getSolution() {
return Grid.fromSpaceType(_inputs).tick().toString();
}
}
class SolutionB extends _Day11Solution {
SolutionB() : super('B');
String getSolution() {
return Grid.fromSpaceType(_inputs, byLineOfSight: true)
.tick(tolerance: 5)
.toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment