1

Advent of Code 2022: Day 17

Серия Advent of Code 2022

Тетрис, больше тетриса! Этот тетрис оказался поголоволомней прошлого. Общая идея была понятна сразу, но «на местности» постоянно вылезали какие-то подводные камешки

Сначала думал пойти через byte[][], выставляя нужные биты в единицу (на что намекала ширина поля), но закопался в сдвигах (жаль, что ввод не оказался набором сдвиговых операций, или я не разгадал его).

Ну, хотя бы с идеей движения «окном» не промахнулся — во второй части она пригодилась.

Подготовительная работа

enum Move {

LEFT,

RIGHT;

private static final Map<Character, Move> cache = Map.of('<', LEFT, '>', RIGHT);

static Move of(char code) {

return cache.get(code);

}

}

class Triple {

public int left;

public int middle;

public List<Integer> right;

public Triple(int left, int middle, List<Integer> right) {

this.left = left;

this.middle = middle;

this.right = right;

}

public boolean equals(Object obj) {

if (obj == this) {

return true;

} else if (!(obj instanceof Triple)) {

return false;

} else {

Triple other = (Triple) obj;

return Objects.equals(left, other.left)

&& Objects.equals(middle, other.middle)

&& Objects.equals(right, other.right);

}

}

public int hashCode() {

return Objects.hashCode(left) ^ Objects.hashCode(middle)

^ Objects.hashCode(right);

}

}

static boolean hasMove(boolean[][] shape, int x, int y, Map<Integer, boolean[]> rows) {

for (int yIdx = 0; yIdx < shape.length; yIdx++) {

int currentY = y + (shape.length - yIdx);

if (rows.containsKey(currentY)) {

boolean[] shapeRow = shape[yIdx];

boolean[] fieldRow = rows.get(currentY);

for (int xIdx = 0; xIdx < shapeRow.length; xIdx++) {

if (fieldRow[x + xIdx] && shapeRow[xIdx]) return false;

}

}

}

return true;

}

Triple позаимствовал в Apache Commons, чтобы не изгаляться с импортом в консольный скрипт.

Время собирать камни

static void day17(String puzzleInputUri) throws IOException, InterruptedException {

boolean[][][] shapes = new boolean[][][] {

{ {true, true, true, true} }, // ___

{ {false, true, false}, {true, true, true}, {false, true, false} }, // +

{ {false, false, true}, {false, false, true}, {true, true, true} }, // _|

{ {true}, {true}, {true}, {true} }, // |

{ {true, true}, {true, true} } // #

};

List<Move> moves = client.send(request.uri((URI.create(puzzleInputUri))).build(), HttpResponse.BodyHandlers.ofLines())

.body()

.map(String::toCharArray)

.flatMap(chars -> CharBuffer.wrap(chars).chars().mapToObj(i -> (char) i))

.map(Move::of)

.collect(Collectors.toList());

int level = 0;

int ins = 0;

Map<Integer, boolean[]> rows = new HashMap<>();

Map<Triple, Map.Entry<Long, Integer>> visited = new HashMap<>();

long additionalLevel = 0;

// long totalRocks = 2022L;

long totalRocks = 1_000_000_000_000L;

for (long rock = 0; rock < totalRocks; rock++) {

int currentShapeIdx = (int) (rock % shapes.length);

boolean[][] shape = shapes[currentShapeIdx];

int x = 2;

int y = level + 3;

while (true) {

Move direction = moves.get(ins++ % moves.size());

if ((Move.LEFT == direction && x > 0) && hasMove(shape, x - 1, y, rows)) {

x--;

} else if ((Move.RIGHT == direction && x < 7 - shape[0].length)

&& hasMove(shape, x + 1, y, rows)) {

x++;

}

if (hasMove(shape, x, y - 1, rows) && y > 0) {

y--;

} else {

for (int yIdx = 0; yIdx < shape.length; yIdx++) {

int currentY = y + (shape.length - yIdx);

boolean[] shapeRow = shape[yIdx];

boolean[] fieldRow = rows.computeIfAbsent(currentY, it -> new boolean[7]);

for (int xIdx = 0; xIdx < shapeRow.length; xIdx++) {

fieldRow[x + xIdx] |= shapeRow[xIdx];

}

}

break;

}

}

var levels = rows.keySet().stream().mapToInt(Integer::intValue).sorted().toArray();

level = levels[levels.length - 1];

List<Integer> window = new ArrayList<>(Collections.nCopies(7, -1));

for (int i = 0; i < 7; i++) {

for (int j = levels.length - 1; j >= 0; j--) {

if (rows.get(levels[j])[i]) {

window.add(i, level - levels[j]);

break;

}

}

}

var visitedRow = new Triple((ins - 1) % moves.size(), currentShapeIdx, window);

if (visited.containsKey(visitedRow)) {

var previous = visited.get(visitedRow);

long repeat = (totalRocks - rock) / (rock - previous.getKey());

rock += (rock - previous.getKey()) * repeat;

additionalLevel += repeat * (level - previous.getValue());

}

visited.put(visitedRow, Map.entry(rock, level));

}

System.out.println(level + additionalLevel);

}

Ну и прочитанное когда-то интервью с создателем оригинального «Тетриса» тоже помогло — там он как раз описывал, почему в игре «сгорают» ряды.

А впереди — день восемнадцатый!

Вы смотрите срез комментариев. Показать все
0
Автор поста оценил этот комментарий

Может код лучше ссылкой через гист? А то он тут не читаемый совсем

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

А она встраивается? Если нет - проще на блевничок ссылку ставить. Но хотелось, чтобы в посте здесь всё было сразу, без необходимости по ссылкам бегать.

Вы смотрите срез комментариев. Чтобы написать комментарий, перейдите к общему списку

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества