Skip to content

Commit

Permalink
Parallelize
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 16, 2023
1 parent c366bba commit 19e6097
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 49 deletions.
3 changes: 2 additions & 1 deletion hs/src/Day16.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Description: <https://adventofcode.com/2023/day/16 Day 16: The Floor Will Be
module Day16 (part1, part2) where

import Control.Arrow (first, second)
import Control.Parallel.Strategies (parMap, rseq)
import Data.List (foldl')
import qualified Data.Set as Set (empty, fromList, insert, member, size, toList)
import Data.Text (Text)
Expand Down Expand Up @@ -49,7 +50,7 @@ part1, part2 :: Text -> Int
part1 = flip fill ((0, 0), R) . V.fromList . filter (not . T.null) . T.lines
part2 input
| V.null v = 0
| otherwise = maximum . map (fill v) $
| otherwise = maximum . parMap rseq (fill v) $
[((0, x), D) | x <- [0..T.length (V.head v) - 1]] ++
[((y, 0), R) | y <- [0..V.length v - 1]] ++
[((V.length v - 1, x), U) | x <- [0..T.length (V.last v) - 1]] ++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class Day16Bench {
@Benchmark
fun part1() = Day16(input).part1()

@Suppress("EXPOSED_FUNCTION_RETURN_TYPE")
@Benchmark
fun part2() = Day16(input).part2()
fun part2() = runSuspend {
Day16(input).part2()
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.github.ephemient.aoc2023

import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.reduce
import kotlinx.coroutines.launch

class Day16(input: String) {
private val input = input.lines().filter { it.isNotEmpty() }

private fun fill(y: Int, x: Int, d: Direction, todo: MutableSet<Pair<IntPair, Direction>>): Int {
private fun fill(y: Int, x: Int, d: Direction): Int {
val stack = mutableListOf(y to x to d)
val visited = stack.toMutableSet()
while (true) {
Expand All @@ -13,45 +17,29 @@ class Day16(input: String) {
if (p2.first in input.indices && p2.second in input[p2.first].indices) {
val next = p2 to d2
visited.add(next) && stack.add(next)
} else {
todo.remove(p1 to -d1)
}
}
}
return visited.mapTo(mutableSetOf()) { it.first }.size
}

fun part1(): Int = fill(0, 0, Direction.R, mutableSetOf())
fun part1(): Int = fill(0, 0, Direction.R)

fun part2(): Int {
var max = 0
val todo = mutableSetOf<Pair<IntPair, Direction>>()
for (x in 0..input.first().lastIndex) todo.add(0 to x to Direction.D)
suspend fun part2(): Int = channelFlow {
for (x in 0..input.first().lastIndex) {
launch { send(fill(0, x, Direction.D)) }
}
for ((y, line) in input.withIndex()) {
todo.add(y to 0 to Direction.R)
todo.add(y to line.lastIndex to Direction.L)
launch { send(fill(y, 0, Direction.R)) }
launch { send(fill(y, line.lastIndex, Direction.L)) }
}
for (x in 0..input.last().lastIndex) todo.add(input.lastIndex to x to Direction.U)
while (true) {
val iterator = todo.iterator()
if (!iterator.hasNext()) break
val (pos, d) = iterator.next()
iterator.remove()
max = maxOf(max, fill(pos.first, pos.second, d, todo))
for (x in 0..input.last().lastIndex) {
launch { send(fill(input.lastIndex, x, Direction.U)) }
}
return max
}
}.reduce(::maxOf)

private enum class Direction {
U, L, D, R,
;

operator fun unaryMinus(): Direction = when (this) {
U -> D
L -> R
D -> U
R -> L
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.ephemient.aoc2023

import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

Expand All @@ -10,7 +11,7 @@ class Day16Test {
}

@Test
fun part2() {
fun part2() = runTest {
assertEquals(51, Day16(example).part2())
}

Expand Down
26 changes: 19 additions & 7 deletions py/aoc2023/day12.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Day 12: Hot Springs
"""

import multiprocessing

SAMPLE_INPUT = """
???.### 1,1,3
.??..??...?##. 1,1,3
Expand All @@ -12,7 +14,7 @@
"""


def _solve(string: str, runs: list[int]):
def _solve(string, runs):
counts = [
i + runs[-1] <= len(string)
and string[i - 1 : i] != "#"
Expand Down Expand Up @@ -51,18 +53,28 @@ def part1(data):
)


def _solve2(words):
return _solve(
"?".join((words[0],) * 5), tuple(int(run) for run in words[1].split(",")) * 5
)


def part2(data):
"""
>>> part2(SAMPLE_INPUT)
525152
"""
return sum(
_solve(
"?".join((words[0],) * 5), tuple(int(x) for x in words[1].split(",")) * 5
with multiprocessing.Pool() as p:
return sum(
p.imap_unordered(
_solve2,
(
words
for line in data.splitlines()
if len(words := line.split(maxsplit=1)) == 2
),
)
)
for line in data.splitlines()
if len(words := line.split(maxsplit=1))
)


parts = (part1, part2)
34 changes: 25 additions & 9 deletions py/aoc2023/day16.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Day 16: The Floor Will Be Lava
"""

import multiprocessing
from enum import Enum
from itertools import chain

Expand Down Expand Up @@ -54,7 +55,7 @@ def _move(y, x, d):
return y, x + 1


def _fill(data: list[str], y: int, x: int, d: _Direction) -> int:
def _fill(data, y, x, d):
stack = [(y, x, d)]
visited = set(stack)
while stack:
Expand All @@ -79,21 +80,36 @@ def part1(data):
return _fill(list(filter(None, data.splitlines())), 0, 0, _Direction.R)


_DATA2 = []


def _initializer2(data):
_DATA2[:] = data


def _fill2(args):
y, x, d = args
return _fill(_DATA2, y, x, d)


def part2(data):
"""
>>> part2(SAMPLE_INPUT)
51
"""
data = list(filter(None, data.splitlines()))
return max(
_fill(data, y, x, d)
for y, x, d in chain(
((y, 0, _Direction.R) for y in range(len(data))),
((0, x, _Direction.D) for x in range(len(data[0]))),
((y, len(data[0]) - 1, _Direction.L) for y in range(len(data))),
((len(data) - 1, x, _Direction.U) for x in range(len(data[-1]))),
with multiprocessing.Pool(initializer=_initializer2, initargs=(data,)) as p:
return max(
p.imap_unordered(
_fill2,
chain(
((y, 0, _Direction.R) for y in range(len(data))),
((0, x, _Direction.D) for x in range(len(data[0]))),
((y, len(data[0]) - 1, _Direction.L) for y in range(len(data))),
((len(data) - 1, x, _Direction.U) for x in range(len(data[-1]))),
),
)
)
)


parts = (part1, part2)
4 changes: 2 additions & 2 deletions py/aoc2023/day7.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


# pylint: disable=too-many-return-statements
def _rank(hand: tuple[int]):
def _rank(hand):
counts = Counter(x for x in hand if x >= 0).most_common(2)
count0 = counts[0][1] if counts else 0
count1 = counts[1][1] if len(counts) > 1 else 0
Expand All @@ -34,7 +34,7 @@ def _rank(hand: tuple[int]):
return 0


def _solve(cards: str, data: str):
def _solve(cards, data):
hands = [
(_rank(hand := tuple(map(cards.find, words[0]))), hand, int(words[1]))
for line in data.splitlines()
Expand Down
1 change: 1 addition & 0 deletions rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rs/Cargo.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion rs/src/day12.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use itertools::Itertools;
use rayon::iter::{ParallelBridge, ParallelIterator};

fn solve<const N: usize>(line: &str) -> Option<usize> {
let (lhs, rhs) = line.split_once(' ')?;
Expand Down Expand Up @@ -64,7 +65,7 @@ pub fn part1(data: &str) -> usize {
}

pub fn part2(data: &str) -> usize {
data.lines().filter_map(solve::<5>).sum()
data.lines().par_bridge().filter_map(solve::<5>).sum()
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions rs/src/day16.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rayon::iter::{ParallelBridge, ParallelIterator};
use static_init::dynamic;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
Expand Down Expand Up @@ -78,6 +79,7 @@ pub fn part2(data: &str) -> Option<usize> {
(0..data.len()).filter_map(|y| Some((y, data[y].len().checked_sub(1)?, Direction::L))),
)
.chain((0..data.last()?.len()).map(|x| (data.len() - 1, x, Direction::U)))
.par_bridge()
.filter_map(|(y, x, d)| fill(&data, y, x, d))
.max()
}
Expand Down

0 comments on commit 19e6097

Please sign in to comment.