2020aoc/Day11.java

247 lines
8.6 KiB
Java

package dev.forstenlechner.aoc;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
public class Day11 {
private enum SeatState {
EMPTY {
@Override
void print() {
System.out.print("L");
}
}, OCCUPIED {
@Override
void print() {
System.out.print("#");
}
}, FLOOR {
@Override
void print() {
System.out.print(".");
}
};
abstract void print();
static SeatState from(char c) {
return switch (c) {
case 'L' -> EMPTY;
case '#' -> OCCUPIED;
default -> FLOOR;
};
}
}
private record Location(int row, int col) {};
private enum Directon {
UP {
@Override
Location next(Location cur) {
return new Location(cur.row() -1, cur.col);
}
}, DOWN {
@Override
Location next(Location cur) {
return new Location(cur.row() +1, cur.col);
}
}, LEFT{
@Override
Location next(Location cur) {
return new Location(cur.row(), cur.col -1);
}
}, RIGHT{
@Override
Location next(Location cur) {
return new Location(cur.row(), cur.col +1);
}
}, LEFT_UP {
@Override
Location next(Location cur) {
return new Location(cur.row() -1, cur.col -1);
}
}, LEFT_DOWN {
@Override
Location next(Location cur) {
return new Location(cur.row() +1, cur.col -1);
}
}, RIGHT_UP {
@Override
Location next(Location cur) {
return new Location(cur.row() -1, cur.col +1);
}
}, RIGHT_DOWN {
@Override
Location next(Location cur) {
return new Location(cur.row() +1, cur.col +1);
}
};
abstract Location next(Location cur);
}
public static void main(String[] args) throws Exception {
new Day11().solve();
}
private List<List<SeatState>> copySeatMap(List<List<SeatState>> seatMap) {
return seatMap.stream().map(ArrayList::new).collect(toList());
}
private void solve() throws Exception {
Stream<String> lines = Files.lines(Paths.get("day11/input"));
List<List<SeatState>> seatMap = lines
.map(l -> l.chars().mapToObj(c -> (char) c).map(SeatState::from).collect(toList()))
.collect(toList());
solveFirst(seatMap);
solveSecond(seatMap);
}
private void printMap(List<List<SeatState>> seatMap) {
seatMap.forEach(row -> {
row.forEach(SeatState::print);
System.out.println();
});
}
private void solveSecond(List<List<SeatState>> seatMap) {
boolean changed = true;
while (changed) {
// printMap(seatMap);
// System.out.println();
changed = false;
var nextSeatMap = copySeatMap(seatMap);
for (int row = 0; row < seatMap.size(); row++) {
for (int column = 0; column < seatMap.get(row).size(); column++) {
SeatState prev = seatMap.get(row).get(column);
SeatState next = calcSeatWithDirection(seatMap, row, column);
changed |= prev != next;
nextSeatMap.get(row).set(column, next);
}
}
seatMap = nextSeatMap;
}
Long occupiedSeats = seatMap.stream().map(col -> col.stream().filter(state -> state == SeatState.OCCUPIED).count()).reduce(0L, Long::sum);
System.out.println(occupiedSeats);
}
private void solveFirst(List<List<SeatState>> seatMap) {
boolean changed = true;
while (changed) {
changed = false;
var nextSeatMap = copySeatMap(seatMap);
for (int row = 0; row < seatMap.size(); row++) {
for (int column = 0; column < seatMap.get(row).size(); column++) {
SeatState prev = seatMap.get(row).get(column);
SeatState next = calcSeat(seatMap, row, column);
changed |= prev != next;
nextSeatMap.get(row).set(column, next);
}
}
seatMap = nextSeatMap;
}
Long occupiedSeats = seatMap.stream().map(col -> col.stream().filter(state -> state == SeatState.OCCUPIED).count()).reduce(0L, Long::sum);
System.out.println(occupiedSeats);
}
private SeatState calcSeat(List<List<SeatState>> seatMap, int row, int column) {
int occupiedSeatsAround = 0;
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row-1, column-1);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row-1, column);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row-1, column+1);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row+1, column-1);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row+1, column);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row+1, column+1);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row, column-1);
occupiedSeatsAround += isSeatOccupiedInt(seatMap, row, column+1);
if (seatMap.get(row).get(column) == SeatState.EMPTY && occupiedSeatsAround == 0) {
return SeatState.OCCUPIED;
}
if (seatMap.get(row).get(column) == SeatState.OCCUPIED && occupiedSeatsAround >= 4) {
return SeatState.EMPTY;
}
return seatMap.get(row).get(column);
}
private int isSeatOccupiedInt(List<List<SeatState>> seatMap, int row, int column) {
if (row >= 0 && row < seatMap.size()
&& column >= 0 && column < seatMap.get(row).size()) {
return seatMap.get(row).get(column) == SeatState.OCCUPIED ? 1 : 0;
}
return 0;
}
private SeatState calcSeatWithDirection(List<List<SeatState>> seatMap, int row, int column) {
int occupiedSeatsAround = 0;
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.UP);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.DOWN);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.LEFT);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.RIGHT);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.LEFT_UP);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.LEFT_DOWN);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.RIGHT_UP);
occupiedSeatsAround += isSeatOccupiedIntWithDirection(seatMap, row, column, Directon.RIGHT_DOWN);
if (seatMap.get(row).get(column) == SeatState.EMPTY && occupiedSeatsAround == 0) {
return SeatState.OCCUPIED;
}
if (seatMap.get(row).get(column) == SeatState.OCCUPIED && occupiedSeatsAround >= 5) { // change in rule!
return SeatState.EMPTY;
}
return seatMap.get(row).get(column);
}
private int isSeatOccupiedIntWithDirection(List<List<SeatState>> seatMap, int row, int column, Directon directon) {
return isSeatOccupiedIntWithDirection(seatMap, new Location(row, column), directon);
}
private int isSeatOccupiedIntWithDirection(List<List<SeatState>> seatMap, Location test, Directon directon) {
test = directon.next(test);
while (test.row() >= 0 && test.row() < seatMap.size()
&& test.col() >= 0 && test.col() < seatMap.get(test.row()).size()
&& seatMap.get(test.row).get(test.col()) == SeatState.FLOOR) {
test = directon.next(test);
}
if (test.row() >= 0 && test.row() < seatMap.size()
&& test.col() >= 0 && test.col() < seatMap.get(test.row()).size()) {
return seatMap.get(test.row()).get(test.col()) == SeatState.OCCUPIED ? 1 : 0;
}
return 0;
}
}