Skip to content

Instantly share code, notes, and snippets.

@maartenpaauw
Created December 6, 2023 21:18
Show Gist options
  • Save maartenpaauw/315e3ec3ab548f06c0510e379229e756 to your computer and use it in GitHub Desktop.
Save maartenpaauw/315e3ec3ab548f06c0510e379229e756 to your computer and use it in GitHub Desktop.
Advent of Code 2023 - Day 5
<?php
use Illuminate\Support\Collection;
use Illuminate\Support\Stringable;
use Illuminate\Support\Str;
$input = "seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4";
final readonly class Mapper
{
private Collection $ranges;
/**
* @param Collection<array-key, Range> $ranges
*/
public function __construct(Collection $ranges)
{
$this->ranges = $ranges
->sortBy(static fn (Range $range): int => $range->sourceStart())
->values();
}
/**
* @return Collection<array-key, Seed>
*/
public function transform(Seed $seed): Collection
{
foreach ($this->ranges->all() as $range) {
if ($seed->within($range)) {
return Collection::wrap(new Seed(
start: $seed->start() + $range->difference(),
amount: $seed->amount(),
));
}
}
if ($this->ranges->every(static fn (Range $range): bool => $seed->outside($range))) {
return Collection::wrap($seed);
}
foreach ($this->ranges->all() as $range) {
if ($seed->overlapEnd($range)) {
$amount = $range->sourceEnd() - $seed->start() + 1;
return $this->transform(new Seed(
start: $seed->start() + $amount,
amount: $seed->amount() - $amount,
))->merge([new Seed(
start: $seed->start() + $range->difference(),
amount: $amount,
)]);
} else if ($seed->overlapStart($range)) {
$amount = $seed->end() - $range->sourceStart() + 1;
return $this->transform(new Seed(
start: $seed->end() - $seed->amount() + 1,
amount: $seed->amount() - $amount,
))->merge([new Seed(
start: $range->sourceStart(),
amount: $amount,
)]);
}
}
throw new LogicException();
}
}
final readonly class Range
{
public function __construct(
private int $source,
private int $destination,
private int $length,
) {
}
public function sourceStart(): int
{
return $this->source;
}
public function sourceEnd(): int
{
return $this->source + $this->length - 1;
}
public function difference(): int
{
return $this->destination + -$this->sourceStart();
}
}
final readonly class Seed
{
public function __construct(
private int $start,
private int $amount,
) {
}
public function start(): int
{
return $this->start;
}
public function end(): int
{
return $this->start + $this->amount - 1;
}
public function amount(): int
{
return $this->amount;
}
public function outside(Range $range): bool
{
return $this->end() < $range->sourceStart() || $this->start() > $range->sourceEnd();
}
public function within(Range $range): bool
{
return $this->start() >= $range->sourceStart() && $this->end() <= $range->sourceEnd();
}
public function overlapStart(Range $range): bool
{
return $this->start() <= $range->sourceStart() && $this->end() >= $range->sourceStart();
}
public function overlapEnd(Range $range): bool
{
return $this->end() >= $range->sourceEnd() && $this->start() <= $range->sourceEnd();
}
}
/** @var Collection<array-key, Stringable> $result */
$result = Str::of($input)
->split('/\n\n/')
->mapInto(Stringable::class);
/** @var Collection<array-key, int> $seeds */
$seeds = $result
->shift()
->matchAll('/\d+/')
->map(static fn (string $seed): int => intval($seed));
$mappers = $result
->map(static fn (Stringable $notation) => new Mapper(
$notation
->split('/\n/')
->mapInto(Stringable::class)
->slice(1)
->map(static function (Stringable $range) {
return $range
->matchAll('/\d+/')
->map(static fn (string $number): int => intval($number));
})
->map(static fn (Collection $config) => new Range(
$config->get(1),
$config->get(0),
$config->get(2),
))
->sortBy(static fn (Range $range): int => $range->sourceStart())
->values(),
));
$singleSeeds = $seeds
->map(static fn (int $seed) => new Seed($seed, 1))
->sort(static fn (Seed $seed) => $seed->start());
$part1 = $mappers
->reduce(static function (Collection $seeds, Mapper $mapper) {
return $seeds
->map(static fn (Seed $seed) => $mapper->transform($seed))
->flatten(1);
}, $singleSeeds)
->map(static fn (Seed $seed): int => $seed->start())
->min();
$seedRanges = $seeds
->chunk(2)
->map(static fn (Collection $range) => new Seed(
$range->first(),
$range->last(),
))
->sort(static fn (Seed $seed) => $seed->start());
$part2 = $mappers
->reduce(static function (Collection $seeds, Mapper $mapper) {
return $seeds
->map(static fn (Seed $seed) => $mapper->transform($seed))
->flatten(1);
}, $seedRanges)
->map(static fn (Seed $seed): int => $seed->start())
->min();
$result = [$part1, $part2];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment