1
0

Preparations for AdventOfCode section

This commit is contained in:
Mike Schwörer 2019-11-02 20:19:34 +01:00
parent e12d410015
commit e00edb7425
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
106 changed files with 19401 additions and 11 deletions

View File

@ -0,0 +1,128 @@
<?php
class AdventOfCode
{
const YEARS =
[
'2018' => [ 'url-aoc'=>'https://adventofcode.com/2018/day/', 'blog-id' => 23, 'github' => 'https://github.com/Mikescher/AdventOfCode2018' ],
'2019' => [ 'url-aoc'=>'https://adventofcode.com/2019/day/', 'blog-id' => 24, 'github' => 'https://github.com/Mikescher/AdventOfCode2019' ],
];
const LANGUAGES =
[
'cs' => ['ext'=>'linq', 'css'=>'language-csharp', 'name'=>'C#'],
];
public static function listAllFromAllYears()
{
$all = require (__DIR__ . '/../statics/aoc/__all.php');
array_walk($all, function(&$value, $year) { array_walk($value, function (&$innervalue) use ($year) { $innervalue = self::readSingle($year, $innervalue); }); });
return $all;
}
public static function listSingleYear($year)
{
$all = require (__DIR__ . '/../statics/aoc/__all.php');
return array_map('self::readSingle', $all[$year]);
}
public static function listYears()
{
$all = require (__DIR__ . '/../statics/aoc/__all.php');
return array_keys($all);
}
public static function readSingle($year, $a)
{
$yeardata = self::YEARS[$year];
$n2p = str_pad($a['day'], 2, '0', STR_PAD_LEFT);
$a['day-padded'] = $n2p;
$a['url'] = '/blog/' . $yeardata['blog-id'] . '/Advent_of_Code_' . $year . '/day-' . $n2p;
$a['canonical'] = "https://www.mikescher.com" . $a['url'];
$a['url_aoc'] = $yeardata['url-aoc'] . $a['day']; // adventofcode.com/{year}/day/{day}
$a['file_challenge'] = (__DIR__ . '/../statics/aoc/'.$year.'/'.$n2p.'_challenge.txt');
$a['file_input'] = (__DIR__ . '/../statics/aoc/'.$year.'/'.$n2p.'_input.txt');
$solutionfiles = [];
foreach ($a['languages'] as $language)
{
for ($i=1; $i <= $a['parts']; $i++)
{
$solutionfiles []= (__DIR__ . '/../statics/aoc/' . $year . '/' . $n2p . '-' . $i . '.' . self::LANGUAGES[$language]['ext']);
}
}
$a['file_solutions'] = $solutionfiles;
return $a;
}
public static function getDayFromStrIdent($year, $ident)
{
$e = explode('-', $ident, 2); // day-xxx
if (count($e)!==2) return null;
$i = intval($e[1], 10);
if ($i == 0) return null;
return self::getSingleDay($year, $i);
}
public static function getSingleDay($year, $day)
{
foreach (self::listSingleYear($year) as $aocd) {
if ($aocd['day'] == $day) return $aocd;
}
return null;
}
public static function checkConsistency()
{
$warn = null;
foreach (self::listAllFromAllYears() as $year => $yd)
{
$daylist = [];
$titlelist = [];
if (!array_key_exists($year, self::YEARS)) return ['result'=>'err', 'message' => 'Invalid Year: ' . $year];
foreach ($yd as $aocdata)
{
if (in_array($aocdata['day'], $daylist)) return ['result'=>'err', 'message' => 'Duplicate day ' . $aocdata['day']];
$daylist []= $aocdata['day'];
if (in_array($aocdata['title'], $titlelist)) return ['result'=>'err', 'message' => 'Duplicate title ' . $aocdata['title']];
$titlelist []= $aocdata['title'];
if (count($aocdata['solutions']) !== $aocdata['parts']) return ['result'=>'err', 'message' => 'Not enough solution-values in day' . $aocdata['day']];
if (count($aocdata['file_solutions']) !== $aocdata['parts']) return ['result'=>'err', 'message' => 'Not enough solution-files in day' . $aocdata['day']];
if (!file_exists($aocdata['file_challenge'])) return ['result'=>'err', 'message' => 'file_challenge not found ' . $aocdata['file_challenge']];
if (!file_exists($aocdata['file_input'])) return ['result'=>'err', 'message' => 'file_input not found ' . $aocdata['file_input']];
foreach ($aocdata['file_solutions'] as $sfile)
{
if (!file_exists($sfile)) return ['result'=>'err', 'message' => 'file_solution[?] not found ' . $sfile];
}
foreach ($aocdata['languages'] as $lang)
{
if (!array_key_exists($lang, self::LANGUAGES)) return ['result'=>'err', 'message' => 'Unknown language ' . $lang];
}
}
}
if ($warn != null) return $warn;
return ['result'=>'ok', 'message' => ''];
}
}

View File

@ -6,10 +6,10 @@ class Blog
{ {
$all = require (__DIR__ . '/../statics/blog/__all.php'); $all = require (__DIR__ . '/../statics/blog/__all.php');
return array_map('self::completeSingle', $all); return array_map('self::readSingle', $all);
} }
private static function completeSingle($d) private static function readSingle($d)
{ {
if ($d['cat']==='blog') if ($d['cat']==='blog')
$d['url'] = "/blog/" . $d['id'] . "/" . destructiveUrlEncode($d['title']); $d['url'] = "/blog/" . $d['id'] . "/" . destructiveUrlEncode($d['title']);
@ -20,6 +20,8 @@ class Blog
$d['file_fragment'] = __DIR__ . '/../statics/blog/' . $d['fragment']; $d['file_fragment'] = __DIR__ . '/../statics/blog/' . $d['fragment'];
if (!array_key_exists('extras', $d)) $d['extras'] = [];
return $d; return $d;
} }
@ -66,6 +68,12 @@ class Blog
// aok // aok
} else if ($post['type'] === 'aoc') {
if (!array_key_exists('aoc:year', $post['extras'])) return ['result'=>'err', 'message' => 'AdventOfCode metadata [aoc:year] missing: ' . $post['title']];
// aok
} else { } else {
return ['result'=>'err', 'message' => 'Unknown type ' . $post['type']]; return ['result'=>'err', 'message' => 'Unknown type ' . $post['type']];

View File

@ -34,7 +34,7 @@ class Euler
public static function getEulerProblemFromStrIdent($ident) public static function getEulerProblemFromStrIdent($ident)
{ {
$e = explode('-', $ident, 2); $e = explode('-', $ident, 2); // problem-xxx
if (count($e)!==2) return null; if (count($e)!==2) return null;
$i = intval($e[1], 10); $i = intval($e[1], 10);

View File

@ -6,6 +6,7 @@ require_once (__DIR__ . '/../internals/highscores.php');
require_once (__DIR__ . '/../internals/alephnoteStatistics.php'); require_once (__DIR__ . '/../internals/alephnoteStatistics.php');
require_once (__DIR__ . '/../internals/blog.php'); require_once (__DIR__ . '/../internals/blog.php');
require_once (__DIR__ . '/../internals/euler.php'); require_once (__DIR__ . '/../internals/euler.php');
require_once (__DIR__ . '/../internals/adventofcode.php');
require_once (__DIR__ . '/../internals/highscores.php'); require_once (__DIR__ . '/../internals/highscores.php');
require_once (__DIR__ . '/../internals/mikeschergitgraph.php'); require_once (__DIR__ . '/../internals/mikeschergitgraph.php');
require_once (__DIR__ . '/../internals/programs.php'); require_once (__DIR__ . '/../internals/programs.php');
@ -21,6 +22,7 @@ $consistency_books = Books::checkConsistency();
$consistency_egh = MikescherGitGraph::checkConsistency(); $consistency_egh = MikescherGitGraph::checkConsistency();
$consistency_progimg = Programs::checkThumbnails(); $consistency_progimg = Programs::checkThumbnails();
$consistency_bookimg = Books::checkThumbnails(); $consistency_bookimg = Books::checkThumbnails();
$consistency_aoc = AdventOfCode::checkConsistency();
?> ?>
<?php <?php
@ -77,6 +79,7 @@ function dumpConsistency($c) {
<div><span>Book thumbnails:</span> <?php dumpConsistency($consistency_bookimg); ?></div> <div><span>Book thumbnails:</span> <?php dumpConsistency($consistency_bookimg); ?></div>
<div><span>Blog data:</span> <?php dumpConsistency($consistency_blog); ?></div> <div><span>Blog data:</span> <?php dumpConsistency($consistency_blog); ?></div>
<div><span>Euler data:</span> <?php dumpConsistency($consistency_euler); ?></div> <div><span>Euler data:</span> <?php dumpConsistency($consistency_euler); ?></div>
<div><span>AdventOfCode data:</span> <?php dumpConsistency($consistency_aoc); ?></div>
<div><span>Programs data:</span> <?php dumpConsistency($consistency_prog); ?></div> <div><span>Programs data:</span> <?php dumpConsistency($consistency_prog); ?></div>
<div><span>Books data:</span> <?php dumpConsistency($consistency_books); ?></div> <div><span>Books data:</span> <?php dumpConsistency($consistency_books); ?></div>
</div> </div>

View File

@ -9,12 +9,10 @@ $id = $OPTIONS['id'];
$subview = $OPTIONS['subview']; $subview = $OPTIONS['subview'];
$post = Blog::getBlogpost($id); $post = Blog::getBlogpost($id);
if ($post === NULL) httpError(404, 'Blogpost not found'); if ($post === NULL) httpError(404, 'Blogpost not found');
$isSubEuler = ($post['type'] === 'euler' && $subview !== ''); $isSubEuler = ($post['type'] === 'euler' && $subview !== '');
$isBaseEuler = ($post['type'] === 'euler'); $isBaseEuler = ($post['type'] === 'euler');
$eulerproblem = null; $eulerproblem = null;
if ($isSubEuler) if ($isSubEuler)
{ {
@ -23,13 +21,31 @@ if ($isSubEuler)
} }
if ($eulerproblem === null) $isSubEuler = false; if ($eulerproblem === null) $isSubEuler = false;
$isSubAdventOfCode = ($post['type'] === 'aoc' && $subview !== '');
$isAdventOfCode = ($post['type'] === 'aoc');
$adventofcodeday = null;
if ($isSubAdventOfCode)
{
require_once(__DIR__ . '/../internals/adventofcode.php');
$adventofcodeday = AdventOfCode::getDayFromStrIdent($post['extras']['aoc:year'], $subview);
}
if ($adventofcodeday === null) $isSubAdventOfCode = false;
$title = $post['title'];
if ($isSubEuler) $title = $eulerproblem['title'];
if ($isSubAdventOfCode) $title = $adventofcodeday['title'];
$canonical = $post['canonical'];
if ($isSubEuler) $canonical = $eulerproblem['canonical'];
if ($isSubAdventOfCode) $canonical = $adventofcodeday['canonical'];
?> ?>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Mikescher.com - <?php echo ($isSubEuler ? $eulerproblem['title'] : $post['title']); ?></title> <title>Mikescher.com - <?php echo $title; ?></title>
<link rel="icon" type="image/png" href="/data/images/favicon.png"/> <link rel="icon" type="image/png" href="/data/images/favicon.png"/>
<?php printCSS(); ?> <?php printCSS(); ?>
<?php echo '<link rel="canonical" href="' . ($isSubEuler ? $eulerproblem['canonical'] : $post['canonical']) . '"/>'; ?> <?php echo '<link rel="canonical" href="' . $canonical . '"/>'; ?>
</head> </head>
<body> <body>
@ -53,15 +69,16 @@ if ($eulerproblem === null) $isSubEuler = false;
include (__DIR__ . '/../fragments/blogview_markdown.php'); include (__DIR__ . '/../fragments/blogview_markdown.php');
} elseif ($post['type'] === 'bfjoust') {
include (__DIR__ . '/../fragments/blogview_bfjoust.php');
} elseif ($post['type'] === 'euler') { } elseif ($post['type'] === 'euler') {
if ($subview === '') include (__DIR__ . '/../fragments/blogview_euler_list.php'); if ($subview === '') include (__DIR__ . '/../fragments/blogview_euler_list.php');
else include (__DIR__ . '/../fragments/blogview_euler_single.php'); else include (__DIR__ . '/../fragments/blogview_euler_single.php');
} elseif ($post['type'] === 'aoc') {
if ($subview === '') include (__DIR__ . '/../fragments/blogview_aoc_list.php');
else include (__DIR__ . '/../fragments/blogview_aoc_single.php');
} }
?> ?>

View File

@ -0,0 +1,6 @@
<Query Kind="Expression" />
File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"01_input.txt"))
.Select(int.Parse)
.Sum()

View File

@ -0,0 +1,9 @@
<Query Kind="Statements" />
HashSet<int> visited = new HashSet<int>();
int sum = 0;
foreach (var v in Enumerable.Repeat(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"01_input.txt")).Select(int.Parse), int.MaxValue).SelectMany(p=>p))
{
sum += v;
if (!visited.Add(sum)) { sum.Dump(); return; }
}

View File

@ -0,0 +1,53 @@
--- Day 1: Chronal Calibration ---
"We've detected some temporal anomalies," one of Santa's Elves at the Temporal Anomaly Research and Detection Instrument Station tells you. She sounded pretty worried when she called you down here. "At 500-year intervals into the past, someone has been changing Santa's history!"
"The good news is that the changes won't propagate to our time stream for another 25 days, and we have a device" - she attaches something to your wrist - "that will let you fix the changes with no such propagation delay. It's configured to send you 500 years further into the past every few days; that was the best we could do on such short notice."
"The bad news is that we are detecting roughly fifty anomalies throughout time; the device will indicate fixed anomalies with stars. The other bad news is that we only have one device and you're the best person for the job! Good lu--" She taps a button on the device and you suddenly feel like you're falling. To save Christmas, you need to get all fifty stars by December 25th.
Collect stars by solving puzzles. Two puzzles will be made available on each day in the advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!
After feeling like you've been falling for a few minutes, you look at the device's tiny screen. "Error: Device must be calibrated before first use. Frequency drift detected. Cannot maintain destination lock." Below the message, the device shows a sequence of changes in frequency (your puzzle input). A value like +6 means the current frequency increases by 6; a value like -3 means the current frequency decreases by 3.
For example, if the device displays frequency changes of +1, -2, +3, +1, then starting from a frequency of zero, the following changes would occur:
Current frequency 0, change of +1; resulting frequency 1.
Current frequency 1, change of -2; resulting frequency -1.
Current frequency -1, change of +3; resulting frequency 2.
Current frequency 2, change of +1; resulting frequency 3.
In this example, the resulting frequency is 3.
Here are other example situations:
+1, +1, +1 results in 3
+1, +1, -2 results in 0
-1, -2, -3 results in -6
Starting with a frequency of zero, what is the resulting frequency after all of the changes in frequency have been applied?
--- Part Two ---
You notice that the device repeats the same frequency change list over and over. To calibrate the device, you need to find the first frequency it reaches twice.
For example, using the same list of changes above, the device would loop as follows:
Current frequency 0, change of +1; resulting frequency 1.
Current frequency 1, change of -2; resulting frequency -1.
Current frequency -1, change of +3; resulting frequency 2.
Current frequency 2, change of +1; resulting frequency 3.
(At this point, the device continues from the start of the list.)
Current frequency 3, change of +1; resulting frequency 4.
Current frequency 4, change of -2; resulting frequency 2, which has already been seen.
In this example, the first frequency reached twice is 2. Note that your device might need to repeat its list of frequency changes many times before a duplicate frequency is found, and that duplicates might be found while in the middle of processing the list.
Here are other examples:
+1, -1 first reaches 0 twice.
+3, +3, +4, -2, -4 first reaches 10 twice.
-6, +3, +8, +5, -6 first reaches 5 twice.
+7, +7, -2, -7, -4 first reaches 14 twice.
What is the first frequency your device reaches twice?

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
<Query Kind="Expression" />
File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"02_input.txt"))
.Select(s => (s.GroupBy(p => p).Any(g => g.Count() == 2) ? 1 : 0, s.GroupBy(p => p).Any(g => g.Count() == 3) ? 1 : 0))
.Select(p => new[] { p.Item1, p.Item2 })
.Aggregate((a, b) => new[] { a[0] + b[0], a[1] + b[1] })
.Aggregate((a, b) => a * b)

View File

@ -0,0 +1,11 @@
<Query Kind="Statements" />
var data = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"02_input.txt"));
data
.Select(d1 => data.Where(d2 => d1.Zip(d2.ToCharArray(), (a,b) => a!=b).Count(p=>p)==1 ))
.Where(p => p.Any())
.Select(p => p.First())
.Aggregate((a, b) => new string(a.Zip(b.ToCharArray(), (u,v)=>(u==v?u:'_')).ToArray()) )
.Replace("_", "")
.Dump();

View File

@ -0,0 +1,43 @@
--- Day 2: Inventory Management System ---
You stop falling through time, catch your breath, and check the screen on the device. "Destination reached. Current Year: 1518. Current Location: North Pole Utility Closet 83N10." You made it! Now, to find those anomalies.
Outside the utility closet, you hear footsteps and a voice. "...I'm not sure either. But now that so many people have chimneys, maybe he could sneak in that way?" Another voice responds, "Actually, we've been working on a new kind of suit that would let him fit through tight spaces like that. But, I heard that a few days ago, they lost the prototype fabric, the design plans, everything! Nobody on the team can even seem to remember important details of the project!"
"Wouldn't they have had enough fabric to fill several boxes in the warehouse? They'd be stored together, so the box IDs should be similar. Too bad it would take forever to search the warehouse for two similar box IDs..." They walk too far away to hear any more.
Late at night, you sneak to the warehouse - who knows what kinds of paradoxes you could cause if you were discovered - and use your fancy wrist device to quickly scan every box and produce a list of the likely candidates (your puzzle input).
To make sure you didn't miss any, you scan the likely candidate boxes again, counting the number that have an ID containing exactly two of any letter and then separately counting those with exactly three of any letter. You can multiply those two counts together to get a rudimentary checksum and compare it to what your device predicts.
For example, if you see the following box IDs:
abcdef contains no letters that appear exactly two or three times.
bababc contains two a and three b, so it counts for both.
abbcde contains two b, but no letter appears exactly three times.
abcccd contains three c, but no letter appears exactly two times.
aabcdd contains two a and two d, but it only counts once.
abcdee contains two e.
ababab contains three a and three b, but it only counts once.
Of these box IDs, four of them contain a letter which appears exactly twice, and three of them contain a letter which appears exactly three times. Multiplying these together produces a checksum of 4 * 3 = 12.
What is the checksum for your list of box IDs?
--- Part Two ---
Confident that your list of box IDs is complete, you're ready to find the boxes full of prototype fabric.
The boxes will have IDs which differ by exactly one character at the same position in both strings. For example, given the following box IDs:
abcde
fghij
klmno
pqrst
fguij
axcye
wvxyz
The IDs abcde and axcye are close, but they differ by two characters (the second and fourth). However, the IDs fghij and fguij differ by exactly one character, the third (h and u). Those must be the correct boxes.
What letters are common between the two correct box IDs? (In the example above, this is found by removing the differing character from either ID, producing fgij.)

View File

@ -0,0 +1,250 @@
ayitmcjvlhedbsyoqfzukjpxwt
agirmcjvlheybsyogfzuknpxxt
wgirmcjvlvedbsyoqfzujnpxwt
agizmcjvlhedbsyoqfzuenlxwt
aryrmcjvlheebsyoqfzuknpxwt
agirmcjelhedbsyoqfzukosxwt
azirmcjvlhedbsooqfzuknpxvt
agirmcjvffedbsyoqfzudnpxwt
agilmcjvlhedbsyrqfzuknpxrt
agirmcjvlhndbsyoofzukcpxwt
awirmcjvlhedbsyoqfzuknpxlz
aghrmcjmlhewbsyoqfzuknpxwt
apirmcjvlmedbsyoqfzcknpxwt
jgiricjvlhedbsyrqfzuknpxwt
abirmcjvlbedbsyoqfzuknpxwo
agirmcjvlhedbsyojfzuknpgkt
agicmclvlhedbmyoqfzuknpxwt
aslrzcjvlhedbsyoqfzuknpxwt
agiqmcjvlhedbsymqfzurnpxwt
agirmcjvlwedbsyoqfzuknfxmt
agiumcjvlhedbsyoqfzuknpbyt
xgirxcjvlwedbsyoqfzuknpxwt
bgtrvcjvlhedbsyoqfzuknpxwt
afirmcjvlpedbsyoqvzuknpxwt
agirmcjjvhedbsyoqfzukmpxwt
ggirmcjvlhedbsyoqfzukypxvt
agirmdjulhekbsyoqfzuknpxwt
agirmcjqlhedbsyoqfztknixwt
agirmcjvjhedbsyomfduknpxwt
agirmcjvlhedbgyoqfzuknpxtq
agirmvjvlhbdbsyfqfzuknpxwt
agirmcjvlhedbsyatfzbknpxwt
agirmcjvlrlybsyoqfzuknpxwt
agirmajvlhedbsqovfzuknpxwt
abinmcrvlhedbsyoqfzuknpxwt
agnrmcjvlhedbsyurfzuknpxwt
agirmpjvlhedbsyoqezuknpxct
agirmxjvlhedbsgoqjzuknpxwt
agirmcjvlhehbstoqfzuknpxht
qgirmcjvlhepcsyoqfzuknpxwt
tgirmcjvlhkdbsyoqszuknpxwt
agirmcjvdhedbscoqftuknpxwt
agbrmcjvlhedbsyoqfzukqpxwj
agurmcjvlhedbsyaqfzuknpxmt
agirmcjvohudbsyoqfmuknpxwt
agirmcjvlhekbsyoqfbuktpxwt
agirmcjvlhedhsyoqfzugnnxwt
agirmcjvlhedbsyjqyzuknpxft
agirmcjvlhedbsymufznknpxwt
agirmcjhlheubsyoqfzuknpxmt
agirmcjvlhwdbsywqfzwknpxwt
agirmcjvljedbsgqqfzuknpxwt
aglrmcjelhedbsyoqfzuknpxkt
agxrmcjvlhxdbsyoqfquknpxwt
agirmcjvnhedbsyoqfzuenfxwt
agirmcjvlhedbsyoqfzatnqxwt
agirmcvvlhedbsboqfzuknuxwt
agirncjvlhezbsyoqfzulnpxwt
agiamcjvdiedbsyoqfzuknpxwt
agirmcjvwhedbskoqfzhknpxwt
agiwmcjflhedbsyoqfzulnpxwt
agirmcjvlhedboyoqfzuknpjwl
agivmcjslhedbsyoqfzdknpxwt
agirmcjvlcedbsyoqfzukepxyt
akirmcjvlhjdbssoqfzuknpxwt
agvrmcjvldedmsyoqfzuknpxwt
agirecjvlhidbsyoqfzukbpxwt
abirmcjvlhjdbsyoqfkuknpxwt
agirmcjelhedbfyoqfzuknpxwj
agirmcjvlhedbbyoqrzukwpxwt
akirmcjvlhedbsyoyfzuknplwt
agirmcjvlhedbsydsfzuknpxwq
agirrcjvlhedbsyoqazuknpmwt
aeirmcjvlhedbsyoqfvuknpwwt
akirmcjvlhedbsyoqpzudnpxwt
agijmcjvlhedbsyuqfzunnpxwt
agirmcjilhedasyoqizuknpxwt
agirmczvlhzdbsyoqfzuknpxwx
agirmcjvlhehbsyoifzuknpxwo
agirwcjvlhedbsyoqfzuenpxst
agirmcjvlhedbsyoquzuknhxft
agirmcqvlkedbsyoqfzrknpxwt
agirmcqvlhenbsyoqfzuknpuwt
agirmcjvleedbsyoqfzhhnpxwt
agirmcjvlhembsyrqfauknpxwt
agirmcjvlhedbssoqflcknpxwt
aqirmcjvlnedbsyoqfzuknpxpt
agirmcjqlhedbxpoqfzuknpxwt
fgirmcjvlhedbsyoqfzukqpqwt
aggrmcjvlhpdbsyoqfzuknpxjt
agirmwjvlhedbsywqfzuknpzwt
agirmcailhembsyoqfzuknpxwt
aglrmcjvlhxdbsyoqfzuknpxet
xgirmcjvlhzdbsyoqfzukrpxwt
agvrmcjvuhedbsyoqfzuknpxgt
agikmcjvlhecbsyoqfzuknpxwr
agyrmcjvlhezbsyoqfouknpxwt
agirmcjvfhjdbsyokfzuknpxwt
agkrmjjvlhedtsyoqfzuknpxwt
agirmgjvlhedbiyoqfzuknpxwv
wcirmcjvlhedbsyoqfzuknpxwo
aairmcjvlhedbstoqfguknpxwt
hgirmcjvlhedwfyoqfzuknpxwt
agirmcjvmhfdbmyoqfzuknpxwt
agirmcjvlhvdbsioqfzuanpxwt
agrrmcjvgsedbsyoqfzuknpxwt
agirmcjvlqetbsysqfzuknpxwt
agirccjvlhedbsyoqfzuknkcwt
agirmqjvlhedbsdoqfzkknpxwt
agirmcjvlheobsyopfzuknpxwg
agirmcjolhedbsyofpzuknpxwt
agirmcjnlhedbsyoqkzukfpxwt
agiumcjvlheabsyoqfzuknpxbt
agipmcjvlhedbsyoqfzukupxwz
atirmcrvlhedbsyoqfnuknpxwt
agirmcjvnhedfkyoqfzuknpxwt
agirmrjvlhedboyoqfzvknpxwt
abhrmcjvlhedbtyoqfzuknpxwt
cbirmcjvlhedbfyoqfzuknpxwt
agirmcjvlhedbsyoqfmwknjxwt
ahirmcjvlhedbsloqfzuknpfwt
agarmjjvlhedbsyoqfzyknpxwt
ajirmcjvlhevjsyoqfzuknpxwt
agirmcjvlhpdbstoqfzuknpewt
agirmcsvlhedbsyoqfbupnpxwt
agirmcjvlhexbsyodfzukqpxwt
auiymcjblhedbsyoqfzuknpxwt
azirmcjvchedbsyoqfziknpxwt
aeirmcjvlhedvsyoqfzuonpxwt
agirmcjvlhedbfyoqfbukjpxwt
ygirmcjvlhidbsyoqfzukncxwt
agirmxpvlhedbsyoqffuknpxwt
ztirmcjvlhedosyoqfzuknpxwt
agirmcjvlhepbsyoqfzuenppwt
agirmcjvshedbsyoqnzaknpxwt
awirmcjvlhydbsyoqfzuknoxwt
ucirmcjvlhedbsyoqfjuknpxwt
agirmwjvlhkbbsyoqfzuknpxwt
agirmcjvldedbsyohfzuknpxzt
agirmcjvwhedbsyoqfznknpxgt
agiricjvlhedxqyoqfzuknpxwt
agirmcjvlhzdbjyoqfzukapxwt
agirmcgvlhedbsyoqfzuknaowt
agidmcjvlhedbsyoqayuknpxwt
agirmcjvlhedisnoqfzuknpxnt
wkjrmcjvlhedbsyoqfzuknpxwt
agirmcjvlhedbuyojfzukxpxwt
agkrmcjvlhedbsybqfzurnpxwt
agirmcjvghedbsyoqfzuknexwj
agirmcjvnhedbsyoqfzuznpxit
agirmcjvlbedbsyoqfiukwpxwt
agirlctvlheabsyoqfzuknpxwt
agirmcjzzhedbsyoqfzcknpxwt
akirmcjvlnedbsyoqfzlknpxwt
agirmdjvlhedpsyoqfzuknpjwt
agiyjcuvlhedbsyoqfzuknpxwt
agirmcbvltedysyoqfzuknpxwt
agirmcjvlhedfdyoqfzubnpxwt
agidmcjvlhedesfoqfzuknpxwt
aeirmcjvlhedqsyoqfxuknpxwt
agifmcjvlhedbsyoqfquknptwt
agidmcjvlhedbsyfqfzuknpxwb
agirvcjvlhedbsroqfzuknjxwt
agirmcqvlhddbsyoqfzuknpxwj
agirmcjvlhmdqsyoqizuknpxwt
atirmcjvltedbsyoqfzuknpxwz
agirxnjvlhedbsyoqfzuknpxkt
agihmcjvlhedbsyoqfzukepxqt
agirmcjvlhedbsmoqzsuknpxwt
agirycjvlhedbuyoqfwuknpxwt
agirmcjvlhedbsyoqfzfkrfxwt
agirzcjvlhedbsyoqfhuknpxnt
agigmcjvlhedbsqnqfzuknpxwt
agirmgzvlhedbsyoqfzuonpxwt
agirmcjvqhedbqyoqfzukqpxwt
anarmcjvlhedbsyocfzuknpxwt
agirmcjuihedbshoqfzuknpxwt
agirdckvlhedbsyoqfzxknpxwt
ugirmujvlhwdbsyoqfzuknpxwt
mgirmcjvlheobsyovfzuknpxwt
agirmcjvghedbsyoqfzufxpxwt
agirmcjvlhedbsyoinzuknuxwt
agirmzjvlhbdbsyoqfzlknpxwt
agivmcjvlhedbsconfzuknpxwt
agirmwfvlhedtsyoqfzuknpxwt
agirmcjvlhedbbyoqrzukncxwt
agirmcjvlhelbsyoqfzupnlxwt
agirmmjvluedqsyoqfzuknpxwt
agjrmcjvlhedbsyaqfcuknpxwt
agiwmcjvlhedbsyoqzzuknpswt
agirxcjvlhedbsyoqfyvknpxwt
agirmljvlhedbsyoqkzuknpxjt
agirmcjvchedbsyoqfzmknyxwt
agirmcjvlhedbsyovfzuynpxwl
agtrmcjvlhedysyoqfzuknexwt
agirmcjvmhedbslonfzuknpxwt
agirmcjfshedbsyoqfziknpxwt
agirmcjvlhedbsygqfzkknpbwt
agyrmcivlhedbsyovfzuknpxwt
agirmcjvghedbsyoqjzuknkxwt
agirmcjvlhedqsyoqfzukspxmt
ayirmcjvhhedbsyomfzuknpxwt
agirmcjvlnembsypqfzuknpxwt
agirmcjqlhedbsyuvfzuknpxwt
agirmcjvlhembsybqfzuknpxwa
agirjcfvlhedbsyoqfuuknpxwt
agirmcjvohedbsyowfzuknxxwt
agirmcjvlhedroyoqfzukncxwt
agrrmijvlhedbsyoqfnuknpxwt
agirmjjvlhsdbsyoqfzumnpxwt
agirrcjvnhedbsyoqfzuktpxwt
agirmcjvlzedjsyoqfzuknpdwt
agirmkjvlhedbsyoqfzxinpxwt
agirmcjvlhedbzyojfzuknpvwt
arirmcjvlheddsyoqfzuknrxwt
agirmcjvlhedbsyoqhzuanpxmt
agirmcjvluedbsyoqozuknwxwt
afirmcjwlhedxsyoqfzuknpxwt
agirmcjvlhefbsyoqfkuinpxwt
agirycjvltedbsypqfzuknpxwt
agirmrxvlhedbsyoqfzeknpxwt
agfrmcqvlhedbsyoqxzuknpxwt
agormcjvuhexbsyoqfzuknpxwt
agyrmcjvehddbsyoqfzuknpxwt
agirmcjvlheqbsynqfzgknpxwt
agirmcjvlhedbsloufwuknpxwt
tgirmcjvlwedbsyoqfzuknpqwt
agirmcjvlhesbzyogfzuknpxwt
agitmdjvlhedpsyoqfzuknpjwt
bgirmejvlhtdbsyoqfzuknpxwt
aginmcjvlhedzsyoqfzuknoxwt
agvrzcjvlhedbsuoqfzuknpxwt
agormcjvlhedbsyoqfzuknpodt
agirmcevlhedbgyojfzuknpxwt
agirmcjblhedboytqfzuknpxwt
qgibmcjvlhedbsyoqfzuknbxwt
agirmcjvlhedbsyoafzutnnxwt
agiamcjvchkdbsyoqfzuknpxwt
agirmcjvehedblyoqwzuknpxwt
agirmcpvlhwdbsyoafzuknpxwt
agirmcjvlhtdbsyoqfzumnpxtt
agirmcjalhegtsyoqfzuknpxwt
agirdijvlhedbsyoqfzutnpxwt
agirmckvlhgdbsyovfzuknpxwt
qgmrmcjvlkedbsyoqfzuknpxwt
agirjcjvlhodbsyoqfzuanpxwt
ajirmcjvlhedbpyoqftuknpxwt
cgirmcjvlhedbsyoqfiuonpxwt
ayirmcjvlhedbsyaqfzuknwxwt
agirmcjvlhedbdyoqbzwknpxwt

View File

@ -0,0 +1,17 @@
<Query Kind="Expression" />
File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"03_input.txt"))
.Where(l => !string.IsNullOrWhiteSpace(l))
.Select(l => new
{
ID = int.Parse(l.Substring(1).Split(' ')[0]),
X = int.Parse(l.Split('@')[1].Split(':')[0].Split(',')[0].Trim()),
Y = int.Parse(l.Split('@')[1].Split(':')[0].Split(',')[1].Trim()),
W = int.Parse(l.Split('@')[1].Split(':')[1].Split('x')[0].Trim()),
H = int.Parse(l.Split('@')[1].Split(':')[1].Split('x')[1].Trim()),
})
.SelectMany(l => Enumerable.Range(0, l.W).SelectMany(wx => Enumerable.Range(0, l.H).Select(hy => new { Coord=(l.X+wx, l.Y+hy), Source=l })))
.GroupBy(p => p.Coord)
.Where(p => p.Count() > 1)
.Count()

View File

@ -0,0 +1,22 @@
<Query Kind="Expression" />
File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"03_input.txt"))
.Where(l => !string.IsNullOrWhiteSpace(l))
.Select(l => new
{
ID = int.Parse(l.Substring(1).Split(' ')[0]),
X = int.Parse(l.Split('@')[1].Split(':')[0].Split(',')[0].Trim()),
Y = int.Parse(l.Split('@')[1].Split(':')[0].Split(',')[1].Trim()),
W = int.Parse(l.Split('@')[1].Split(':')[1].Split('x')[0].Trim()),
H = int.Parse(l.Split('@')[1].Split(':')[1].Split('x')[1].Trim()),
})
.SelectMany(l => Enumerable.Range(0, l.W).SelectMany(wx => Enumerable.Range(0, l.H).Select(hy => new { Coord = (l.X + wx, l.Y + hy), Source = l })))
.GroupBy(p => p.Coord)
.SelectMany(p => p.Select(q => new { Source = q.Source, Count = p.Count() }))
.GroupBy(p => p.Source.ID)
.Select(p => new { Source = p.First().Source, CountSum = p.Sum(q => q.Count) })
.Where(p => p.CountSum == p.Source.W * p.Source.H)
.Single()
.Source
.ID

View File

@ -0,0 +1,53 @@
--- Day 3: No Matter How You Slice It ---
The Elves managed to locate the chimney-squeeze prototype fabric for Santa's suit (thanks to someone who helpfully wrote its box IDs on the wall of the warehouse in the middle of the night). Unfortunately, anomalies are still affecting them - nobody can even agree on how to cut the fabric.
The whole piece of fabric they're working on is a very large square - at least 1000 inches on each side.
Each Elf has made a claim about which area of fabric would be ideal for Santa's suit. All claims have an ID and consist of a single rectangle with edges parallel to the edges of the fabric. Each claim's rectangle is defined as follows:
The number of inches between the left edge of the fabric and the left edge of the rectangle.
The number of inches between the top edge of the fabric and the top edge of the rectangle.
The width of the rectangle in inches.
The height of the rectangle in inches.
A claim like #123 @ 3,2: 5x4 means that claim ID 123 specifies a rectangle 3 inches from the left edge, 2 inches from the top edge, 5 inches wide, and 4 inches tall. Visually, it claims the square inches of fabric represented by # (and ignores the square inches of fabric represented by .) in the diagram below:
...........
...........
...#####...
...#####...
...#####...
...#####...
...........
...........
...........
The problem is that many of the claims overlap, causing two or more claims to cover part of the same areas. For example, consider the following claims:
#1 @ 1,3: 4x4
#2 @ 3,1: 4x4
#3 @ 5,5: 2x2
Visually, these claim the following areas:
........
...2222.
...2222.
.11XX22.
.11XX22.
.111133.
.111133.
........
The four square inches marked with X are claimed by both 1 and 2. (Claim 3, while adjacent to the others, does not overlap either of them.)
If the Elves all proceed with their own plans, none of them will have enough fabric. How many square inches of fabric are within two or more claims?
--- Part Two ---
Amidst the chaos, you notice that exactly one claim doesn't overlap by even a single square inch of fabric with any other claim. If you can somehow draw attention to it, maybe the Elves will be able to make Santa's suit after all!
For example, in the claims above, only claim 3 is intact after all claims are made.
What is the ID of the only claim that doesn't overlap?

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
<Query Kind="Statements" />
var guards = new List<(int id, int awake, int[] minutes)>();
var data = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"04_input.txt"))
.Where(p=>!string.IsNullOrWhiteSpace(p))
.OrderBy(p=>p);
int guard=-1;
int wakeup=0;
int wakesum=0;
bool awake=true;
int[] mins = new int[60];
foreach (var line in data)
{
int m = int.Parse(line.Substring(15,2));
if (line.Contains("Guard"))
{
if (awake)
{
wakesum += (60 - wakeup);
for (int i = wakeup; i < 60; mins[i++] = 1) ;
}
if (guard>=0)guards.Add((guard, wakesum, mins));
guard = int.Parse(line.Substring(26).Split(' ')[0]);
awake=true;
wakeup=0;
wakesum=0;
mins = new int[60];
}
else if (line.Contains("falls asleep"))
{
awake = false;
wakesum += (m-wakeup);
for (int i = wakeup; i < m; mins[i++]=1);
}
else if (line.Contains("wakes up"))
{
awake = true;
wakeup=m;
}
else throw new Exception();
}
guards
.GroupBy(g => g.id)
.Select(p => new
{
id = p.Key,
awake_total = p.Sum(q => q.awake),
asleep_total = p.Sum(q => 60 - q.awake),
mins = p.Select(q => q.minutes).Aggregate((a, b) => a.Zip(b, (u, v) => u + v).ToArray())
})
.Select(p => new
{
id = p.id,
awake_total = p.awake_total,
asleep_total = p.asleep_total,
mins = p.mins,
most_sleep_min = p.mins.Select((v, i) => (v, i)).OrderBy(q => q.v)
})
.Select(p => new
{
id = p.id,
awake_total = p.awake_total,
asleep_total = p.asleep_total,
mins = p.mins,
most_sleep_min = p.most_sleep_min,
result = p.most_sleep_min.First().i * p.id
})
.OrderByDescending(p => p.asleep_total)
.First()
.result
.Dump();

View File

@ -0,0 +1,85 @@
<Query Kind="Statements" />
var guards = new List<(int id, int awake, int[] minutes)>();
var data = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"04_input.txt"))
.Where(p=>!string.IsNullOrWhiteSpace(p))
.OrderBy(p=>p);
int guard=-1;
int wakeup=0;
int wakesum=0;
bool awake=true;
int[] mins = new int[60];
foreach (var line in data)
{
int m = int.Parse(line.Substring(15,2));
if (line.Contains("Guard"))
{
if (awake)
{
wakesum += (60 - wakeup);
for (int i = wakeup; i < 60; mins[i++] = 1) ;
}
if (guard>=0)guards.Add((guard, wakesum, mins));
guard = int.Parse(line.Substring(26).Split(' ')[0]);
awake=true;
wakeup=0;
wakesum=0;
mins = new int[60];
}
else if (line.Contains("falls asleep"))
{
awake = false;
wakesum += (m-wakeup);
for (int i = wakeup; i < m; mins[i++]=1);
}
else if (line.Contains("wakes up"))
{
awake = true;
wakeup=m;
}
else throw new Exception();
}
guards
.GroupBy(g => g.id)
.Select(p => new
{
id = p.Key,
awake_total = p.Sum(q => q.awake),
asleep_total = p.Sum(q => 60 - q.awake),
mins = p.Select(q => q.minutes).Aggregate((a, b) => a.Zip(b, (u, v) => u + v).ToArray()),
shifts = p.Count(),
})
.Select(p => new
{
id = p.id,
awake_total = p.awake_total,
asleep_total = p.asleep_total,
shifts = p.shifts,
mins = p.mins,
most_wake_min = p.mins.Select((v, i) => (v, i)).OrderByDescending(q => q.v),
most_sleep_min = p.mins.Select((v, i) => new { v = p.shifts - v, i = i}).OrderByDescending(q => q.v)
})
.Select(p => new
{
id = p.id,
awake_total = p.awake_total,
asleep_total = p.asleep_total,
shifts = p.shifts,
mins = p.mins,
most_sleep_min = p.most_sleep_min,
most_wake_min = p.most_wake_min,
result = p.most_wake_min.First().i * p.id,
result2 = p.most_sleep_min.First().i * p.id,
})
.OrderByDescending(p => p.most_sleep_min.First().v)
//.First()
//.result
.Dump();

View File

@ -0,0 +1,60 @@
--- Day 4: Repose Record ---
You've sneaked into another supply closet - this time, it's across from the prototype suit manufacturing lab. You need to sneak inside and fix the issues with the suit, but there's a guard stationed outside the lab, so this is as close as you can safely get.
As you search the closet for anything that might help, you discover that you're not the first person to want to sneak in. Covering the walls, someone has spent an hour starting every midnight for the past few months secretly observing this guard post! They've been writing down the ID of the one guard on duty that night - the Elves seem to have decided that one guard was enough for the overnight shift - as well as when they fall asleep or wake up while at their post (your puzzle input).
For example, consider the following records, which have already been organized into chronological order:
[1518-11-01 00:00] Guard #10 begins shift
[1518-11-01 00:05] falls asleep
[1518-11-01 00:25] wakes up
[1518-11-01 00:30] falls asleep
[1518-11-01 00:55] wakes up
[1518-11-01 23:58] Guard #99 begins shift
[1518-11-02 00:40] falls asleep
[1518-11-02 00:50] wakes up
[1518-11-03 00:05] Guard #10 begins shift
[1518-11-03 00:24] falls asleep
[1518-11-03 00:29] wakes up
[1518-11-04 00:02] Guard #99 begins shift
[1518-11-04 00:36] falls asleep
[1518-11-04 00:46] wakes up
[1518-11-05 00:03] Guard #99 begins shift
[1518-11-05 00:45] falls asleep
[1518-11-05 00:55] wakes up
Timestamps are written using year-month-day hour:minute format. The guard falling asleep or waking up is always the one whose shift most recently started. Because all asleep/awake times are during the midnight hour (00:00 - 00:59), only the minute portion (00 - 59) is relevant for those events.
Visually, these records show that the guards are asleep at these times:
Date ID Minute
000000000011111111112222222222333333333344444444445555555555
012345678901234567890123456789012345678901234567890123456789
11-01 #10 .....####################.....#########################.....
11-02 #99 ........................................##########..........
11-03 #10 ........................#####...............................
11-04 #99 ....................................##########..............
11-05 #99 .............................................##########.....
The columns are Date, which shows the month-day portion of the relevant day; ID, which shows the guard on duty that day; and Minute, which shows the minutes during which the guard was asleep within the midnight hour. (The Minute column's header shows the minute's ten's digit in the first row and the one's digit in the second row.) Awake is shown as ., and asleep is shown as #.
Note that guards count as asleep on the minute they fall asleep, and they count as awake on the minute they wake up. For example, because Guard #10 wakes up at 00:25 on 1518-11-01, minute 25 is marked as awake.
If you can figure out the guard most likely to be asleep at a specific time, you might be able to trick that guard into working tonight so you can have the best chance of sneaking in. You have two strategies for choosing the best guard/minute combination.
Strategy 1: Find the guard that has the most minutes asleep. What minute does that guard spend asleep the most?
In the example above, Guard #10 spent the most minutes asleep, a total of 50 minutes (20+25+5), while Guard #99 only slept for a total of 30 minutes (10+10+10). Guard #10 was asleep most during minute 24 (on two days, whereas any other minute the guard was asleep was only seen on one day).
While this example listed the entries in chronological order, your entries are in the order you found them. You'll need to organize them before they can be analyzed.
What is the ID of the guard you chose multiplied by the minute you chose? (In the above example, the answer would be 10 * 24 = 240.)
--- Part Two ---
Strategy 2: Of all guards, which guard is most frequently asleep on the same minute?
In the example above, Guard #99 spent minute 45 asleep more than any other guard or minute - three times in total. (In all other cases, any guard spent any minute asleep at most twice.)
What is the ID of the guard you chose multiplied by the minute you chose? (In the above example, the answer would be 99 * 45 = 4455.)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
<Query Kind="Statements" />
var poly = File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"05_input.txt")).Trim();
for (;;)
{
var len = poly.Length;
for (var i='a';i<='z';i++) poly=poly.Replace(i+""+char.ToUpper(i), "");
for (var i='a';i<='z';i++) poly=poly.Replace(char.ToUpper(i)+""+i, "");
if (len == poly.Length) { poly.Length.Dump(); return; }
}

View File

@ -0,0 +1,22 @@
<Query Kind="Statements" />
var poly0 = File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"05_input.txt")).Trim();
var r = new List<(char, int)>();
for (var cc = 'a'; cc <= 'z'; cc++)
{
var poly = poly0;
poly = poly.Replace(cc + "", "");
poly = poly.Replace(char.ToUpper(cc) + "", "");
for (; ; )
{
var len = poly.Length;
for (var i = 'a'; i <= 'z'; i++) poly = poly.Replace(i + "" + char.ToUpper(i), "");
for (var i = 'a'; i <= 'z'; i++) poly = poly.Replace(char.ToUpper(i) + "" + i, "");
if (len == poly.Length) { r.Add((cc, poly.Length)); break; }
}
}
r.OrderBy(p => p.Item2).First().Item2.Dump();

View File

@ -0,0 +1,42 @@
--- Day 5: Alchemical Reduction ---
You've managed to sneak in to the prototype suit manufacturing lab. The Elves are making decent progress, but are still struggling with the suit's size reduction capabilities.
While the very latest in 1518 alchemical technology might have solved their problem eventually, you can do better. You scan the chemical composition of the suit's material and discover that it is formed by extremely long polymers (one of which is available as your puzzle input).
The polymer is formed by smaller units which, when triggered, react with each other such that two adjacent units of the same type and opposite polarity are destroyed. Units' types are represented by letters; units' polarity is represented by capitalization. For instance, r and R are units with the same type but opposite polarity, whereas r and s are entirely different types and do not react.
For example:
In aA, a and A react, leaving nothing behind.
In abBA, bB destroys itself, leaving aA. As above, this then destroys itself, leaving nothing.
In abAB, no two adjacent units are of the same type, and so nothing happens.
In aabAAB, even though aa and AA are of the same type, their polarities match, and so nothing happens.
Now, consider a larger example, dabAcCaCBAcCcaDA:
dabAcCaCBAcCcaDA The first 'cC' is removed.
dabAaCBAcCcaDA This creates 'Aa', which is removed.
dabCBAcCcaDA Either 'cC' or 'Cc' are removed (the result is the same).
dabCBAcaDA No further actions can be taken.
After all possible reactions, the resulting polymer contains 10 units.
How many units remain after fully reacting the polymer you scanned? (Note: in this puzzle and others, the input is large; if you copy/paste your input, make sure you get the whole thing.)
--- Part Two ---
Time to improve the polymer.
One of the unit types is causing problems; it's preventing the polymer from collapsing as much as it should. Your goal is to figure out which unit type is causing the most problems, remove all instances of it (regardless of polarity), fully react the remaining polymer, and measure its length.
For example, again using the polymer dabAcCaCBAcCcaDA from above:
Removing all A/a units produces dbcCCBcCcD. Fully reacting this polymer produces dbCBcD, which has length 6.
Removing all B/b units produces daAcCaCAcCcaDA. Fully reacting this polymer produces daCAcaDA, which has length 8.
Removing all C/c units produces dabAaBAaDA. Fully reacting this polymer produces daDA, which has length 4.
Removing all D/d units produces abAcCaCBAcCcaA. Fully reacting this polymer produces abCBAc, which has length 6.
In this example, removing all C/c units was best, producing the answer 4.
What is the length of the shortest polymer you can produce by removing all units of exactly one type and fully reacting the result?

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,35 @@
<Query Kind="Statements" />
var coords = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"06_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(l => (int.Parse(l.Split(',')[0].Trim()), int.Parse(l.Split(',')[1].Trim())) )
.ToList();
int[][] d = new int[2][];
for (int i = 0; i < 2; i++)
{
var minX = coords.Min(p => p.Item1)-i;
var maxX = coords.Max(p => p.Item1)+i;
var minY = coords.Min(p => p.Item2)-i;
var maxY = coords.Max(p => p.Item2)+i;
int[,] map = new int[1+maxX-minX, 1+maxY-minY];
d[i] = new int[coords.Count];
for (int x = minX; x <= maxX; x++)
for (int y = minX; y <= maxY; y++)
{
var s = coords.Select((c, idx) => new { I = idx, D = Math.Abs(c.Item1 - x) + Math.Abs(c.Item2 - y), V = c }).OrderBy(c => c.D).ToList();
if (s[0].D == s[1].D) { map[x-minX, y-minY]=-1; continue; }
d[i][s[0].I]++;
map[x-minX, y-minY]=s[0].I;
}
//map.Dump();
}
//d[0].Zip(d[1], (a, b) => (a, b)).Dump();
d[0].Zip(d[1], (a, b) => (a, b)).Where(p => p.a == p.b).Max().a.Dump();

View File

@ -0,0 +1,25 @@
<Query Kind="Statements" />
var coords = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"06_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(l => (int.Parse(l.Split(',')[0].Trim()), int.Parse(l.Split(',')[1].Trim())))
.ToList();
var minX = coords.Min(p => p.Item1) - 5_000;
var maxX = coords.Max(p => p.Item1) + 5_000;
var minY = coords.Min(p => p.Item2) - 5_000;
var maxY = coords.Max(p => p.Item2) + 5_000;
int r = 0;
for (int x = minX; x <= maxX; x++)
for (int y = minX; y <= maxY; y++)
{
var s1 = coords.Sum(c => Math.Abs(c.Item1 - x) + Math.Abs(c.Item2 - y));
if (s1 < 10_000) r++;
Util.Progress=((x-minX)*100)/(maxX-minX);
}
r.Dump();

View File

@ -0,0 +1,87 @@
--- Day 6: Chronal Coordinates ---
The device on your wrist beeps several times, and once again you feel like you're falling.
"Situation critical," the device announces. "Destination indeterminate. Chronal interference detected. Please specify new target coordinates."
The device then produces a list of coordinates (your puzzle input). Are they places it thinks are safe or dangerous? It recommends you check manual page 729. The Elves did not give you a manual.
If they're dangerous, maybe you can minimize the danger by finding the coordinate that gives the largest distance from the other points.
Using only the Manhattan distance, determine the area around each coordinate by counting the number of integer X,Y locations that are closest to that coordinate (and aren't tied in distance to any other coordinate).
Your goal is to find the size of the largest area that isn't infinite. For example, consider the following list of coordinates:
1, 1
1, 6
8, 3
3, 4
5, 5
8, 9
If we name these coordinates A through F, we can draw them on a grid, putting 0,0 at the top left:
..........
.A........
..........
........C.
...D......
.....E....
.B........
..........
..........
........F.
This view is partial - the actual grid extends infinitely in all directions. Using the Manhattan distance, each location's closest coordinate can be determined, shown here in lowercase:
aaaaa.cccc
aAaaa.cccc
aaaddecccc
aadddeccCc
..dDdeeccc
bb.deEeecc
bBb.eeee..
bbb.eeefff
bbb.eeffff
bbb.ffffFf
Locations shown as . are equally far from two or more coordinates, and so they don't count as being closest to any.
In this example, the areas of coordinates A, B, C, and F are infinite - while not shown here, their areas extend forever outside the visible grid. However, the areas of coordinates D and E are finite: D is closest to 9 locations, and E is closest to 17 (both including the coordinate's location itself). Therefore, in this example, the size of the largest area is 17.
What is the size of the largest area that isn't infinite?
--- Part Two ---
On the other hand, if the coordinates are safe, maybe the best you can do is try to find a region near as many coordinates as possible.
For example, suppose you want the sum of the Manhattan distance to all of the coordinates to be less than 32. For each location, add up the distances to all of the given coordinates; if the total of those distances is less than 32, that location is within the desired region. Using the same coordinates as above, the resulting region looks like this:
..........
.A........
..........
...###..C.
..#D###...
..###E#...
.B.###....
..........
..........
........F.
In particular, consider the highlighted location 4,3 located at the top middle of the region. Its calculation is as follows, where abs() is the absolute value function:
Distance to coordinate A: abs(4-1) + abs(3-1) = 5
Distance to coordinate B: abs(4-1) + abs(3-6) = 6
Distance to coordinate C: abs(4-8) + abs(3-3) = 4
Distance to coordinate D: abs(4-3) + abs(3-4) = 2
Distance to coordinate E: abs(4-5) + abs(3-5) = 3
Distance to coordinate F: abs(4-8) + abs(3-9) = 10
Total distance: 5 + 6 + 4 + 2 + 3 + 10 = 30
Because the total distance to all coordinates (30) is less than 32, the location is within the region.
This region, which also includes coordinates D and E, has a total size of 16.
Your actual region will need to be much larger than this example, though, instead including all locations with a total distance of less than 10000.
What is the size of the region containing all locations which have a total distance to all given coordinates of less than 10000?

View File

@ -0,0 +1,50 @@
264, 340
308, 156
252, 127
65, 75
102, 291
47, 67
83, 44
313, 307
159, 48
84, 59
263, 248
188, 258
312, 240
59, 173
191, 130
155, 266
252, 119
108, 299
50, 84
172, 227
226, 159
262, 177
233, 137
140, 211
108, 175
278, 255
259, 209
233, 62
44, 341
58, 175
252, 74
232, 63
176, 119
209, 334
103, 112
155, 94
253, 255
169, 87
135, 342
55, 187
313, 338
210, 63
237, 321
171, 143
63, 238
79, 132
135, 113
310, 294
289, 184
56, 259

View File

@ -0,0 +1,35 @@
<Query Kind="Program" />
class Node { public char N; public List<Node> Precond = new List<Node>(); public List<Node> Postcond = new List<Node>(); }
void Main()
{
var data = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"07_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(l => (l[5], l[36]))
.ToList();
var nodes = data.SelectMany(p => new[] {p.Item1, p.Item2}).Distinct().Select(p => new Node {N=p}).ToList();
foreach (var (pre, post) in data)
{
var n1 = nodes.First(p => p.N == pre);
var n2 = nodes.First(p => p.N == post);
n1.Postcond.Add(n2);
n2.Precond.Add(n1);
}
var r = new StringBuilder();
for(var fin = new HashSet<Node>(); fin.Count<nodes.Count;)
{
var n = nodes.Where(p => !fin.Contains(p)).Where(p => p.Precond.All(q => fin.Contains(q))).OrderBy(p=>p.N).First();
fin.Add(n);
r.Append(n.N);
}
r.ToString().Dump();
}
// Define other methods and classes here

View File

@ -0,0 +1,61 @@
<Query Kind="Program" />
class Node { public char N; public List<Node> Precond = new List<Node>(); public List<Node> Postcond = new List<Node>(); }
class Worker { public Node curr; public int Remaining; }
void Main()
{
var data = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"07_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(l => (l[5], l[36]))
.ToList();
var nodes = data.SelectMany(p => new[] { p.Item1, p.Item2 }).Distinct().Select(p => new Node { N = p }).ToList();
foreach (var (pre, post) in data)
{
var n1 = nodes.First(p => p.N == pre);
var n2 = nodes.First(p => p.N == post);
n1.Postcond.Add(n2);
n2.Precond.Add(n1);
}
var workers = Enumerable.Repeat(0, 5).Select(p => new Worker()).ToList();
var started = new HashSet<Node>();
var fin = new HashSet<Node>();
var done = new StringBuilder();
for (int sec = 0; workers.Any(n => n.curr != null) || fin.Count < nodes.Count; sec++)
{
foreach (var w in workers.Where(p => p.curr != null))
{
if (--w.Remaining == 0)
{
started.Remove(w.curr);
done.Append(w.curr.N);
fin.Add(w.curr);
w.curr = null;
}
}
foreach (var w in workers.Where(p => p.curr == null))
{
var n = nodes
.Where(p => !started.Contains(p))
.Where(p => !fin.Contains(p))
.Where(p => p.Precond.All(q => fin.Contains(q)))
.OrderBy(p => p.N)
.FirstOrDefault();
if (n == null) continue;
w.curr = n;
w.Remaining = 60 + 1 + (n.N - 'A');
started.Add(n);
}
$"{sec:000} {string.Join(" ", workers.Select(w => (w?.curr?.N ?? '.').ToString()))} {done.ToString()}".Dump();
}
}
// Define other methods and classes here

View File

@ -0,0 +1,75 @@
--- Day 7: The Sum of Its Parts ---
You find yourself standing on a snow-covered coastline; apparently, you landed a little off course. The region is too hilly to see the North Pole from here, but you do spot some Elves that seem to be trying to unpack something that washed ashore. It's quite cold out, so you decide to risk creating a paradox by asking them for directions.
"Oh, are you the search party?" Somehow, you can understand whatever Elves from the year 1018 speak; you assume it's Ancient Nordic Elvish. Could the device on your wrist also be a translator? "Those clothes don't look very warm; take this." They hand you a heavy coat.
"We do need to find our way back to the North Pole, but we have higher priorities at the moment. You see, believe it or not, this box contains something that will solve all of Santa's transportation problems - at least, that's what it looks like from the pictures in the instructions." It doesn't seem like they can read whatever language it's in, but you can: "Sleigh kit. Some assembly required."
"'Sleigh'? What a wonderful name! You must help us assemble this 'sleigh' at once!" They start excitedly pulling more parts out of the box.
The instructions specify a series of steps and requirements about which steps must be finished before others can begin (your puzzle input). Each step is designated by a single letter. For example, suppose you have the following instructions:
Step C must be finished before step A can begin.
Step C must be finished before step F can begin.
Step A must be finished before step B can begin.
Step A must be finished before step D can begin.
Step B must be finished before step E can begin.
Step D must be finished before step E can begin.
Step F must be finished before step E can begin.
Visually, these requirements look like this:
-->A--->B--
/ \ \
C -->D----->E
\ /
---->F-----
Your first goal is to determine the order in which the steps should be completed. If more than one step is ready, choose the step which is first alphabetically. In this example, the steps would be completed as follows:
Only C is available, and so it is done first.
Next, both A and F are available. A is first alphabetically, so it is done next.
Then, even though F was available earlier, steps B and D are now also available, and B is the first alphabetically of the three.
After that, only D and F are available. E is not available because only some of its prerequisites are complete. Therefore, D is completed next.
F is the only choice, so it is done next.
Finally, E is completed.
So, in this example, the correct order is CABDFE.
In what order should the steps in your instructions be completed?
--- Part Two ---
As you're about to begin construction, four of the Elves offer to help. "The sun will set soon; it'll go faster if we work together." Now, you need to account for multiple people working on steps simultaneously. If multiple steps are available, workers should still begin them in alphabetical order.
Each step takes 60 seconds plus an amount corresponding to its letter: A=1, B=2, C=3, and so on. So, step A takes 60+1=61 seconds, while step Z takes 60+26=86 seconds. No time is required between steps.
To simplify things for the example, however, suppose you only have help from one Elf (a total of two workers) and that each step takes 60 fewer seconds (so that step A takes 1 second and step Z takes 26 seconds). Then, using the same instructions as above, this is how each second would be spent:
Second Worker 1 Worker 2 Done
0 C .
1 C .
2 C .
3 A F C
4 B F CA
5 B F CA
6 D F CAB
7 D F CAB
8 D F CAB
9 D . CABF
10 E . CABFD
11 E . CABFD
12 E . CABFD
13 E . CABFD
14 E . CABFD
15 . . CABFDE
Each row represents one second of time. The Second column identifies how many seconds have passed as of the beginning of that second. Each worker column shows the step that worker is currently doing (or . if they are idle). The Done column shows completed steps.
Note that the order of the steps has changed; this is because steps now take time to finish and multiple workers can begin multiple steps simultaneously.
In this example, it would take 15 seconds for two workers to complete these steps.
With 5 workers and the 60+ second step durations described above, how long will it take to complete all of the steps?

View File

@ -0,0 +1,101 @@
Step T must be finished before step X can begin.
Step G must be finished before step O can begin.
Step X must be finished before step B can begin.
Step I must be finished before step W can begin.
Step N must be finished before step V can begin.
Step K must be finished before step H can begin.
Step S must be finished before step R can begin.
Step P must be finished before step J can begin.
Step L must be finished before step V can begin.
Step D must be finished before step E can begin.
Step J must be finished before step R can begin.
Step U must be finished before step W can begin.
Step M must be finished before step Q can begin.
Step B must be finished before step F can begin.
Step F must be finished before step E can begin.
Step V must be finished before step Q can begin.
Step C must be finished before step A can begin.
Step H must be finished before step Z can begin.
Step A must be finished before step Y can begin.
Step O must be finished before step Y can begin.
Step W must be finished before step Q can begin.
Step E must be finished before step Y can begin.
Step Y must be finished before step Z can begin.
Step Q must be finished before step R can begin.
Step R must be finished before step Z can begin.
Step S must be finished before step E can begin.
Step O must be finished before step W can begin.
Step G must be finished before step B can begin.
Step I must be finished before step N can begin.
Step G must be finished before step I can begin.
Step H must be finished before step R can begin.
Step N must be finished before step C can begin.
Step M must be finished before step W can begin.
Step Y must be finished before step R can begin.
Step T must be finished before step B can begin.
Step G must be finished before step D can begin.
Step J must be finished before step O can begin.
Step I must be finished before step A can begin.
Step J must be finished before step H can begin.
Step T must be finished before step Y can begin.
Step N must be finished before step H can begin.
Step B must be finished before step V can begin.
Step M must be finished before step R can begin.
Step Y must be finished before step Q can begin.
Step X must be finished before step J can begin.
Step A must be finished before step E can begin.
Step P must be finished before step Z can begin.
Step P must be finished before step C can begin.
Step N must be finished before step Q can begin.
Step A must be finished before step O can begin.
Step G must be finished before step X can begin.
Step P must be finished before step U can begin.
Step T must be finished before step S can begin.
Step I must be finished before step V can begin.
Step V must be finished before step H can begin.
Step U must be finished before step F can begin.
Step D must be finished before step Q can begin.
Step D must be finished before step O can begin.
Step G must be finished before step H can begin.
Step I must be finished before step Z can begin.
Step N must be finished before step D can begin.
Step B must be finished before step Y can begin.
Step J must be finished before step M can begin.
Step V must be finished before step Y can begin.
Step W must be finished before step Y can begin.
Step E must be finished before step Z can begin.
Step T must be finished before step N can begin.
Step L must be finished before step U can begin.
Step S must be finished before step A can begin.
Step Q must be finished before step Z can begin.
Step T must be finished before step F can begin.
Step F must be finished before step Z can begin.
Step J must be finished before step C can begin.
Step X must be finished before step Y can begin.
Step K must be finished before step V can begin.
Step T must be finished before step I can begin.
Step I must be finished before step O can begin.
Step C must be finished before step W can begin.
Step B must be finished before step Q can begin.
Step W must be finished before step Z can begin.
Step D must be finished before step H can begin.
Step K must be finished before step A can begin.
Step M must be finished before step E can begin.
Step T must be finished before step U can begin.
Step I must be finished before step J can begin.
Step O must be finished before step Q can begin.
Step M must be finished before step Z can begin.
Step U must be finished before step C can begin.
Step N must be finished before step F can begin.
Step C must be finished before step H can begin.
Step X must be finished before step E can begin.
Step F must be finished before step O can begin.
Step P must be finished before step O can begin.
Step J must be finished before step A can begin.
Step H must be finished before step Y can begin.
Step A must be finished before step Q can begin.
Step V must be finished before step Z can begin.
Step S must be finished before step L can begin.
Step H must be finished before step E can begin.
Step X must be finished before step I can begin.
Step O must be finished before step R can begin.

View File

@ -0,0 +1,29 @@
<Query Kind="Program" />
class Node { public List<Node> Children = new List<Node>(); public List<int> Metadata = new List<int>(); }
void Main()
{
var data = File
.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"08_input.txt"))
.Split(' ')
.Select(int.Parse)
.ToList();
int i = 0;
var root = Parse(data, ref i);
MetaSum(root).Dump();
}
private Node Parse(List<int> data, ref int pos)
{
var qn = data[pos++];
var qm = data[pos++];
var n = new Node();
for (int i = 0; i < qn; i++) n.Children.Add(Parse(data, ref pos));
for (int i = 0; i < qm; i++) n.Metadata.Add(data[pos++]);
return n;
}
private int MetaSum(Node n) => n.Metadata.Sum() + n.Children.Select(MetaSum).Sum();

View File

@ -0,0 +1,31 @@
<Query Kind="Program" />
class Node { public List<Node> Children = new List<Node>(); public List<int> Metadata = new List<int>(); }
void Main()
{
var data = File
.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"08_input.txt"))
.Split(' ')
.Select(int.Parse)
.ToList();
int i = 0;
var root = Parse(data, ref i);
NodeVal(root).Dump();
}
private Node Parse(List<int> data, ref int pos)
{
var qn = data[pos++];
var qm = data[pos++];
var n = new Node();
for (int i = 0; i < qn; i++) n.Children.Add(Parse(data, ref pos));
for (int i = 0; i < qm; i++) n.Metadata.Add(data[pos++]);
return n;
}
private int MetaSum(Node n) => n.Metadata.Sum() + n.Children.Select(MetaSum).Sum();
private int NodeVal(Node n) => n.Children.Any() ? n.Metadata.Select(i=>i-1).Where(i=>i>=0&&i<n.Children.Count).Select(i=>NodeVal(n.Children[i])).Sum() : n.Metadata.Sum();

View File

@ -0,0 +1,54 @@
--- Day 8: Memory Maneuver ---
The sleigh is much easier to pull than you'd expect for something its weight. Unfortunately, neither you nor the Elves know which way the North Pole is from here.
You check your wrist device for anything that might help. It seems to have some kind of navigation system! Activating the navigation system produces more bad news: "Failed to start navigation system. Could not read software license file."
The navigation system's license file consists of a list of numbers (your puzzle input). The numbers define a data structure which, when processed, produces some kind of tree that can be used to calculate the license number.
The tree is made up of nodes; a single, outermost node forms the tree's root, and it contains all other nodes in the tree (or contains nodes that contain nodes, and so on).
Specifically, a node consists of:
A header, which is always exactly two numbers:
The quantity of child nodes.
The quantity of metadata entries.
Zero or more child nodes (as specified in the header).
One or more metadata entries (as specified in the header).
Each child node is itself a node that has its own header, child nodes, and metadata. For example:
2 3 0 3 10 11 12 1 1 0 1 99 2 1 1 2
A----------------------------------
B----------- C-----------
D-----
In this example, each node of the tree is also marked with an underline starting with a letter for easier identification. In it, there are four nodes:
A, which has 2 child nodes (B, C) and 3 metadata entries (1, 1, 2).
B, which has 0 child nodes and 3 metadata entries (10, 11, 12).
C, which has 1 child node (D) and 1 metadata entry (2).
D, which has 0 child nodes and 1 metadata entry (99).
The first check done on the license file is to simply add up all of the metadata entries. In this example, that sum is 1+1+2+10+11+12+2+99=138.
What is the sum of all metadata entries?
--- Part Two ---
The second check is slightly more complicated: you need to find the value of the root node (A in the example above).
The value of a node depends on whether it has child nodes.
If a node has no child nodes, its value is the sum of its metadata entries. So, the value of node B is 10+11+12=33, and the value of node D is 99.
However, if a node does have child nodes, the metadata entries become indexes which refer to those child nodes. A metadata entry of 1 refers to the first child node, 2 to the second, 3 to the third, and so on. The value of this node is the sum of the values of the child nodes referenced by the metadata entries. If a referenced child node does not exist, that reference is skipped. A child node can be referenced multiple time and counts each time it is referenced. A metadata entry of 0 does not refer to any child node.
For example, again using the above nodes:
Node C has one metadata entry, 2. Because node C has only one child node, 2 references a child node which does not exist, and so the value of node C is 0.
Node A has three metadata entries: 1, 1, and 2. The 1 references node A's first child node, B, and the 2 references node A's second child node, C. Because node B has a value of 33 and node C has a value of 0, the value of node A is 33+33+0=66.
So, in this example, the value of the root node is 66.
What is the value of the root node?

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,36 @@
<Query Kind="Program" />
void Main()
{
var input = File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"09_input.txt"));
var players = int.Parse(input.Split(' ')[0]);
var marbles = int.Parse(input.Split(' ')[6]);
List<int> board = new List<int> { 0 };
int[] score = new int[players];
int curr = 0;
int currplayer = 0;
for (int m = 1; m <= marbles; m++)
{
if (m % 23 != 0)
{
curr = (curr + 2)%board.Count;
board.Insert(curr, m);
}
else
{
score[currplayer] += m;
curr = (board.Count + curr - 7) % board.Count;
score[currplayer] += board[curr];
board.RemoveAt(curr);
}
//($"[{currplayer+1:0}]" + string.Join(" ", board.Select((b, i) => i == curr ? $"({b:00})" : $" {b:00} "))).Dump();
currplayer = (currplayer + 1) % players;
}
score.Max().Dump();
}

View File

@ -0,0 +1,55 @@
<Query Kind="Program" />
class Marble { public Marble Prev, Next; public int Value; }
void Main()
{
var input = File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"09_input.txt"));
var players = int.Parse(input.Split(' ')[0]);
var marbles = int.Parse(input.Split(' ')[6]) * 100;
List<int> board = new List<int> { 0 };
long[] score = new long[players];
Marble curr = new Marble();
curr.Next = curr;
curr.Prev = curr;
int currplayer = 0;
Marble old = null;
for (int m = 1; m <= marbles; m++)
{
if (m % 23 != 0)
{
curr = curr.Next;
var nm = old ?? new Marble();
old=null;
nm.Prev=curr;
nm.Next=curr.Next;
nm.Value=m;
curr.Next.Prev = nm;
curr.Next = nm;
curr = curr.Next;
}
else
{
score[currplayer] += m;
curr = curr.Prev.Prev.Prev.Prev.Prev.Prev;
score[currplayer] += curr.Prev.Value;
old = curr.Prev;
var c = old.Prev;
c.Next = curr;
curr.Prev=c;
}
//($"[{currplayer+1:0}]" + string.Join(" ", board.Select((b, i) => i == curr ? $"({b:00})" : $" {b:00} "))).Dump();
currplayer = (currplayer + 1) % players;
Util.Progress = (m * 100) / marbles;
}
score.Max().Dump();
}

View File

@ -0,0 +1,58 @@
--- Day 9: Marble Mania ---
You talk to the Elves while you wait for your navigation system to initialize. To pass the time, they introduce you to their favorite marble game.
The Elves play this game by taking turns arranging the marbles in a circle according to very particular rules. The marbles are numbered starting with 0 and increasing by 1 until every marble has a number.
First, the marble numbered 0 is placed in the circle. At this point, while it contains only a single marble, it is still a circle: the marble is both clockwise from itself and counter-clockwise from itself. This marble is designated the current marble.
Then, each Elf takes a turn placing the lowest-numbered remaining marble into the circle between the marbles that are 1 and 2 marbles clockwise of the current marble. (When the circle is large enough, this means that there is one marble between the marble that was just placed and the current marble.) The marble that was just placed then becomes the current marble.
However, if the marble that is about to be placed has a number which is a multiple of 23, something entirely different happens. First, the current player keeps the marble they would have placed, adding it to their score. In addition, the marble 7 marbles counter-clockwise from the current marble is removed from the circle and also added to the current player's score. The marble located immediately clockwise of the marble that was removed becomes the new current marble.
For example, suppose there are 9 players. After the marble with value 0 is placed in the middle, each player (shown in square brackets) takes a turn. The result of each of those turns would produce circles of marbles like this, where clockwise is to the right and the resulting current marble is in parentheses:
[-] (0)
[1] 0 (1)
[2] 0 (2) 1
[3] 0 2 1 (3)
[4] 0 (4) 2 1 3
[5] 0 4 2 (5) 1 3
[6] 0 4 2 5 1 (6) 3
[7] 0 4 2 5 1 6 3 (7)
[8] 0 (8) 4 2 5 1 6 3 7
[9] 0 8 4 (9) 2 5 1 6 3 7
[1] 0 8 4 9 2(10) 5 1 6 3 7
[2] 0 8 4 9 2 10 5(11) 1 6 3 7
[3] 0 8 4 9 2 10 5 11 1(12) 6 3 7
[4] 0 8 4 9 2 10 5 11 1 12 6(13) 3 7
[5] 0 8 4 9 2 10 5 11 1 12 6 13 3(14) 7
[6] 0 8 4 9 2 10 5 11 1 12 6 13 3 14 7(15)
[7] 0(16) 8 4 9 2 10 5 11 1 12 6 13 3 14 7 15
[8] 0 16 8(17) 4 9 2 10 5 11 1 12 6 13 3 14 7 15
[9] 0 16 8 17 4(18) 9 2 10 5 11 1 12 6 13 3 14 7 15
[1] 0 16 8 17 4 18 9(19) 2 10 5 11 1 12 6 13 3 14 7 15
[2] 0 16 8 17 4 18 9 19 2(20)10 5 11 1 12 6 13 3 14 7 15
[3] 0 16 8 17 4 18 9 19 2 20 10(21) 5 11 1 12 6 13 3 14 7 15
[4] 0 16 8 17 4 18 9 19 2 20 10 21 5(22)11 1 12 6 13 3 14 7 15
[5] 0 16 8 17 4 18(19) 2 20 10 21 5 22 11 1 12 6 13 3 14 7 15
[6] 0 16 8 17 4 18 19 2(24)20 10 21 5 22 11 1 12 6 13 3 14 7 15
[7] 0 16 8 17 4 18 19 2 24 20(25)10 21 5 22 11 1 12 6 13 3 14 7 15
The goal is to be the player with the highest score after the last marble is used up. Assuming the example above ends after the marble numbered 25, the winning score is 23+9=32 (because player 5 kept marble 23 and removed marble 9, while no other player got any points in this very short example game).
Here are a few more examples:
10 players; last marble is worth 1618 points: high score is 8317
13 players; last marble is worth 7999 points: high score is 146373
17 players; last marble is worth 1104 points: high score is 2764
21 players; last marble is worth 6111 points: high score is 54718
30 players; last marble is worth 5807 points: high score is 37305
What is the winning Elf's score?
--- Part Two ---
Amused by the speed of your answer, the Elves are curious:
What would the new winning Elf's score be if the number of the last marble were 100 times larger?

View File

@ -0,0 +1 @@
447 players; last marble is worth 71510 points

View File

@ -0,0 +1,56 @@
<Query Kind="Program" />
class P { public int XX, YY, VX, VY; }
List<P> input;
void Main()
{
input = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"10_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(l => new P
{
XX=int.Parse(l.Substring(10,6).Trim()),
YY=int.Parse(l.Substring(18,6).Trim()),
VX=int.Parse(l.Substring(36,2).Trim()),
VY=int.Parse(l.Substring(40,2).Trim()),
})
.ToList();
for (;;)
{
foreach (var v in input) { v.XX += v.VX; v.YY += v.VY; }
if (input.Any(Alone)) continue;
Print();
return;
}
}
bool Alone(P p) => !input.Any(i => i != p && Math.Abs(i.XX-p.XX)<=1 && Math.Abs(i.YY-p.YY)<=1);
void Print()
{
int minX = input.Min(i => i.XX);
int maxX = input.Max(i => i.XX);
int minY = input.Min(i => i.YY);
int maxY = input.Max(i => i.YY);
var b = new StringBuilder();
for (int y = minY; y <= maxY; y++)
{
for (int x = minX; x <= maxX; x++)
{
b.Append(input.Any(i => i.XX==x&&i.YY==y)?'#':'.');
}
b.AppendLine();
}
b.ToString().Dump();
}

View File

@ -0,0 +1,56 @@
<Query Kind="Program" />
class P { public int XX, YY, VX, VY; }
List<P> input;
void Main()
{
input = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"10_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(l => new P
{
XX = int.Parse(l.Substring(10, 6).Trim()),
YY = int.Parse(l.Substring(18, 6).Trim()),
VX = int.Parse(l.Substring(36, 2).Trim()),
VY = int.Parse(l.Substring(40, 2).Trim()),
})
.ToList();
for (int i = 1; ; i++)
{
foreach (var v in input) { v.XX += v.VX; v.YY += v.VY; }
if (input.Any(Alone)) continue;
//Print();
i.Dump();
return;
}
}
bool Alone(P p) => !input.Any(i => i != p && Math.Abs(i.XX - p.XX) <= 1 && Math.Abs(i.YY - p.YY) <= 1);
void Print()
{
int minX = input.Min(i => i.XX);
int maxX = input.Max(i => i.XX);
int minY = input.Min(i => i.YY);
int maxY = input.Max(i => i.YY);
var b = new StringBuilder();
for (int y = minY; y <= maxY; y++)
{
for (int x = minX; x <= maxX; x++)
{
b.Append(input.Any(i => i.XX == x && i.YY == y) ? '#' : '.');
}
b.AppendLine();
}
b.ToString().Dump();
}

View File

@ -0,0 +1,149 @@
--- Day 10: The Stars Align ---
It's no use; your navigation system simply isn't capable of providing walking directions in the arctic circle, and certainly not in 1018.
The Elves suggest an alternative. In times like these, North Pole rescue operations will arrange points of light in the sky to guide missing Elves back to base. Unfortunately, the message is easy to miss: the points move slowly enough that it takes hours to align them, but have so much momentum that they only stay aligned for a second. If you blink at the wrong time, it might be hours before another message appears.
You can see these points of light floating in the distance, and record their position in the sky and their velocity, the relative change in position per second (your puzzle input). The coordinates are all given from your perspective; given enough time, those positions and velocities will move the points into a cohesive message!
Rather than wait, you decide to fast-forward the process and calculate what the points will eventually spell.
For example, suppose you note the following points:
position=< 9, 1> velocity=< 0, 2>
position=< 7, 0> velocity=<-1, 0>
position=< 3, -2> velocity=<-1, 1>
position=< 6, 10> velocity=<-2, -1>
position=< 2, -4> velocity=< 2, 2>
position=<-6, 10> velocity=< 2, -2>
position=< 1, 8> velocity=< 1, -1>
position=< 1, 7> velocity=< 1, 0>
position=<-3, 11> velocity=< 1, -2>
position=< 7, 6> velocity=<-1, -1>
position=<-2, 3> velocity=< 1, 0>
position=<-4, 3> velocity=< 2, 0>
position=<10, -3> velocity=<-1, 1>
position=< 5, 11> velocity=< 1, -2>
position=< 4, 7> velocity=< 0, -1>
position=< 8, -2> velocity=< 0, 1>
position=<15, 0> velocity=<-2, 0>
position=< 1, 6> velocity=< 1, 0>
position=< 8, 9> velocity=< 0, -1>
position=< 3, 3> velocity=<-1, 1>
position=< 0, 5> velocity=< 0, -1>
position=<-2, 2> velocity=< 2, 0>
position=< 5, -2> velocity=< 1, 2>
position=< 1, 4> velocity=< 2, 1>
position=<-2, 7> velocity=< 2, -2>
position=< 3, 6> velocity=<-1, -1>
position=< 5, 0> velocity=< 1, 0>
position=<-6, 0> velocity=< 2, 0>
position=< 5, 9> velocity=< 1, -2>
position=<14, 7> velocity=<-2, 0>
position=<-3, 6> velocity=< 2, -1>
Each line represents one point. Positions are given as <X, Y> pairs: X represents how far left (negative) or right (positive) the point appears, while Y represents how far up (negative) or down (positive) the point appears.
At 0 seconds, each point has the position given. Each second, each point's velocity is added to its position. So, a point with velocity <1, -2> is moving to the right, but is moving upward twice as quickly. If this point's initial position were <3, 9>, after 3 seconds, its position would become <6, 3>.
Over time, the points listed above would move like this:
Initially:
........#.............
................#.....
.........#.#..#.......
......................
#..........#.#.......#
...............#......
....#.................
..#.#....#............
.......#..............
......#...............
...#...#.#...#........
....#..#..#.........#.
.......#..............
...........#..#.......
#...........#.........
...#.......#..........
After 1 second:
......................
......................
..........#....#......
........#.....#.......
..#.........#......#..
......................
......#...............
....##.........#......
......#.#.............
.....##.##..#.........
........#.#...........
........#...#.....#...
..#...........#.......
....#.....#.#.........
......................
......................
After 2 seconds:
......................
......................
......................
..............#.......
....#..#...####..#....
......................
........#....#........
......#.#.............
.......#...#..........
.......#..#..#.#......
....#....#.#..........
.....#...#...##.#.....
........#.............
......................
......................
......................
After 3 seconds:
......................
......................
......................
......................
......#...#..###......
......#...#...#.......
......#...#...#.......
......#####...#.......
......#...#...#.......
......#...#...#.......
......#...#...#.......
......#...#..###......
......................
......................
......................
......................
After 4 seconds:
......................
......................
......................
............#.........
........##...#.#......
......#.....#..#......
.....#..##.##.#.......
.......##.#....#......
...........#....#.....
..............#.......
....#......#...#......
.....#.....##.........
...............#......
...............#......
......................
......................
After 3 seconds, the message appeared briefly: HI. Of course, your message will be much longer and will take many more seconds to appear.
What message will eventually appear in the sky?
--- Part Two ---
Good thing you didn't have to wait, because that would have taken a long time - much longer than the 3 seconds in the example above.
Impressed by your sub-hour communication capabilities, the Elves are curious: exactly how many seconds would they have needed to wait for that message to appear?

View File

@ -0,0 +1,337 @@
position=< 21518, -21209> velocity=<-2, 2>
position=< 10842, 21423> velocity=<-1, -2>
position=< 32189, -21209> velocity=<-3, 2>
position=<-21158, -21218> velocity=< 2, 2>
position=<-31794, -53194> velocity=< 3, 5>
position=<-42469, 42743> velocity=< 4, -4>
position=<-53120, -31873> velocity=< 5, 3>
position=< 32177, -42536> velocity=<-3, 4>
position=< 53505, 32084> velocity=<-5, -3>
position=<-53091, 10764> velocity=< 5, -1>
position=<-53141, -21211> velocity=< 5, 2>
position=<-42429, 10764> velocity=< 4, -1>
position=<-10492, -31873> velocity=< 1, 3>
position=< 42819, -10550> velocity=<-4, 1>
position=<-53096, -10551> velocity=< 5, 1>
position=<-10492, -42536> velocity=< 1, 4>
position=<-10508, 42739> velocity=< 1, -4>
position=<-10471, -42533> velocity=< 1, 4>
position=< 53446, 32086> velocity=<-5, -3>
position=< 10820, -53195> velocity=<-1, 5>
position=< 10858, 21423> velocity=<-1, -2>
position=<-31810, -10553> velocity=< 3, 1>
position=<-10492, 32077> velocity=< 1, -3>
position=<-31799, -42527> velocity=< 3, 4>
position=<-10484, 42738> velocity=< 1, -4>
position=< 10862, 10767> velocity=<-1, -1>
position=<-10503, 10759> velocity=< 1, -1>
position=<-21130, 32081> velocity=< 2, -3>
position=<-42477, -31877> velocity=< 4, 3>
position=<-10452, -42530> velocity=< 1, 4>
position=<-10452, 53399> velocity=< 1, -5>
position=<-31826, 42744> velocity=< 3, -4>
position=<-42444, -10556> velocity=< 4, 1>
position=< 42824, -31873> velocity=<-4, 3>
position=<-10492, 32082> velocity=< 1, -3>
position=<-53092, 10759> velocity=< 5, -1>
position=< 21522, 42745> velocity=<-2, -4>
position=< 53474, -42532> velocity=<-5, 4>
position=<-31822, 42737> velocity=< 3, -4>
position=< 32131, -42529> velocity=<-3, 4>
position=<-10476, -53193> velocity=< 1, 5>
position=< 21473, -10551> velocity=<-2, 1>
position=<-10497, 21422> velocity=< 1, -2>
position=< 21469, 42741> velocity=<-2, -4>
position=< 21470, 10763> velocity=<-2, -1>
position=<-31807, 32086> velocity=< 3, -3>
position=< 32144, 32078> velocity=<-3, -3>
position=<-53120, -42531> velocity=< 5, 4>
position=<-31773, -31871> velocity=< 3, 3>
position=<-31800, -21218> velocity=< 3, 2>
position=< 53478, -21213> velocity=<-5, 2>
position=< 21498, 21424> velocity=<-2, -2>
position=< 42798, 32081> velocity=<-4, -3>
position=< 42790, 10761> velocity=<-4, -1>
position=<-31778, -10558> velocity=< 3, 1>
position=<-21106, 21418> velocity=< 2, -2>
position=<-31785, 53401> velocity=< 3, -5>
position=< 53497, -42536> velocity=<-5, 4>
position=< 53491, -42535> velocity=<-5, 4>
position=<-21115, -21218> velocity=< 2, 2>
position=< 53478, 53401> velocity=<-5, -5>
position=< 10835, 32077> velocity=<-1, -3>
position=<-10500, 21421> velocity=< 1, -2>
position=<-53142, -21212> velocity=< 5, 2>
position=<-10490, -31868> velocity=< 1, 3>
position=< 53506, 21419> velocity=<-5, -2>
position=< 21517, 10764> velocity=<-2, -1>
position=< 32136, 32082> velocity=<-3, -3>
position=<-42485, -42536> velocity=< 4, 4>
position=< 10821, -53195> velocity=<-1, 5>
position=<-42445, -10551> velocity=< 4, 1>
position=<-10473, -21214> velocity=< 1, 2>
position=< 42806, -53186> velocity=<-4, 5>
position=< 42791, 10767> velocity=<-4, -1>
position=<-53144, 32083> velocity=< 5, -3>
position=<-53096, 53402> velocity=< 5, -5>
position=<-21119, 42738> velocity=< 2, -4>
position=< 42824, 53404> velocity=<-4, -5>
position=< 21495, 32081> velocity=<-2, -3>
position=<-31782, 21421> velocity=< 3, -2>
position=<-21157, -31873> velocity=< 2, 3>
position=< 53478, 32083> velocity=<-5, -3>
position=< 53470, 53398> velocity=<-5, -5>
position=< 42797, 21422> velocity=<-4, -2>
position=<-21143, -31877> velocity=< 2, 3>
position=< 32144, -21216> velocity=<-3, 2>
position=<-10482, 32086> velocity=< 1, -3>
position=< 42831, -21216> velocity=<-4, 2>
position=<-42448, 53397> velocity=< 4, -5>
position=<-21135, -21213> velocity=< 2, 2>
position=< 42806, 32086> velocity=<-4, -3>
position=< 42805, 53404> velocity=<-4, -5>
position=<-53096, 32080> velocity=< 5, -3>
position=< 32155, 32077> velocity=<-3, -3>
position=< 53463, -10550> velocity=<-5, 1>
position=< 53475, 42737> velocity=<-5, -4>
position=< 21470, -31873> velocity=<-2, 3>
position=<-10452, -10550> velocity=< 1, 1>
position=< 10850, 21427> velocity=<-1, -2>
position=< 10854, 53398> velocity=<-1, -5>
position=<-10500, 53395> velocity=< 1, -5>
position=< 21497, 10759> velocity=<-2, -1>
position=< 32155, -42527> velocity=<-3, 4>
position=< 53505, -42534> velocity=<-5, 4>
position=<-21142, 53404> velocity=< 2, -5>
position=< 32176, -53193> velocity=<-3, 5>
position=<-42437, -53193> velocity=< 4, 5>
position=<-21163, 53396> velocity=< 2, -5>
position=< 32128, -42531> velocity=<-3, 4>
position=< 42798, -53195> velocity=<-4, 5>
position=< 42829, 32081> velocity=<-4, -3>
position=< 21510, -53192> velocity=<-2, 5>
position=<-53108, -21214> velocity=< 5, 2>
position=<-21124, 53399> velocity=< 2, -5>
position=<-21108, -31875> velocity=< 2, 3>
position=<-31794, 10768> velocity=< 3, -1>
position=< 10842, 21426> velocity=<-1, -2>
position=<-53142, 21424> velocity=< 5, -2>
position=<-42476, 53399> velocity=< 4, -5>
position=< 42816, -10554> velocity=<-4, 1>
position=< 32179, 42741> velocity=<-3, -4>
position=< 32171, 32081> velocity=<-3, -3>
position=<-10503, 32077> velocity=< 1, -3>
position=< 42813, 42745> velocity=<-4, -4>
position=< 21485, 42736> velocity=<-2, -4>
position=<-42444, 10762> velocity=< 4, -1>
position=<-31789, -53187> velocity=< 3, 5>
position=<-10480, 32077> velocity=< 1, -3>
position=< 32128, -10556> velocity=<-3, 1>
position=< 10858, -31869> velocity=<-1, 3>
position=<-53144, -42532> velocity=< 5, 4>
position=<-42461, -21215> velocity=< 4, 2>
position=<-42457, -10550> velocity=< 4, 1>
position=< 32186, 32083> velocity=<-3, -3>
position=<-42440, 53403> velocity=< 4, -5>
position=<-21130, 21419> velocity=< 2, -2>
position=< 42819, -21216> velocity=<-4, 2>
position=<-31810, -31874> velocity=< 3, 3>
position=<-53120, 32080> velocity=< 5, -3>
position=< 32178, -42536> velocity=<-3, 4>
position=< 21509, -31876> velocity=<-2, 3>
position=<-42436, -31868> velocity=< 4, 3>
position=<-42432, -21211> velocity=< 4, 2>
position=<-21155, 21422> velocity=< 2, -2>
position=<-53111, -42532> velocity=< 5, 4>
position=< 42819, -53188> velocity=<-4, 5>
position=<-21147, -21209> velocity=< 2, 2>
position=< 42840, 42741> velocity=<-4, -4>
position=<-31782, 32079> velocity=< 3, -3>
position=<-42448, -31871> velocity=< 4, 3>
position=< 21501, 53398> velocity=<-2, -5>
position=<-31797, 21426> velocity=< 3, -2>
position=<-42448, 53402> velocity=< 4, -5>
position=<-53110, -21214> velocity=< 5, 2>
position=< 32179, 32086> velocity=<-3, -3>
position=<-21138, 32080> velocity=< 2, -3>
position=< 10871, 32086> velocity=<-1, -3>
position=< 53470, 32082> velocity=<-5, -3>
position=<-53141, -53193> velocity=< 5, 5>
position=< 53462, -31868> velocity=<-5, 3>
position=<-21116, -42531> velocity=< 2, 4>
position=<-31782, -21212> velocity=< 3, 2>
position=<-42477, -31874> velocity=< 4, 3>
position=< 42836, -21218> velocity=<-4, 2>
position=<-31826, -53193> velocity=< 3, 5>
position=< 10818, 42740> velocity=<-1, -4>
position=< 53503, 10763> velocity=<-5, -1>
position=<-21109, -21215> velocity=< 2, 2>
position=<-21143, 53403> velocity=< 2, -5>
position=<-53120, 32078> velocity=< 5, -3>
position=<-31794, -10550> velocity=< 3, 1>
position=<-42473, 42740> velocity=< 4, -4>
position=<-31782, 53398> velocity=< 3, -5>
position=<-53120, -21218> velocity=< 5, 2>
position=<-53119, 53399> velocity=< 5, -5>
position=<-10500, 53404> velocity=< 1, -5>
position=< 53503, 53399> velocity=<-5, -5>
position=< 21498, 53396> velocity=<-2, -5>
position=<-31766, 42737> velocity=< 3, -4>
position=< 32138, -42532> velocity=<-3, 4>
position=< 32133, -10559> velocity=<-3, 1>
position=< 53505, 42738> velocity=<-5, -4>
position=< 21518, 21418> velocity=<-2, -2>
position=<-53144, 53399> velocity=< 5, -5>
position=<-31810, -42535> velocity=< 3, 4>
position=<-53099, -53187> velocity=< 5, 5>
position=<-31805, -42527> velocity=< 3, 4>
position=< 21478, 42740> velocity=<-2, -4>
position=< 53506, 53403> velocity=<-5, -5>
position=<-21165, -42533> velocity=< 2, 4>
position=< 10850, -21217> velocity=<-1, 2>
position=<-21143, -42528> velocity=< 2, 4>
position=<-31769, 42740> velocity=< 3, -4>
position=<-53088, -21211> velocity=< 5, 2>
position=<-53119, -31868> velocity=< 5, 3>
position=<-42448, 32084> velocity=< 4, -3>
position=<-53096, -31872> velocity=< 5, 3>
position=<-53091, -53186> velocity=< 5, 5>
position=< 42787, 10760> velocity=<-4, -1>
position=< 10861, 21427> velocity=<-1, -2>
position=< 53446, 53402> velocity=<-5, -5>
position=< 42832, 10759> velocity=<-4, -1>
position=< 21485, -42535> velocity=<-2, 4>
position=<-10482, -42532> velocity=< 1, 4>
position=< 53483, -53194> velocity=<-5, 5>
position=< 10835, -31868> velocity=<-1, 3>
position=< 53507, -10550> velocity=<-5, 1>
position=< 42815, 32077> velocity=<-4, -3>
position=< 10847, -42533> velocity=<-1, 4>
position=<-42448, 32084> velocity=< 4, -3>
position=<-21117, 32086> velocity=< 2, -3>
position=<-21167, 42742> velocity=< 2, -4>
position=<-31826, 21427> velocity=< 3, -2>
position=< 32181, 42745> velocity=<-3, -4>
position=< 32136, 10766> velocity=<-3, -1>
position=<-21138, 32085> velocity=< 2, -3>
position=< 21501, -21218> velocity=<-2, 2>
position=< 21510, -10557> velocity=<-2, 1>
position=<-31777, 21427> velocity=< 3, -2>
position=<-53084, 21426> velocity=< 5, -2>
position=<-31810, 21426> velocity=< 3, -2>
position=<-31797, -10556> velocity=< 3, 1>
position=<-31794, 42744> velocity=< 3, -4>
position=<-31773, -42529> velocity=< 3, 4>
position=< 10818, 32084> velocity=<-1, -3>
position=<-21149, -21209> velocity=< 2, 2>
position=< 10855, 32078> velocity=<-1, -3>
position=< 32186, -21215> velocity=<-3, 2>
position=< 53454, 42743> velocity=<-5, -4>
position=< 21530, 10759> velocity=<-2, -1>
position=< 42795, 53404> velocity=<-4, -5>
position=< 21522, 32078> velocity=<-2, -3>
position=<-21111, -42535> velocity=< 2, 4>
position=<-31826, 32079> velocity=< 3, -3>
position=<-21133, 10763> velocity=< 2, -1>
position=<-10499, 32077> velocity=< 1, -3>
position=< 32170, -21213> velocity=<-3, 2>
position=<-53096, 10765> velocity=< 5, -1>
position=< 42839, -10554> velocity=<-4, 1>
position=<-53101, 53400> velocity=< 5, -5>
position=<-31782, -21212> velocity=< 3, 2>
position=<-10508, -10556> velocity=< 1, 1>
position=<-53142, -31874> velocity=< 5, 3>
position=< 53479, 53399> velocity=<-5, -5>
position=<-31818, -31871> velocity=< 3, 3>
position=<-31789, -21217> velocity=< 3, 2>
position=< 32186, 10765> velocity=<-3, -1>
position=<-42427, -21215> velocity=< 4, 2>
position=<-21157, 10759> velocity=< 2, -1>
position=<-10448, 42737> velocity=< 1, -4>
position=< 21493, -10550> velocity=<-2, 1>
position=< 42819, -10557> velocity=<-4, 1>
position=< 42830, 10764> velocity=<-4, -1>
position=<-31794, -21211> velocity=< 3, 2>
position=<-21139, -10555> velocity=< 2, 1>
position=<-42441, 10766> velocity=< 4, -1>
position=< 32176, 42740> velocity=<-3, -4>
position=< 32136, 53401> velocity=<-3, -5>
position=<-10484, 42737> velocity=< 1, -4>
position=<-21138, 32079> velocity=< 2, -3>
position=<-10508, 10759> velocity=< 1, -1>
position=< 21522, 53396> velocity=<-2, -5>
position=<-31818, 21419> velocity=< 3, -2>
position=< 10867, -21213> velocity=<-1, 2>
position=< 21469, 21424> velocity=<-2, -2>
position=< 32139, -53191> velocity=<-3, 5>
position=< 42819, -21214> velocity=<-4, 2>
position=< 42803, -42534> velocity=<-4, 4>
position=< 10847, 53398> velocity=<-1, -5>
position=<-42437, -42528> velocity=< 4, 4>
position=< 21521, -42531> velocity=<-2, 4>
position=< 42835, 32080> velocity=<-4, -3>
position=<-10503, 21427> velocity=< 1, -2>
position=<-21167, -42527> velocity=< 2, 4>
position=< 21521, 32077> velocity=<-2, -3>
position=<-10484, -10553> velocity=< 1, 1>
position=<-10452, -42529> velocity=< 1, 4>
position=< 10847, -21213> velocity=<-1, 2>
position=< 53470, 21419> velocity=<-5, -2>
position=< 10862, -10551> velocity=<-1, 1>
position=<-42469, -31868> velocity=< 4, 3>
position=< 10838, -10550> velocity=<-1, 1>
position=< 32152, -42527> velocity=<-3, 4>
position=< 32176, 53401> velocity=<-3, -5>
position=<-31825, -10554> velocity=< 3, 1>
position=<-21167, -42528> velocity=< 2, 4>
position=<-21115, -31872> velocity=< 2, 3>
position=< 10866, -21217> velocity=<-1, 2>
position=< 21506, -10555> velocity=<-2, 1>
position=<-21143, 53399> velocity=< 2, -5>
position=<-10471, -31877> velocity=< 1, 3>
position=<-10508, 32085> velocity=< 1, -3>
position=< 10866, -10550> velocity=<-1, 1>
position=<-53088, -31877> velocity=< 5, 3>
position=<-10495, 10759> velocity=< 1, -1>
position=< 10847, 21418> velocity=<-1, -2>
position=<-10484, 42743> velocity=< 1, -4>
position=< 10818, 10764> velocity=<-1, -1>
position=< 32155, 42740> velocity=<-3, -4>
position=< 42787, 21423> velocity=<-4, -2>
position=< 53490, 21425> velocity=<-5, -2>
position=<-42448, 10764> velocity=< 4, -1>
position=<-21119, 53396> velocity=< 2, -5>
position=< 21528, -21211> velocity=<-2, 2>
position=< 53502, 10762> velocity=<-5, -1>
position=< 42820, -31873> velocity=<-4, 3>
position=< 21481, 53395> velocity=<-2, -5>
position=<-10468, -42527> velocity=< 1, 4>
position=< 32130, -21212> velocity=<-3, 2>
position=< 32181, -10554> velocity=<-3, 1>
position=< 32184, 53395> velocity=<-3, -5>
position=< 10851, -53188> velocity=<-1, 5>
position=< 21477, 42736> velocity=<-2, -4>
position=<-21159, 21426> velocity=< 2, -2>
position=<-31773, 10767> velocity=< 3, -1>
position=< 42827, 32077> velocity=<-4, -3>
position=<-31818, -21210> velocity=< 3, 2>
position=< 53458, -21218> velocity=<-5, 2>
position=<-10452, 42740> velocity=< 1, -4>
position=<-53142, -21215> velocity=< 5, 2>
position=< 10826, 53398> velocity=<-1, -5>
position=<-21166, 42741> velocity=< 2, -4>
position=<-42469, -53187> velocity=< 4, 5>
position=< 32173, 10768> velocity=<-3, -1>
position=< 53497, 10759> velocity=<-5, -1>
position=< 53449, -10557> velocity=<-5, 1>
position=< 32170, -53190> velocity=<-3, 5>
position=< 42843, -53187> velocity=<-4, 5>
position=< 10852, 32081> velocity=<-1, -3>
position=<-10499, 53395> velocity=< 1, -5>
position=< 21496, -53195> velocity=<-2, 5>
position=< 32157, 32084> velocity=<-3, -3>
position=< 42831, 10765> velocity=<-4, -1>
position=< 21498, -10556> velocity=<-2, 1>
position=< 42843, -53193> velocity=<-4, 5>
position=< 42811, -53189> velocity=<-4, 5>
position=<-42477, 53397> velocity=< 4, -5>

View File

@ -0,0 +1,20 @@
<Query Kind="Program" />
public int serial_num;
void Main()
{
serial_num = int.Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"11_input.txt")));
Enumerable
.Range(1, 300-3)
.SelectMany(x => Enumerable.Range(1, 300-3).Select(y => new {X=x, Y=y, Pow=SPow(x, y) }))
.OrderByDescending(p => p.Pow)
.Select(p => $"{p.X},{p.Y}")
.First()
.Dump();
}
int Pow(int x, int y) => (((x + 10) * ((x + 10) * y + serial_num) / 100) % 10) - 5;
int SPow(int x, int y) => Enumerable.Range(0, 9).Sum(v => Pow(x+v%3, y+v/3));

View File

@ -0,0 +1,34 @@
<Query Kind="Statements" />
int serial_num = int.Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"11_input.txt")));
int[,] scache = new int[301, 301];
for (int x = 1; x <= 300; x++) for (int y = 1; y <= 300; y++) scache[x, y] = (((x + 10) * ((x + 10) * y + serial_num) / 100) % 10) - 5;
int[,] cache = new int[301, 301];
for (int x = 1; x <= 300; x++) for (int y = 1; y <= 300; y++) cache[x, y] = 0;
var max = (pow:0, x:0,y:0,s:0);
for (int s = 1; s <= 300; s++)
{
for (int x = 1; x <= 1+ 300-s; x++)
{
for (int y = 1; y <= 1 + 300 - s; y++)
{
var v = cache[x, y];
for (int ix = 0; ix < s; ix++) v += scache[x + ix, y + s - 1];
for (int iy = 0; iy < s-1; iy++) v += scache[x + s - 1, y + iy];
cache[x,y]=v;
if (v > max.pow) max = (v,x,y,s);
}
}
Util.Progress = (s*100)/300;
}
$"{max.x},{max.y},{max.s}".Dump();

View File

@ -0,0 +1,70 @@
--- Day 11: Chronal Charge ---
You watch the Elves and their sleigh fade into the distance as they head toward the North Pole.
Actually, you're the one fading. The falling sensation returns.
The low fuel warning light is illuminated on your wrist-mounted device. Tapping it once causes it to project a hologram of the situation: a 300x300 grid of fuel cells and their current power levels, some negative. You're not sure what negative power means in the context of time travel, but it can't be good.
Each fuel cell has a coordinate ranging from 1 to 300 in both the X (horizontal) and Y (vertical) direction. In X,Y notation, the top-left cell is 1,1, and the top-right cell is 300,1.
The interface lets you select any 3x3 square of fuel cells. To increase your chances of getting to your destination, you decide to choose the 3x3 square with the largest total power.
The power level in a given fuel cell can be found through the following process:
Find the fuel cell's rack ID, which is its X coordinate plus 10.
Begin with a power level of the rack ID times the Y coordinate.
Increase the power level by the value of the grid serial number (your puzzle input).
Set the power level to itself multiplied by the rack ID.
Keep only the hundreds digit of the power level (so 12345 becomes 3; numbers with no hundreds digit become 0).
Subtract 5 from the power level.
For example, to find the power level of the fuel cell at 3,5 in a grid with serial number 8:
The rack ID is 3 + 10 = 13.
The power level starts at 13 * 5 = 65.
Adding the serial number produces 65 + 8 = 73.
Multiplying by the rack ID produces 73 * 13 = 949.
The hundreds digit of 949 is 9.
Subtracting 5 produces 9 - 5 = 4.
So, the power level of this fuel cell is 4.
Here are some more example power levels:
Fuel cell at 122,79, grid serial number 57: power level -5.
Fuel cell at 217,196, grid serial number 39: power level 0.
Fuel cell at 101,153, grid serial number 71: power level 4.
Your goal is to find the 3x3 square which has the largest total power. The square must be entirely within the 300x300 grid. Identify this square using the X,Y coordinate of its top-left fuel cell. For example:
For grid serial number 18, the largest total 3x3 square has a top-left corner of 33,45 (with a total power of 29); these fuel cells appear in the middle of this 5x5 region:
-2 -4 4 4 4
-4 4 4 4 -5
4 3 3 4 -4
1 1 2 4 -3
-1 0 2 -5 -2
For grid serial number 42, the largest 3x3 square's top-left is 21,61 (with a total power of 30); they are in the middle of this region:
-3 4 2 2 2
-4 4 3 3 4
-5 3 3 4 -4
4 3 3 4 -3
3 3 3 -5 -1
What is the X,Y coordinate of the top-left fuel cell of the 3x3 square with the largest total power?
--- Part Two ---
You discover a dial on the side of the device; it seems to let you select a square of any size, not just 3x3. Sizes from 1x1 to 300x300 are supported.
Realizing this, you now must find the square of any size with the largest total power. Identify this square by including its size as a third parameter after the top-left coordinate: a 9x9 square with a top-left corner of 3,5 is identified as 3,5,9.
For example:
For grid serial number 18, the largest total square (with a total power of 113) is 16x16 and has a top-left corner of 90,269, so its identifier is 90,269,16.
For grid serial number 42, the largest total square (with a total power of 119) is 12x12 and has a top-left corner of 232,251, so its identifier is 232,251,12.
What is the X,Y,size identifier of the square with the largest total power?

View File

@ -0,0 +1 @@
1723

View File

@ -0,0 +1,44 @@
<Query Kind="Statements" />
var input_initial = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"12_input.txt"))[0].Substring(15).Select(p=>p=='#'?1:0).ToArray();
var input_rules = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"12_input.txt"))
.Skip(1)
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select( p => ( p.Split(' ')[0].Select(q=>q=='#'?1:0).ToArray() , p.Split(' ')[2][0]=='#'?1:0 ) )
.Select( p => ( p.Item1.Reverse().Select((u,i)=>(u<<i)).Aggregate((s,t)=>s|t) , p.Item2 ) )
.OrderBy(p => p.Item1)
.Select(p => p.Item2)
.ToArray();
int LEN = 2 * 20 + input_initial.Length + 2 * 20;
int OFF = LEN / 2 - input_initial.Length / 2;
int[] pots = new int[LEN];
int[] ipots = new int[LEN];
//input_initial.Dump();
//input_rules.Dump();
for (int i = 0; i < input_initial.Length; i++) pots[i+OFF]=input_initial[i];
int[] rules = new int[OFF*2+100];
for (int gen = 0; gen < 20; gen++)
{
//string.Join("", rules.Select(p => p + "")).Dump();
($"{gen:00}: "+string.Join("", pots.Select(p => (p == 0) ? "." : "#"))).Dump();
for (int p = 2; p < pots.Length-2; p++)
{
var rule = (pots[p - 2] << 4) | (pots[p - 1] << 3) | (pots[p - 0] << 2) | (pots[p + 1] << 1) | (pots[p + 2] << 0);
rules[p]=rule;
ipots[p]=input_rules[rule];
}
for (int p = 0, q = 0; p < pots.Length; pots[p++] = ipots[q++]) ;
}
($"{20:00}: " + string.Join("", pots.Select(p => (p == 0) ? "." : "#"))).Dump();
"".Dump();
pots.Select((p,i) => (p,i-OFF)).Where(p => p.p>0).Sum(p=>p.Item2).Dump();

View File

@ -0,0 +1,60 @@
<Query Kind="Statements" />
var input_initial = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"12_input.txt"))[0].Substring(15).Select(p => p == '#' ? 1 : 0).ToArray();
var input_rules = File
.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"12_input.txt"))
.Skip(1)
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(p => (p.Split(' ')[0].Select(q => q == '#' ? 1 : 0).ToArray(), p.Split(' ')[2][0] == '#' ? 1 : 0))
.Select(p => (p.Item1.Reverse().Select((u, i) => (u << i)).Aggregate((s, t) => s | t), p.Item2))
.OrderBy(p => p.Item1)
.Select(p => p.Item2)
.ToArray();
int LEN = 2 * 2000 + input_initial.Length + 2 * 2000;
int OFF = LEN / 2 - input_initial.Length / 2;
int[] pots = new int[LEN];
int[] ipots = new int[LEN];
//input_initial.Dump();
//input_rules.Dump();
for (int i = 0; i < input_initial.Length; i++) pots[i + OFF] = input_initial[i];
int hashOffset = 0;
string lastHash = "";
for (long gen = 0; gen < 50000000000L; gen++)
{
for (int p = 2; p < pots.Length - 2; p++)
{
var rule = (pots[p - 2] << 4) | (pots[p - 1] << 3) | (pots[p - 0] << 2) | (pots[p + 1] << 1) | (pots[p + 2] << 0);
ipots[p] = input_rules[rule];
}
for (int p = 0, q = 0; p < pots.Length; pots[p++] = ipots[q++]);
var currHash = string.Join("", pots.SkipWhile(p => p == 0).Reverse().SkipWhile(p => p == 0).Select(p => p.ToString()));
if (currHash == lastHash)
{
checked
{
var hashOffsetNew = pots.TakeWhile(p => p == 0).Count();
var drift = (hashOffsetNew - hashOffset);
var score_curr = pots.Select((p, i) => (p, i - OFF)).Where(p => p.p > 0).Sum(p => p.Item2);
var count_curr = pots.Sum();
var score_Future = score_curr + (50_000_000_000 - (gen + 1)) * count_curr;
score_Future.Dump();
$"stable after {gen} Generations with generational drift of {drift}".Dump();
return;
}
}
lastHash = currHash;
hashOffset = pots.TakeWhile(p => p==0).Count();
}
"ERROR - UNSTABLE".Dump();

View File

@ -0,0 +1,74 @@
--- Day 12: Subterranean Sustainability ---
The year 518 is significantly more underground than your history books implied. Either that, or you've arrived in a vast cavern network under the North Pole.
After exploring a little, you discover a long tunnel that contains a row of small pots as far as you can see to your left and right. A few of them contain plants - someone is trying to grow things in these geothermally-heated caves.
The pots are numbered, with 0 in front of you. To the left, the pots are numbered -1, -2, -3, and so on; to the right, 1, 2, 3.... Your puzzle input contains a list of pots from 0 to the right and whether they do (#) or do not (.) currently contain a plant, the initial state. (No other pots currently contain plants.) For example, an initial state of #..##.... indicates that pots 0, 3, and 4 currently contain plants.
Your puzzle input also contains some notes you find on a nearby table: someone has been trying to figure out how these plants spread to nearby pots. Based on the notes, for each generation of plants, a given pot has or does not have a plant based on whether that pot (and the two pots on either side of it) had a plant in the last generation. These are written as LLCRR => N, where L are pots to the left, C is the current pot being considered, R are the pots to the right, and N is whether the current pot will have a plant in the next generation. For example:
A note like ..#.. => . means that a pot that contains a plant but with no plants within two pots of it will not have a plant in it during the next generation.
A note like ##.## => . means that an empty pot with two plants on each side of it will remain empty in the next generation.
A note like .##.# => # means that a pot has a plant in a given generation if, in the previous generation, there were plants in that pot, the one immediately to the left, and the one two pots to the right, but not in the ones immediately to the right and two to the left.
It's not clear what these plants are for, but you're sure it's important, so you'd like to make sure the current configuration of plants is sustainable by determining what will happen after 20 generations.
For example, given the following input:
initial state: #..#.#..##......###...###
...## => #
..#.. => #
.#... => #
.#.#. => #
.#.## => #
.##.. => #
.#### => #
#.#.# => #
#.### => #
##.#. => #
##.## => #
###.. => #
###.# => #
####. => #
For brevity, in this example, only the combinations which do produce a plant are listed. (Your input includes all possible combinations.) Then, the next 20 generations will look like this:
1 2 3
0 0 0 0
0: ...#..#.#..##......###...###...........
1: ...#...#....#.....#..#..#..#...........
2: ...##..##...##....#..#..#..##..........
3: ..#.#...#..#.#....#..#..#...#..........
4: ...#.#..#...#.#...#..#..##..##.........
5: ....#...##...#.#..#..#...#...#.........
6: ....##.#.#....#...#..##..##..##........
7: ...#..###.#...##..#...#...#...#........
8: ...#....##.#.#.#..##..##..##..##.......
9: ...##..#..#####....#...#...#...#.......
10: ..#.#..#...#.##....##..##..##..##......
11: ...#...##...#.#...#.#...#...#...#......
12: ...##.#.#....#.#...#.#..##..##..##.....
13: ..#..###.#....#.#...#....#...#...#.....
14: ..#....##.#....#.#..##...##..##..##....
15: ..##..#..#.#....#....#..#.#...#...#....
16: .#.#..#...#.#...##...#...#.#..##..##...
17: ..#...##...#.#.#.#...##...#....#...#...
18: ..##.#.#....#####.#.#.#...##...##..##..
19: .#..###.#..#.#.#######.#.#.#..#.#...#..
20: .#....##....#####...#######....#.#..##.
The generation is shown along the left, where 0 is the initial state. The pot numbers are shown along the top, where 0 labels the center pot, negative-numbered pots extend to the left, and positive pots extend toward the right. Remember, the initial state begins at pot 0, which is not the leftmost pot used in this example.
After one generation, only seven plants remain. The one in pot 0 matched the rule looking for ..#.., the one in pot 4 matched the rule looking for .#.#., pot 9 matched .##.., and so on.
In this example, after 20 generations, the pots shown as # contain plants, the furthest left of which is pot -2, and the furthest right of which is pot 34. Adding up all the numbers of plant-containing pots after the 20th generation produces 325.
After 20 generations, what is the sum of the numbers of all pots which contain a plant?
--- Part Two ---
You realize that 20 generations aren't enough. After all, these plants will need to last another 1500 years to even reach your timeline, not to mention your future.
After fifty billion (50000000000) generations, what is the sum of the numbers of all pots which contain a plant?

View File

@ -0,0 +1,34 @@
initial state: .##..#.#..##..##..##...#####.#.....#..#..##.###.#.####......#.......#..###.#.#.##.#.#.###...##.###.#
.##.# => #
##.#. => #
##... => #
#.... => .
.#..# => .
#.##. => .
.##.. => .
.#.## => .
###.. => .
..##. => #
##### => #
#...# => #
.#... => #
###.# => #
#.### => #
##..# => .
.###. => #
...## => .
..#.# => .
##.## => #
....# => .
#.#.# => #
#.#.. => .
.#### => .
...#. => #
..### => .
..#.. => #
..... => .
####. => .
#..## => #
.#.#. => .
#..#. => #

View File

@ -0,0 +1,229 @@
<Query Kind="Program" />
class Rail
{
public int X,Y;
public Rail N,E,S,W;
}
class Cart
{
public Dir D;
public Rail R;
public int Mem=0;
}
enum Dir {N=0,E=1,S=2,W=3}
void Main()
{
(List<Rail> rails, List<Cart> carts) = Parse(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"13_input.txt")));
for (int gen=0;;gen++)
{
foreach (var c in carts.OrderBy(c => c.R.Y*10000+c.R.X))
{
switch (c.D)
{
case Dir.S:
Move(c, new[] { (Dir.E, c.R.E), (Dir.S, c.R.S), (Dir.W, c.R.W) });
break;
case Dir.W:
Move(c, new[] { (Dir.S, c.R.S), (Dir.W, c.R.W), (Dir.N, c.R.N) });
break;
case Dir.N:
Move(c, new[] { (Dir.W, c.R.W), (Dir.N, c.R.N), (Dir.E, c.R.E) });
break;
case Dir.E:
Move(c, new[] { (Dir.N, c.R.N), (Dir.E, c.R.E), (Dir.S, c.R.S) });
break;
}
if (carts.Any(oc => oc != c && oc.R == c.R)) { $"{c.R.X},{c.R.Y} after {gen}".Dump(); return; }
}
//foreach (var c in carts) $"{c.R.X}|{c.R.Y}|{c.D}".Dump();"------".Dump();
}
}
void Move(Cart c, IEnumerable<(Dir, Rail)> options)
{
var outs = options.Where(p => p.Item2 != null).ToList();
if (outs.Count == 1)
{
c.R = outs[0].Item2;
c.D = outs[0].Item1;
}
else
{
c.R = outs[c.Mem % 3].Item2;
c.D = outs[c.Mem % 3].Item1;
c.Mem++;
}
}
void Dump(List<Rail> rails, List<Cart> carts)
{
int maxx = rails.Max(r => r.X) + 1;
int maxy = rails.Max(r => r.Y) + 1;
var b = new StringBuilder();
for (int yy = 0; yy < maxy; yy++)
{
for (int xx = 0; xx < maxx; xx++)
{
var cc = carts.FirstOrDefault(c => c.R.X == xx && c.R.Y == yy);
if (cc != null)
{
if (cc.D == Dir.N) b.Append('^');
if (cc.D == Dir.E) b.Append('>');
if (cc.D == Dir.S) b.Append('v');
if (cc.D == Dir.W) b.Append('<');
}
else if (rails.Any(r => r.X == xx && r.Y == yy))
{
b.Append('.');
}
else
{
b.Append(' ');
}
}
b.AppendLine();
}
b.ToString().Dump();
"".Dump();
}
(List<Rail>, List<Cart>) Parse(string[] line)
{
var rails = new Dictionary<(int,int), Rail>();
var carts = new List<Cart>();
Rail get(int x, int y) { if(rails.TryGetValue((x,y), out var v))return v; Rail r = new Rail { X = x, Y = y }; rails.Add((x,y),r); return r; }
char last = ' ';
for (int y = 0; y < line.Length; y++)
{
last = ' ';
for (int x = 0; x < line[y].Length; x++)
{
char c = line[y][x];
if (c==' ') continue;
switch (c)
{
case '-':
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x + 1, y);
rr.W = ra; ra.E = rr;
rr.E = rb; rb.W = rr;
}
break;
case '|':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x, y + 1);
rr.N = ra; ra.S = rr;
rr.S = rb; rb.N = rr;
}
break;
case '+':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x + 1, y);
var rc = get(x, y + 1);
var rd = get(x - 1, y);
rr.N = ra; ra.S = rr;
rr.E = rb; rb.W = rr;
rr.S = rc; rc.N = rr;
rr.W = rd; rd.E = rr;
}
break;
case '/':
if (last == '-' || last == '+')
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x, y - 1);
rr.W = ra; ra.E = rr;
rr.N = rb; rb.S = rr;
}
else
{
var rr = get(x, y);
var ra = get(x + 1, y);
var rb = get(x, y + 1);
rr.E = ra; ra.W = rr;
rr.S = rb; rb.N = rr;
}
break;
case '\\':
if (last == '-' || last == '+')
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x, y + 1);
rr.W = ra; ra.E = rr;
rr.S = rb; rb.N = rr;
}
else
{
var rr = get(x, y);
var ra = get(x + 1, y);
var rb = get(x, y - 1);
rr.E = ra; ra.W = rr;
rr.N = rb; rb.S = rr;
}
break;
case '^':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x, y + 1);
rr.N = ra; ra.S = rr;
rr.S = rb; rb.N = rr;
carts.Add(new Cart { D = Dir.N, R=rr, });
}
break;
case 'v':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x, y + 1);
rr.N = ra; ra.S = rr;
rr.S = rb; rb.N = rr;
carts.Add(new Cart { D = Dir.S, R=rr, });
}
break;
case '>':
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x + 1, y);
rr.W = ra; ra.E = rr;
rr.E = rb; rb.W = rr;
carts.Add(new Cart { D = Dir.E, R = rr, });
}
break;
case '<':
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x + 1, y);
rr.W = ra; ra.E = rr;
rr.E = rb; rb.W = rr;
carts.Add(new Cart { D = Dir.W, R = rr, });
}
break;
}
last = c;
}
}
return (rails.Select(p=>p.Value).ToList(), carts);
}

View File

@ -0,0 +1,234 @@
<Query Kind="Program" />
class Rail
{
public int X,Y;
public Rail N,E,S,W;
}
class Cart
{
public bool C=false;
public Dir D;
public Rail R;
public int Mem = 0;
}
enum Dir { N = 0, E = 1, S = 2, W = 3 }
void Main()
{
(List<Rail> rails, List<Cart> carts) = Parse(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"13_input.txt")));
int cc = carts.Count;
for (int gen = 0; ; gen++)
{
foreach (var c in carts.OrderBy(c => c.R.Y * 10000 + c.R.X))
{
if (c.C)continue;
switch (c.D)
{
case Dir.S:
Move(c, new[] { (Dir.E, c.R.E), (Dir.S, c.R.S), (Dir.W, c.R.W) });
break;
case Dir.W:
Move(c, new[] { (Dir.S, c.R.S), (Dir.W, c.R.W), (Dir.N, c.R.N) });
break;
case Dir.N:
Move(c, new[] { (Dir.W, c.R.W), (Dir.N, c.R.N), (Dir.E, c.R.E) });
break;
case Dir.E:
Move(c, new[] { (Dir.N, c.R.N), (Dir.E, c.R.E), (Dir.S, c.R.S) });
break;
}
var dc = carts.FirstOrDefault(oc => oc != c && oc.R == c.R && !oc.C);
if (dc != null) { c.C = dc.C = true; cc-=2; }
if (cc == 1) { $"{carts.Single(p => !p.C).R.X},{carts.Single(p => !p.C).R.Y} after {gen}".Dump(); return; }
}
//foreach (var c in carts) $"{c.R.X}|{c.R.Y}|{c.D}".Dump();"------".Dump();
}
}
void Move(Cart c, IEnumerable<(Dir, Rail)> options)
{
var outs = options.Where(p => p.Item2 != null).ToList();
if (outs.Count == 1)
{
c.R = outs[0].Item2;
c.D = outs[0].Item1;
}
else
{
c.R = outs[c.Mem % 3].Item2;
c.D = outs[c.Mem % 3].Item1;
c.Mem++;
}
}
void Dump(List<Rail> rails, List<Cart> carts)
{
int maxx = rails.Max(r => r.X) + 1;
int maxy = rails.Max(r => r.Y) + 1;
var b = new StringBuilder();
for (int yy = 0; yy < maxy; yy++)
{
for (int xx = 0; xx < maxx; xx++)
{
var cc = carts.FirstOrDefault(c => c.R.X == xx && c.R.Y == yy);
if (cc != null)
{
if (cc.D == Dir.N) b.Append('^');
if (cc.D == Dir.E) b.Append('>');
if (cc.D == Dir.S) b.Append('v');
if (cc.D == Dir.W) b.Append('<');
}
else if (rails.Any(r => r.X == xx && r.Y == yy))
{
b.Append('.');
}
else
{
b.Append(' ');
}
}
b.AppendLine();
}
b.ToString().Dump();
"".Dump();
}
(List<Rail>, List<Cart>) Parse(string[] line)
{
var rails = new Dictionary<(int, int), Rail>();
var carts = new List<Cart>();
Rail get(int x, int y) { if (rails.TryGetValue((x, y), out var v)) return v; Rail r = new Rail { X = x, Y = y }; rails.Add((x, y), r); return r; }
char last = ' ';
for (int y = 0; y < line.Length; y++)
{
last = ' ';
for (int x = 0; x < line[y].Length; x++)
{
char c = line[y][x];
if (c == ' ') continue;
switch (c)
{
case '-':
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x + 1, y);
rr.W = ra; ra.E = rr;
rr.E = rb; rb.W = rr;
}
break;
case '|':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x, y + 1);
rr.N = ra; ra.S = rr;
rr.S = rb; rb.N = rr;
}
break;
case '+':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x + 1, y);
var rc = get(x, y + 1);
var rd = get(x - 1, y);
rr.N = ra; ra.S = rr;
rr.E = rb; rb.W = rr;
rr.S = rc; rc.N = rr;
rr.W = rd; rd.E = rr;
}
break;
case '/':
if (last == '-' || last == '+')
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x, y - 1);
rr.W = ra; ra.E = rr;
rr.N = rb; rb.S = rr;
}
else
{
var rr = get(x, y);
var ra = get(x + 1, y);
var rb = get(x, y + 1);
rr.E = ra; ra.W = rr;
rr.S = rb; rb.N = rr;
}
break;
case '\\':
if (last == '-' || last == '+')
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x, y + 1);
rr.W = ra; ra.E = rr;
rr.S = rb; rb.N = rr;
}
else
{
var rr = get(x, y);
var ra = get(x + 1, y);
var rb = get(x, y - 1);
rr.E = ra; ra.W = rr;
rr.N = rb; rb.S = rr;
}
break;
case '^':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x, y + 1);
rr.N = ra; ra.S = rr;
rr.S = rb; rb.N = rr;
carts.Add(new Cart { D = Dir.N, R = rr, });
}
break;
case 'v':
{
var rr = get(x, y);
var ra = get(x, y - 1);
var rb = get(x, y + 1);
rr.N = ra; ra.S = rr;
rr.S = rb; rb.N = rr;
carts.Add(new Cart { D = Dir.S, R = rr, });
}
break;
case '>':
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x + 1, y);
rr.W = ra; ra.E = rr;
rr.E = rb; rb.W = rr;
carts.Add(new Cart { D = Dir.E, R = rr, });
}
break;
case '<':
{
var rr = get(x, y);
var ra = get(x - 1, y);
var rb = get(x + 1, y);
rr.W = ra; ra.E = rr;
rr.E = rb; rb.W = rr;
carts.Add(new Cart { D = Dir.W, R = rr, });
}
break;
}
last = c;
}
}
return (rails.Select(p => p.Value).ToList(), carts);
}

View File

@ -0,0 +1,206 @@
--- Day 13: Mine Cart Madness ---
A crop of this size requires significant logistics to transport produce, soil, fertilizer, and so on. The Elves are very busy pushing things around in carts on some kind of rudimentary system of tracks they've come up with.
Seeing as how cart-and-track systems don't appear in recorded history for another 1000 years, the Elves seem to be making this up as they go along. They haven't even figured out how to avoid collisions yet.
You map out the tracks (your puzzle input) and see where you can help.
Tracks consist of straight paths (| and -), curves (/ and \), and intersections (+). Curves connect exactly two perpendicular pieces of track; for example, this is a closed loop:
/----\
| |
| |
\----/
Intersections occur when two perpendicular paths cross. At an intersection, a cart is capable of turning left, turning right, or continuing straight. Here are two loops connected by two intersections:
/-----\
| |
| /--+--\
| | | |
\--+--/ |
| |
\-----/
Several carts are also on the tracks. Carts always face either up (^), down (v), left (<), or right (>). (On your initial map, the track under each cart is a straight path matching the direction the cart is facing.)
Each time a cart has the option to turn (by arriving at any intersection), it turns left the first time, goes straight the second time, turns right the third time, and then repeats those directions starting again with left the fourth time, straight the fifth time, and so on. This process is independent of the particular intersection at which the cart has arrived - that is, the cart has no per-intersection memory.
Carts all move at the same speed; they take turns moving a single step at a time. They do this based on their current location: carts on the top row move first (acting from left to right), then carts on the second row move (again from left to right), then carts on the third row, and so on. Once each cart has moved one step, the process repeats; each of these loops is called a tick.
For example, suppose there are two carts on a straight track:
| | | | |
v | | | |
| v v | |
| | | v X
| | ^ ^ |
^ ^ | | |
| | | | |
First, the top cart moves. It is facing down (v), so it moves down one square. Second, the bottom cart moves. It is facing up (^), so it moves up one square. Because all carts have moved, the first tick ends. Then, the process repeats, starting with the first cart. The first cart moves down, then the second cart moves up - right into the first cart, colliding with it! (The location of the crash is marked with an X.) This ends the second and last tick.
Here is a longer example:
/->-\
| | /----\
| /-+--+-\ |
| | | | v |
\-+-/ \-+--/
\------/
/-->\
| | /----\
| /-+--+-\ |
| | | | | |
\-+-/ \->--/
\------/
/---v
| | /----\
| /-+--+-\ |
| | | | | |
\-+-/ \-+>-/
\------/
/---\
| v /----\
| /-+--+-\ |
| | | | | |
\-+-/ \-+->/
\------/
/---\
| | /----\
| /->--+-\ |
| | | | | |
\-+-/ \-+--^
\------/
/---\
| | /----\
| /-+>-+-\ |
| | | | | ^
\-+-/ \-+--/
\------/
/---\
| | /----\
| /-+->+-\ ^
| | | | | |
\-+-/ \-+--/
\------/
/---\
| | /----<
| /-+-->-\ |
| | | | | |
\-+-/ \-+--/
\------/
/---\
| | /---<\
| /-+--+>\ |
| | | | | |
\-+-/ \-+--/
\------/
/---\
| | /--<-\
| /-+--+-v |
| | | | | |
\-+-/ \-+--/
\------/
/---\
| | /-<--\
| /-+--+-\ |
| | | | v |
\-+-/ \-+--/
\------/
/---\
| | /<---\
| /-+--+-\ |
| | | | | |
\-+-/ \-<--/
\------/
/---\
| | v----\
| /-+--+-\ |
| | | | | |
\-+-/ \<+--/
\------/
/---\
| | /----\
| /-+--v-\ |
| | | | | |
\-+-/ ^-+--/
\------/
/---\
| | /----\
| /-+--+-\ |
| | | X | |
\-+-/ \-+--/
\------/
After following their respective paths for a while, the carts eventually crash. To help prevent crashes, you'd like to know the location of the first crash. Locations are given in X,Y coordinates, where the furthest left column is X=0 and the furthest top row is Y=0:
111
0123456789012
0/---\
1| | /----\
2| /-+--+-\ |
3| | | X | |
4\-+-/ \-+--/
5 \------/
In this example, the location of the first crash is 7,3.
--- Part Two ---
There isn't much you can do to prevent crashes in this ridiculous system. However, by predicting the crashes, the Elves know where to be in advance and instantly remove the two crashing carts the moment any crash occurs.
They can proceed like this for a while, but eventually, they're going to run out of carts. It could be useful to figure out where the last cart that hasn't crashed will end up.
For example:
/>-<\
| |
| /<+-\
| | | v
\>+</ |
| ^
\<->/
/---\
| |
| v-+-\
| | | |
\-+-/ |
| |
^---^
/---\
| |
| /-+-\
| v | |
\-+-/ |
^ ^
\---/
/---\
| |
| /-+-\
| | | |
\-+-/ ^
| |
\---/
After four very expensive crashes, a tick ends with only one cart remaining; its final location is 6,4.
What is the location of the last cart at the end of the first tick where it is the only cart left?

View File

@ -0,0 +1,150 @@
/---------------------\ /----------------<---------------------------------------------------------------------\
| | | /---------------------------\ |
/-------+---------------------+-----------+---------+---------------------------+------\ |
/----------+-------+---------------------+---\ /+---------+---------------------------+------+------\ |
/-+----------+-------+---------------------+---+------++---------+-------------------------\ | | | |
| | | | /------------------+---+------++---------+-----\ | | /----+------+---------------------------\ |
| | /------+-------+--+------------------+---+-\ || | | /----+-+-+----+------+----------------\ | |
| | | | /-----+--+------------------+---+-+----++---------+-----+--------------+----+-+-+----+---\ | | | |
| | | | | /+--+--------------\ | | | || /----+-----+--------------+----+-+-+----+---+--+----------------+---------\| |
| | | | | || | | | | | || | | | | | | | | | | | || |
| | | | | || | /-------+---+-\ | | || | | | | | | | | | | /-------+---------++------+---\
| | | | | || | | | | | | | || | | | /--------+----+-+-+----+---+\ | | | || | |
| | | | | || | | | | | | | || |/---+-----+\ | | | | | | || | | | || | |
| | | /-+-+----++--+------+-------+---+-+-+-+----++----++---+-----++--->+--------+----+-+-+----+---++-+-----\ | | || | |
| | | | | | /++--+------+-------+---+-+-+-+----++----++---+-----++----+--------+----+-+-+----+---++-+-----+--+-------+-\ || | |
| | /+----+-+-+---+++--+------+-------+---+-+-+-+----++-\ || | || | | | | | | || | | | | | || | |
| | || | | | ||| | | | | | | | || | /++---+-----++----+--------+----+-+-+-\ | || | | | | | || | |
| | || |/+-+---+++--+------+-------+---+-+-+-+----++-+-+++---+\ /-++----+--\ | | | | | | || | | | | |/------++--\ | |
| | || ||| |/--+++--+------+-------+---+-+-+-+----++\| ||| || | || | | /---+----+-+-+-+--+---++-+----\| | | || || | | |
| | || ||| || ||| |/-----+-------+---+-+-+-+----++++-+++---++--+-++----+--+-+---+----+-+-+-+--+---++-+----++--+-------+-++---\ || | | |
| | || ||| || |\+--++-----+-------/ | | | | |||| ||| v| | || | | | | | | | | |/--++-+----++--+-------+-++---+--++--+---+--\|
/+-+--++----+++-++--+-+--++-----+-----------+-+-+-+----++++-+++---++--+-++----+--+-+-\ \----+-+-+-+--++--++-+----++--+-------/ || | || | | ||
|| | /++----+++-++--+-+--++-----+-----------+-+-+-+----++++-+++---++--+-++---\| | | | | | | | || || | || | || | || | | ||
|| | ||| ||| || | | || | | | | |/---++++-+++---++--+-++---++--+-+-+------+-+-+-+--++--++-+----++--+---------++--\| || | | ||
|| | |||/---+++-++--+-+--++-----+-----------+-+-+-++---++++-+++---++--+-++---++--+-+-+---\ | | | | || || | || | || || || | | ||
|| | |||| ||| || | | || | | | | || |||| ||| || | ||/--++--+-+-+---+--+-+\| | || || | || | || || || | | ||
|| | |||| ||| || /+-+--++-----+-----------+-+-+-++---++++-+++---++--+-+++--++--+-+-+---+--+-+++-+--++--++-+----++--+-------\ || || || | | ||
|| | |||| ||| \+-++-+--++-----+-----------+-+-+-++---++++-+++---++--+-+++--++--+-+-+---+--+-+++-+--++--/| | || \-------+-++--++--++--+---+--+/
|| | |||| ||| |/++-+--++-----+-----------+-+-+-++---++++-+++---++--+-+++\ || | | | | | ||| | || | | || | || || || | | |
|| | |||| ||| |||| | || | | | | || |||| ||| || | |||| || | | | | | ||| | || | | || | || || || | | |
|| |/++++---+++--++++-+--++---\ | | | | || |||| ||| ||/-+-++++-++--+-+-+---+--+-+++-+--++---+-+----++----------+-++--++--++--+---+--+\
|| |||||| ||| |||| | || | | | | | || |||| |\+---+++-+-++++-++--+-+-+---+--+-+++-+--++---+-+----++----------+-++--++--/| | | ||
|| |||||| ||| |||| | || | | /--------+-+-+-++---++++-+-+---+++-+-++++-++--+-+-+---+-\| ||| | || | | || | || || | | | ||
|| |||||| ||| |||| | /++---+-+--+--------+-+-+-++---++++-+-+---+++-+-++++-++--+-+-+---+-++-+++-+--++-\ | | || /--------+-++--++---+--+\ | ||
|| |||||| ||| |||| | ||| | | | /----+-+-+-++---++++-+-+---+++-+-++++-++--+-+-+---+-++-+++-+--++-+-+-+----++-+--------+-++--++---+--++--+-\||
|| |||||| ||| |||| | ||| | | | | | | | || /++++-+-+--\||| | |||| || |/+-+---+-++-+++-+--++-+-+-+----++-+--------+-++--++---+--++-\| |||
|| |||||| ||| |||| | ||| /+-+--+---+----+-+-+-++--+++++-+-+--++++-+-++++-++--+++-+---+-++-+++-+--++-+-+-+----++-+--\ | || || | || || |||
|| |||||| /-+++--++++-+-+++--++-+--+---+----+-+\| || ||||| | | |||| | |||| || ||| | | || ||| | || | | | || | | /-+-++--++---+\ || || |||
|| |||||| | ||| |||| \-+++--++-+--+---+----/ ||| || ||||| | | |||| | |||| || ||| | | || ||| | || | | | || | | | | || || || || || |||
|| ||||\+-+-+++--++++---+++--++-+--+---+>-----+++-/| ||||| | | |||\-+-++++-++--+++-+---+-++-+++-+--++-+-+-+----++-+--+---+-+-++--++---++-++-++-++/
|| |||| | | ||| |||| ||| || | | |/-----+++--+--+++++-+-+--+++--+-++++-++--+++-+---+-++-+++-+--++-+-+-+-\ || | | | | || || || || || ||
|| |||| | | ||| |\++---+++--++-+--+---++-----+++--+--+++++-+-+--+++--+-+++//++--+++-+---+-++-+++-+--++-+-+-+-+--++-+--+---+\| || || || || || ||
|| |||| | | ||| | |\---+++--++-+--+---++-----+++--+--+++++-+-+--+++--+>+++-+++--+++-+---+-++-+++-+--++-+-+-+-+--++-+--+---+++-/| || || || || ||
|| |||| | | ||| | | ||| || | | /-++-----+++--+--+++++-+-+--+++--+-+++-+++--+++-+---+-++-+++-+--++-+-+-+-+--++-+--+---+++--+--++-<-++-++-++\||
|| |||\-+-+-+++--+-+----+++--++-+--+-+-++-----+++--+--++++/ | | ||| | ||| ||| ||| | | || ||| | || |/+-+-+--++-+--+---+++--+--++---++-++-+++++\
|| ||| | | ||| | | ||| || | | | || ||| | |||| \-+--+++--+-+++-+++--+++-+---+-++-+++-/ || ||| | | || | | ||| | || || || ||||||
|| ||| | | ||| | | ||| || | | | ||/----+++--+--++++----+\ ||| | ||| ||| |||/+---+-++-+++----++-+++-+-+--++-+--+---+++--+--++--\|| || ||||||
|| ||| | | ||| | | ||| || | |/+-+++----+++--+--++++----++-+++--+-+++-+++--+++++---+-++-+++----++-+++-+-+--++-+--+---+++-\| || ||| || ||||||
|| \++--+-+-+++--+-+----+++--++-+--+++-+++----++/ | |||| || ||| \-+++-+++--/|||| | || ||| || ||| | | || | | ||| || || ||| || ||||||
|| || | | ||| | | ||| || | ||| ||| || | |||| || ||| ||| ||\---++++---+-++-+++----++-++/ | | || | | ||| || || ||| || ||||||
|| || | | ||| | | ||| || | ||| ||| || | |||| \+-+++----+/| \+----++++---+-++-+++----++-++--+-+--++-+--+---+/| || || ||| || ||||||
|| || /+-+-+++--+-+----+++--++-+--+++-+++----++---+--++++-----+-+++----+-+--+----++++---+\|| ||| || || | | || \--+---+-+-++--++--+++-+/ ||||||
|| || || | ||| | | /+++--++-+--+++-+++----++---+--++++-----+-+++----+-+--+----++++---++++-+++----++-++--+-+--++--\ | | | || || ||| | ||||||
|| || || | ||| | | |||| || | ||| ||| || | |||| | |\+----+-+--+----++++---++++-/|| || || | | || | | | | || || ||| | ||||||
|| || || | ||| | | /++++--++-+--+++-+++----++---+--++++-----+-+-+----+-+--+----++++---++++--++----++-++--+-+--++--+-+---+-+-++-\|| ||| | ||||||
|| || || | ||| | | ||||| || | ||| ||| || | |||| | | | | | | |||| |||| /++----++-++--+-+--++--+\| |/+-++-+++\ ||| | ||||||
|| || || | ||| | | ||||| || | ||| ||| || |/-++++-----+-+-+----+-+--+----++++---++++-+++\ || || | | || ||| ||| || |||| ||| | ||||||
|| || || | ||| | | /+++++--++-+--+++<+++----++---++-++++-----+-+-+----+-+--+----++++---++++-++++---++-++--+\| || ||| ||| || |||| ||| | ||||||
|| || || | ||| | | |||||| || | |\+-+++----++---++-++++-----+-+-+----+-+--+----++++---++++-++++---++-++--+++--++--+++---+++-/| |||| ||| | ||||||
|| || || | ||| | | |||\++--++-+--+-+-+++----++---++-++++-----+-+-+----+-+--+----++++---++++-++++---++-/| ||| || ||| ||| | |||| ||| | |^||||
|| || || | ||| | |/+++-++--++-+--+-+-+++----++---++-++++-----+-+-+----+-+--+----++++\ |||| |||| |\--+--+++--++--+++---+++--+-++++-+++-+--++++/|
|| || || | ||| | ||||| || || | | | ||| || || |||| | | | | | | ||\++--++++-++++---+---+--+++--++--+++---+++--+-++++-/|| | |||| |
|| || || | ||| | ||||| || || | | | ||| || || |||| | | | | | | || || |||| |||| | | ||| || ||| ||| | |||| || | |||| |
|| || || | \++--+-+++++-++--++-+--+-+-+++----++---++-++++-----+-+-+----+-+--+----++-++--++++-++++---+---+--+++--+/ ||| ||| | |||| || | |||| |
|| || || | || | ||||| || || | | | ||| || || |||| | | | | | | || || |||| |||| | | ||| | ||| ||| | |||| || | |||| |
|| ||/++-+--++--+-+++++-++--++-+--+-+-+++----++---++-++++-----+-+-+----+-+--+----++-++\ |||| |||| | | ||| | ||| ||| | |||| || | |||| |
|| \++++-+--++--+-+++++-++--+/ | | | ||| /--++---++-++++-----+-+-+----+-+--+----++-+++-++++-++++>--+---+--+++--+---+++---+++--+\|||| || | |||| |
|| |||| | || | ||||| |\--+--+--+-+-+++-+--++---++-++++-----+-+-+----+-+--+----++-+++-++++-++++---+---+--+++--+---+++---+++--++++/| || | |||| |
|| |||| | || | ||||| | | | | | ||\-+--++---++-++++-----/ | | |/+--+----++-+++\|||| |||| |/--+--+++--+---+++---+++--++++-+--++-+-\|||| |
|| |||| | || | ||||| | | | | | || |/-++---++-++++-------+-+----+++--+----++-++++++++-++++---++--+--+++--+---+++---+++--++++-+--++\| ||||| |
|| |||| | || | ||||| | /-+--+--+-+-++--++-++---++-++++-------+-+----+++--+----++-++++++++-++++---++--+--+++\ | ||| ||| |||| | |||| ||||| |
|\---++++-+--++--+-+++++-+-+-+--+--+-+-++--++-++---++-++++-------+-+----+++--+----++-+++++++/ |||| || | |||| | ||| ||| |||| | |||| ||||| |
| |||\-+--++--+-+++++-+-+-+--+--+-+-++--++-++---++-++++-------+-+----+++--+----++-++++/|| |||| || | |||| | ||| ||| |||| | |||| ||||| |
| |||/-+--++--+-+++++-+-+-+--+--+-+-++--++-++---++-++++-------+-+-\ ||| | || |||| || |||| || | |||| | ||| ||| |||| | |||| ||||| |
| |||| | || | ||||| | | | | | | || || || |\-++++-------+-+-+--+++--+----++-++++-++--+++/ || | |||| | ||| ||| \+++-+--+++/ ||||| |
| |||| | || | ||||| | | |/-+--+-+-++--++-++---+--++++-------+-+-+--+++--+----++-++++-++--+++----++--+--++++\| ||| ||| ||| | ||| ||||| |
| |||| | || | ||||| | | || | | | || || || /-+--++++-------+-+-+--+++--+--\ || |||| || ||| || | |||||| ||| ||| ||| | ||| ||||| |
| |||| | || | ||||| \-+-++-+--+-+-++--++-++-+-+--++++-------+-+-+--/|| | | || |||| || ||| || | |||||| ||| ||| ||| | ||| ||||| |
| |||| | || | ||||| | || | | | || || || | | |||| /-----+-+-+---++--+--+-++-++++-++--+++----++--+--++++++---+++---+++\ ||| | ||| ||||| |
| |||| | \+--+-+++++---+-++-+--+-+-++--++-++-+-+--++++-+-----+-/ | || | | || |||| || ||| /++--+--++++++\ ||| |||| /+++-+--+++\ ||||| |
| |||| \---+--+-+++++---+-++-+--+-+-++--++-+/ v | |\++-+-----+---+---++--+--+-++-++++-++--+++---+++--+--/|||||| ||| |||| |||| | |||| ||||| |
| |||| /--+--+-+++++--\|/++-+--+-+-++--++-+--+-+--+-++-+-----+---+---++--+--+-++-++++-++--+++---+++--+\ |||||| ||| |||| |||| | |||| ||||| |
| |||| | | /+-+++++--+++++-+--+-+-++--++-+--+-+--+-++-+-----+---+---++-\| | || |||| || ||| |||/-++--++++++-\||| |||| |||| | |||| ||||| |
| |||| | | || ||||| ||||| | | | || || | | | | || |/----+---+---++-++--+-++-++++-++--+++---++++-++--++++++-++++\ |||| |||| | |||| ||||| |
\----++++--+--+-++-+++++--+++++-+--+-+-++--++-+--+-+--+-++-++----+---+---++-++--+-++-/||| || ||| |||| || |||||| ||||| |||| |||| | |||| ||||| |
/++++--+--+-++-+++++\ |||\+-+--+-+-++--++-+--+-+--+-++-++----+---+---++-++--+-++--+++-++--+++---++++-++--++++++-+++/| |||| |||| | |||| ||||| |
|||||/-+--+-++-++++++-+++-+-+-\| \-++--++-+--+-+--+-++-++----+---+---++-++--+-++--+++-++--+++---++++-++--++++++-+++-+--++++-++++-+--++++-+++/| |
|||||| | | || |||||| |\+-+<+-++---++--++-+--+-+--+-++-++----+---+---++-++--+-++--+++-++--+++---++++-++--++/||| ||| | |||| |||| | |||| ||| | |
|||||| | | || |||||| | | \-+-++---++--++-+--+-+--+-++-++----+---+---++-++--+-++--+++-++--+++---++++-++--++-/|| ||| | |||| |||| | |||| ||| | |
|||||| | | || |||||| | | | || || || | \-+--+-++-++----+---+---++-++--/ || ||| || ||| |||| || || || ||| | |||| |||| | |||| ||| | |
|||||| | | || |||\++-+-+---+-++---++--++-+----+--+-++-++----+---+---++-++----++--+++-++--+++---++++-++--++--++-+++-+--++++-++/| | |||| ||| | |
|||||| | |/++-+++-++-+-+---+-++---++--++-+----+--+-++-++----+---+---++-++----++--+++-++\ ||| |||| || || || ||| | |||| || | | |||| ||| | |
|||||| | |||| ||| || | | | |\---++--++-+----+--+-++-++----+---+---++-++----++--+++-+/| ||| |||| || || || ||| | |||| || | | |||| ||| | |
|||||| | |||| ||| || | | | | || \+-+----+--+-++-++----+---+---++-++----++--+++-+-+-+++---++++-++--++--++-+++-+--++++-+/ | | |||| ||| | |
|||||| | |||| ||| || | | | | ||/--+-+----+--+-++-++---\| | || || || ||| | | ||| |||| || || || ||| | |||| | | | |||| ||| | |
|||||| | |||| ||| || | | | | ||| | | | | || || || | || || || ||| | | ||\---++++-++--++--++-+++-+--++++-+--+-+--/||| ||| | |
|||||| | |||| ||| |^ | | | | ||| | | | | || || || | || || || |||/+-+-++----++++-++--++--++-+++\| |||| | | | ||| ||| | |
/-++++++-+--++++\||| || | | | | ||| | | | | || || || | \+-++----++-<++/|| | || |||| || || || ||||| |||| | | | ||| ||| | |
| |||||| \--++++++++-++-/ | | | ||| /+-+----+--+-++-++---++---+---\| || /--++--++-++-+-++----++++-++--++--++-+++++--++++-+--+\| ||| ||| | |
| |||||| |||||||| || | | | ||| || | | | || || || | || || | || || || | \+----++++-++--++--++-++/|| |||| | ||| ||| ||| | |
| |||||| |||||||| || | | | /--+++-++-+----+--+-++-++---++---+\ || || | \+--++-++-+--+----++++-++--++--++-++-++--++++-+--+++---+++-+/| | |
| |||||| |||||||| || | | | | ||| || | | /+-++-++---++---++--++-++-+---+--++-++-+--+----++++-++--++--++-++-++--++++-+--+++---+++-+-+-+-+\
| |||||| |||||||| || | | | | ||| || | | || \+-++---++---++--++-++-+---+--++-++-+--+----++++-++--++--++-++-++--++++-+--+++---+++-+-/ | ||
| |||||| \+++++++-++---+---+-+-+--+++-++-+----+-++--+-++---++---++--++-++-+---+--++-++-+--+----+/|| || || || || || |||| | ||| ||| | | ||
| |||||| ||||||| || | | | | ||| || | | || | || || || || || | | || || | | | || || || || || || |||| | ||| ||| | | ||
| |||||| ||||||| || | | | | \++-++-+----+-++--+-++---++---++--++-++-+---+--++-++-+--+----+-++-++--++--++-++-++--++++-+--+++---+++-+---/ ||
| \+++++-----+++++++-+/ | \-+-+---++-++-/ | || | || || || || || | \--++-++-+--+----+-++-++--++--/| || || |\++-+--++/ ||| | ||
| |v|||/----+++++++-+----+--\ | | || || \-++--+-++---++---++--++-++-+------++-++-+--+----+-++-++--++---+-++-++--+-++-+--/| ||| | ||
| |||||| \++++++-+----+--+--+-+---++-++--------++--+-++---++---++--++-++-+------++-++-/ | | || || || | || || | || | | ||| | ||
| |||||| /-++++++-+----+--+--+-+---++-++--------++--+-++---++---++--++-++-+------++-++----+----+-++\|| || | || || | || | | ||| | ||
| |||||| | |||||| | | | | | \+-++--------++--+-++---++---++--++-++-+------++-++----+----+-+++++--+/ | || || | || | | ||| | ||
| |||||| | |||||| \----+--+--+-+----+-++--------++--+-++---++---++--++-++-+------++-++----+----+-+++++--+----+-+/ || | || | | ||| | ||
\--++++++---+-++/||| | | | | | || || | || || || || || | /--++-++----+----+-+++++--+----+-+--++\ | || | | ||| | ||
|||||| | || ||| | | | | | \+--------++--+-++---++---++--/| || | | || || | | ||||| | | | ||| | || | | ||| | ||
\+++++---+-++-+++------+--+--+-+----+--+--------++--+-++---++---++---+-+/ \---+--++-++----+----+-+++++--+----+-+--+++-+-++-+---/ ||| | ||
||||| /-+-++-+++------+--+--+-+----+--+--------++--+-++---++---++---+-+------+--++-++----+----+-+++++--+\ | | ||| \-++-+--------/|| | ||
||||| | | || ||| \--+--+-+----+--+--------++--+-++---++---++---+-+->----+--++-++----+----+-++++/ || | | ||| || | || | ||
||||| | | || ||| | | | | | |\--+-++---+/ ||/--+-+------+--++-++----+---\| |\++---++---+-/ ||| || | || | ||
/----+++++-+-+-++\||| | | | \--+--------+---+-++---/ ||| | | | || || | || | |\---++---+----+++---++-+---------++-+-----/|
| ||||| | | |||||| | | | | | | || ||| | | | || || | || | | || | ||| || | || | |
| ||||| | | |\++++---------+--+-+-------+--------+---/ || ||| | | | || || | || | | || | ||| || | || | |
| ||||| | | | |||| | | | | | || /----+++--+-+------+--++-++----+---++-+-+----++---+----+++---++-+------\ || | |
| ||||| | | | |||| | | | | \-----++---+----+++--+-+------+--++-++----+---++-+-+----++---+----+++---++-+------+--++-+------/
| ||||| | | | |||| | | \-------+--------------++---+----+/| | | | || || | || | | || | ||| /-++-+------+-\|| |
| ||||| | | | |||| | | | \+---+----+-+--+-+------+--++-++----+---++-+-+----++---+----+++-+-+/ | | ||| |
| ||||| | \-+-++++---------+--+---------+---------------+---+----+-+--+-+------+--++-++----+---++-+-/ || | ||| | | | | ||| |
| ||||| | | |||\---------+--+---------+---------------+---+----+-+--+-+------+--++-++----+---++-+------/| | ||| | | | | ||| |
|/---+++++-+---+-+++-\ | | /---+---------------+---+----+-+--+-+------+--++-++----+---++-+-------+---+----+++-+-+--+------+\||| |
\+---+++++-+---+-/|| | | | | | |/--+----+-+--+-+------+--++-++----+---++-+-----\ | | ||| | | | ||||| |
| \++++-+---+--++-+--------+--+-----+---+---------------++--+----+-+--+-+------+--+/ || | || | | | | ||| | | \------++++/ |
| |||| | | || | | | | | || | | | \-+------+--+--++----/ || | | | | ||| | | |||| |
| |||| | | || | | | v | || | | | | | | \+--------++-+-----+-+---+----/|| | | |||| |
| ||\+-+---+--++-+--------+--/ | | || | | | | | | | |\-+-----+-+---/ || | | |||| |
| || | | | \+-+--------+--------+---+---------------++--+----+-+----+------+--+---+--------+--+----<+-+---------++-+-/ |||| |
| || | | | \-+--------+--------+---+---------------++--+----+-+----+------+--/ | | | | | || \-----------++/| |
| \+-+-+---+-----+--------+--------+---+---------------++--+----+-+----+------+------/ | | | | || || | |
| | | | | | | \---+---------------++--+----+-+----+------+---------------+--+-----+-+---------++-------------+/ | |
| | | | | | | | || | | | | | | | | | || | | |
| | | \---+-----+--------+------------+---------------++--+----+-+----+------+---------------+--+-----+-/ || | | |
| | | \-----+--------+------------+---------------++--+----+-+----/ | | | | || | | |
| | | | | | /----------++--+----+-+-----------+---------------+--+-----+-----------++----\ | | |
| | | | | | | \+--+----+-+-----------+---------------+--+-----+-----------/| | | | |
\-----+-+-----------/ | | | \--+----+-+-----------+---------------+--+-----/ | | | | |
| | | \----+--------------+----+-+-----------+---------------+--+------------------+----+--------+--/ |
| | | | | | | \---------------+--+------------------/ | | |
\-+--------------------+-----------------+--------------+----/ | | | | | |
| | | | \---------------------------/ | | | |
\--------------------/ | \-------------------------------------+-----------------------+--------/ |
| \-----------------------+--------------/
\----------------------------------------------------------------------------/

View File

@ -0,0 +1,27 @@
<Query Kind="Statements" />
var input = int.Parse(File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"14_input.txt")));
var board = new List<int>(input);
board.Add(3);
board.Add(7);
int elf1 = 0;
int elf2 = 1;
for(int gen=0;;gen++)
{
var sum1=board[elf1]+board[elf2];
if (sum1>=10)board.Add(1); board.Add(sum1%10);
elf1 = (elf1 + 1 + board[elf1]) % board.Count;
elf2 = (elf2 + 1 + board[elf2]) % board.Count;
//board.Select((p, i) => (i == elf1) ? $"({p})" : ((i == elf2) ? $"[{p}]" : $" {p} ")).Aggregate((a,b)=>a+" "+b).Dump();
if (board.Count >= input+10)
{
board.Skip(input).Take(10).Select(p=>p.ToString()).Aggregate((a,b)=>a+b).Dump();
return;
}
}

View File

@ -0,0 +1,33 @@
<Query Kind="Statements" />
var input = File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"14_input.txt")).Select(c => c-'0').Reverse().ToArray();
var board = new List<short>();
board.Add(3);
board.Add(7);
int elf1 = 0;
int elf2 = 1;
for (int gen = 0; ; gen++)
{
var sum1 = board[elf1] + board[elf2];
if (sum1 >= 10) board.Add(1); board.Add((short)(sum1 % 10));
elf1 = (elf1 + 1 + board[elf1]) % board.Count;
elf2 = (elf2 + 1 + board[elf2]) % board.Count;
//board.Select((p, i) => (i == elf1) ? $"({p})" : ((i == elf2) ? $"[{p}]" : $" {p} ")).Aggregate((a,b)=>a+" "+b).Dump();
if (board.Count >= input.Length && input.Select((p,i) => board[board.Count-1-i]==p ).All(p=>p))
{
(board.Count-input.Length).Dump();
return;
}
if (board.Count >= input.Length && sum1 >= 10 && input.Select((p, i) => board[board.Count - 2 - i] == p).All(p => p))
{
(board.Count - input.Length - 1).Dump();
return;
}
}

View File

@ -0,0 +1,52 @@
--- Day 14: Chocolate Charts ---
You finally have a chance to look at all of the produce moving around. Chocolate, cinnamon, mint, chili peppers, nutmeg, vanilla... the Elves must be growing these plants to make hot chocolate! As you realize this, you hear a conversation in the distance. When you go to investigate, you discover two Elves in what appears to be a makeshift underground kitchen/laboratory.
The Elves are trying to come up with the ultimate hot chocolate recipe; they're even maintaining a scoreboard which tracks the quality score (0-9) of each recipe.
Only two recipes are on the board: the first recipe got a score of 3, the second, 7. Each of the two Elves has a current recipe: the first Elf starts with the first recipe, and the second Elf starts with the second recipe.
To create new recipes, the two Elves combine their current recipes. This creates new recipes from the digits of the sum of the current recipes' scores. With the current recipes' scores of 3 and 7, their sum is 10, and so two new recipes would be created: the first with score 1 and the second with score 0. If the current recipes' scores were 2 and 3, the sum, 5, would only create one recipe (with a score of 5) with its single digit.
The new recipes are added to the end of the scoreboard in the order they are created. So, after the first round, the scoreboard is 3, 7, 1, 0.
After all new recipes are added to the scoreboard, each Elf picks a new current recipe. To do this, the Elf steps forward through the scoreboard a number of recipes equal to 1 plus the score of their current recipe. So, after the first round, the first Elf moves forward 1 + 3 = 4 times, while the second Elf moves forward 1 + 7 = 8 times. If they run out of recipes, they loop back around to the beginning. After the first round, both Elves happen to loop around until they land on the same recipe that they had in the beginning; in general, they will move to different recipes.
Drawing the first Elf as parentheses and the second Elf as square brackets, they continue this process:
(3)[7]
(3)[7] 1 0
3 7 1 [0](1) 0
3 7 1 0 [1] 0 (1)
(3) 7 1 0 1 0 [1] 2
3 7 1 0 (1) 0 1 2 [4]
3 7 1 [0] 1 0 (1) 2 4 5
3 7 1 0 [1] 0 1 2 (4) 5 1
3 (7) 1 0 1 0 [1] 2 4 5 1 5
3 7 1 0 1 0 1 2 [4](5) 1 5 8
3 (7) 1 0 1 0 1 2 4 5 1 5 8 [9]
3 7 1 0 1 0 1 [2] 4 (5) 1 5 8 9 1 6
3 7 1 0 1 0 1 2 4 5 [1] 5 8 9 1 (6) 7
3 7 1 0 (1) 0 1 2 4 5 1 5 [8] 9 1 6 7 7
3 7 [1] 0 1 0 (1) 2 4 5 1 5 8 9 1 6 7 7 9
3 7 1 0 [1] 0 1 2 (4) 5 1 5 8 9 1 6 7 7 9 2
The Elves think their skill will improve after making a few recipes (your puzzle input). However, that could take ages; you can speed this up considerably by identifying the scores of the ten recipes after that. For example:
If the Elves think their skill will improve after making 9 recipes, the scores of the ten recipes after the first nine on the scoreboard would be 5158916779 (highlighted in the last line of the diagram).
After 5 recipes, the scores of the next ten would be 0124515891.
After 18 recipes, the scores of the next ten would be 9251071085.
After 2018 recipes, the scores of the next ten would be 5941429882.
What are the scores of the ten recipes immediately after the number of recipes in your puzzle input?
--- Part Two ---
As it turns out, you got the Elves' plan backwards. They actually want to know how many recipes appear on the scoreboard to the left of the first recipes whose scores are the digits from your puzzle input.
51589 first appears after 9 recipes.
01245 first appears after 5 recipes.
92510 first appears after 18 recipes.
59414 first appears after 2018 recipes.
How many recipes appear on the scoreboard to the left of the score sequence in your puzzle input?

View File

@ -0,0 +1 @@
260321

View File

@ -0,0 +1,277 @@
<Query Kind="Program" />
enum Fraction { Wall, Elf, Goblin }
class Entity
{
public Fraction Frac;
public int AttackPower;
public int HitPoints;
public int X, Y;
public bool Alive;
public override string ToString() => $"{Frac}[{AttackPower};{HitPoints}]";
}
int Width = 0;
int Height = 0;
Entity[,] Map = null;
List<Entity> Units = null;
readonly bool DUMP_REACHABLE = false;
readonly bool DUMP_PATHFINDING = false;
readonly bool DUMP_MAP = false;
void Main()
{
Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"15_input.txt")));
for (int gen = 0; ; gen++)
{
if (DUMP_MAP) DumpMap(gen);
//if (gen==60)Util.Break();
foreach (var u in Units.OrderBy(p=>p.Y).ThenBy(p=>p.X).ToList())
{
if (!u.Alive) continue;
var success = Tick(u);
if (!success && (Units.Count(q => q.Frac == Fraction.Elf) == 0 || Units.Count(q => q.Frac == Fraction.Goblin) == 0))
{
if (DUMP_MAP) DumpMap(gen+1);
var winner = Units.Where(q => q.Frac != Fraction.Wall).Select(p => p.Frac).Distinct().Single();
var count = Units.Count(q => q.Frac != Fraction.Wall);
var hpsum = Units.Where(q => q.Frac != Fraction.Wall).Sum(q => q.HitPoints);
$"Finished after {gen} rounds with {count} Units ({winner}) and {hpsum} Total HP. The score is [ {hpsum * gen} ] ".Dump();
return;
}
}
}
}
bool Tick(Entity e)
{
var enemyFraction = e.Frac==Fraction.Elf ? Fraction.Goblin : Fraction.Elf;
// [1] Fast Attack
{
Entity target = null;
if (e.Y > 0 && Map[e.X, e.Y - 1] != null && Map[e.X, e.Y - 1].Frac == enemyFraction && (target == null || Map[e.X, e.Y - 1].HitPoints < target.HitPoints)) target = Map[e.X, e.Y - 1];
if (e.X > 0 && Map[e.X - 1, e.Y] != null && Map[e.X - 1, e.Y].Frac == enemyFraction && (target == null || Map[e.X - 1, e.Y].HitPoints < target.HitPoints)) target = Map[e.X - 1, e.Y];
if (e.X < Width - 1 && Map[e.X + 1, e.Y] != null && Map[e.X + 1, e.Y].Frac == enemyFraction && (target == null || Map[e.X + 1, e.Y].HitPoints < target.HitPoints)) target = Map[e.X + 1, e.Y];
if (e.Y < Height - 1 && Map[e.X, e.Y + 1] != null && Map[e.X, e.Y + 1].Frac == enemyFraction && (target == null || Map[e.X, e.Y + 1].HitPoints < target.HitPoints)) target = Map[e.X, e.Y + 1];
if (target != null) { Attack(e,target); return true; }
}
// [2] Path Finding
{
var targetPos = ListTargets(enemyFraction)
.Select(p => (p, GetDistance( (e.X, e.Y), (p.x, p.y) ) ) )
.Where(p => p.Item2!=null)
.OrderBy(p => p.Item2)
.ThenBy(p=>p.p.y)
.ThenBy(p=>p.p.x)
.Select(p=>p.p)
.FirstOrDefault();
if (targetPos==default) { return false; }
int[,] matrix = DoPathFinding(targetPos.x, targetPos.y);
if (DUMP_PATHFINDING) DumpPathFinding(matrix, e, (targetPos.x, targetPos.y));
Tuple<int, int, int> targetStep = null;
if (e.Y > 0 && matrix[e.X, e.Y - 1] >= 0 && matrix[e.X, e.Y - 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y - 1])) targetStep = Tuple.Create(e.X, e.Y - 1, matrix[e.X, e.Y - 1]);
if (e.X > 0 && matrix[e.X - 1, e.Y] >= 0 && matrix[e.X - 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X - 1, e.Y])) targetStep = Tuple.Create(e.X - 1, e.Y, matrix[e.X - 1, e.Y]);
if (e.X < Width - 1 && matrix[e.X + 1, e.Y] >= 0 && matrix[e.X + 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X + 1, e.Y])) targetStep = Tuple.Create(e.X + 1, e.Y, matrix[e.X + 1, e.Y]);
if (e.Y < Height - 1 && matrix[e.X, e.Y + 1] >= 0 && matrix[e.X, e.Y + 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y + 1])) targetStep = Tuple.Create(e.X, e.Y + 1, matrix[e.X, e.Y + 1]);
//if (e.X > 0 && matrix[e.X - 1, e.Y] >= 0 && matrix[e.X - 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X - 1, e.Y])) targetStep = Tuple.Create(e.X - 1, e.Y, matrix[e.X - 1, e.Y]);
//if (e.Y > 0 && matrix[e.X, e.Y - 1] >= 0 && matrix[e.X, e.Y - 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y - 1])) targetStep = Tuple.Create(e.X, e.Y - 1, matrix[e.X, e.Y - 1]);
//if (e.Y < Height - 1 && matrix[e.X, e.Y + 1] >= 0 && matrix[e.X, e.Y + 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y + 1])) targetStep = Tuple.Create(e.X, e.Y + 1, matrix[e.X, e.Y + 1]);
//if (e.X < Width - 1 && matrix[e.X + 1, e.Y] >= 0 && matrix[e.X + 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X + 1, e.Y])) targetStep = Tuple.Create(e.X + 1, e.Y, matrix[e.X + 1, e.Y]);
if (targetStep == null) { return false; }
Move(e, targetStep.Item1, targetStep.Item2);
// [3] Normal Attack
if (targetStep.Item3 == 0)
{
Entity att = null;
if (e.Y > 0 && Map[e.X, e.Y - 1] != null && Map[e.X, e.Y - 1].Frac==enemyFraction && (att == null || att.HitPoints > Map[e.X, e.Y - 1].HitPoints)) att = Map[e.X, e.Y - 1];
if (e.X > 0 && Map[e.X - 1, e.Y] != null && Map[e.X - 1, e.Y].Frac==enemyFraction && (att == null || att.HitPoints > Map[e.X - 1, e.Y].HitPoints)) att = Map[e.X - 1, e.Y];
if (e.X < Width - 1 && Map[e.X + 1, e.Y] != null && Map[e.X + 1, e.Y].Frac == enemyFraction && (att == null || att.HitPoints > Map[e.X + 1, e.Y].HitPoints)) att = Map[e.X + 1, e.Y];
if (e.X < Height - 1 && Map[e.X, e.Y + 1] != null && Map[e.X, e.Y + 1].Frac == enemyFraction && (att == null || att.HitPoints > Map[e.X, e.Y + 1].HitPoints)) att = Map[e.X, e.Y + 1];
Attack(e, att);
return true;
}
return true;
}
}
int? GetDistance((int x, int y) p1, (int x, int y) p2)
{
int[,] dmap = new int[Width, Height];
var workload = new Stack<(int, int)>(); // <x,y>
workload.Push((p1.x, p1.y));
for (int yy = 0; yy < Height; yy++) for (int xx = 0; xx < Width; xx++) dmap[xx, yy] = (Map[xx, yy] != null) ? -1 : int.MaxValue;
dmap[p1.x, p1.y] = 0;
dmap[p2.x, p2.y] = int.MaxValue;
while (workload.Any())
{
(var x, var y) = workload.Pop();
if (y > 0 && dmap[x, y - 1] - 1 > dmap[x, y]) { dmap[x, y - 1] = dmap[x, y] + 1; workload.Push((x, y - 1)); } // [N]
if (x < Width - 1 && dmap[x + 1, y ] - 1 > dmap[x, y]) { dmap[x + 1, y] = dmap[x, y] + 1; workload.Push((x + 1, y )); } // [E]
if (x > 0 && dmap[x - 1, y ] - 1 > dmap[x, y]) { dmap[x - 1, y] = dmap[x, y] + 1; workload.Push((x - 1, y )); } // [W]
if (y < Height - 1 && dmap[x, y + 1] - 1 > dmap[x, y]) { dmap[x, y + 1] = dmap[x, y] + 1; workload.Push((x, y + 1)); } // [S]
}
if (DUMP_REACHABLE) DumpReachable(dmap, p1, p2);
return dmap[p2.x, p2.y]==int.MaxValue ? (int?)null : dmap[p2.x, p2.y];
}
void Move(Entity e, int x, int y)
{
Map[e.X, e.Y] = null;
e.X = x;
e.Y = y;
Map[e.X, e.Y] = e;
}
IEnumerable<(int x,int y, Entity e)> ListTargets(Fraction destFrac)
{
foreach (var u in Units.Where(q => q.Frac==destFrac))
{
if (u.Y > 0 && Map[u.X, u.Y - 1] == null) yield return (u.X, u.Y - 1, u); // [N]
if (u.X < Width - 1 && Map[u.X + 1, u.Y ] == null) yield return (u.X + 1, u.Y, u); // [E]
if (u.X > 0 && Map[u.X - 1, u.Y ] == null) yield return (u.X - 1, u.Y, u); // [W]
if (u.Y < Height - 1 && Map[u.X, u.Y + 1] == null) yield return (u.X, u.Y + 1, u); // [S]
}
}
int[,] DoPathFinding(int dx, int dy)
{
int[,] dmap = new int[Width, Height];
var workload = new Stack<(int, int)>(); // <x,y>
workload.Push((dx, dy));
for (int yy = 0; yy < Height; yy++) for (int xx = 0; xx < Width; xx++) dmap[xx,yy] = (Map[xx,yy]!=null) ? -1 : int.MaxValue;
dmap[dx,dy]=0;
while (workload.Any())
{
(var x, var y) = workload.Pop();
if (y > 0 && dmap[x, y - 1] - 1 > dmap[x, y]) { dmap[x, y - 1] = dmap[x, y] + 1; workload.Push((x, y - 1)); } // [N]
if (x < Width - 1 && dmap[x + 1, y] - 1 > dmap[x, y]) { dmap[x + 1, y] = dmap[x, y] + 1; workload.Push((x + 1, y)); } // [E]
if (x > 0 && dmap[x - 1, y] - 1 > dmap[x, y]) { dmap[x - 1, y] = dmap[x, y] + 1; workload.Push((x - 1, y)); } // [W]
if (y < Height - 1 && dmap[x, y + 1] - 1 > dmap[x, y]) { dmap[x, y + 1] = dmap[x, y] + 1; workload.Push((x, y + 1)); } // [S]
}
return dmap;
}
void Attack(Entity src, Entity dst)
{
dst.HitPoints -= src.AttackPower;
if (dst.HitPoints <= 0) { dst.Alive = false; Units.Remove(dst); Map[dst.X, dst.Y] = null; }
}
void DumpPathFinding(int[,] dmap, Entity src, (int X, int Y) dst)
{
StringBuilder b = new StringBuilder();
for (int yy = 0; yy < Height; yy++)
{
b.Append(" ");
for (int xx = 0; xx < Width; xx++)
{
if (xx == src.X && yy == src.Y) b.Append('+');
else if (xx == dst.X && yy == dst.Y) b.Append('O');
else if (dmap[xx, yy] == int.MaxValue) b.Append(' ');
else if (dmap[xx, yy] < 0) b.Append('#');
else if (dmap[xx, yy] <= 9) b.Append(dmap[xx, yy]);
else if (dmap[xx, yy] < 36) b.Append((char)('A' + (dmap[xx, yy] - 10)));
else b.Append('$');
}
b.AppendLine();
}
b.ToString().Dump();
}
void DumpReachable(int[,] rmap, (int X, int Y) src, (int X, int Y) dst)
{
StringBuilder b = new StringBuilder();
for (int yy = 0; yy < Height; yy++)
{
b.Append(": ");
for (int xx = 0; xx < Width; xx++)
{
if (xx == src.X && yy == src.Y) b.Append('+');
else if (xx == dst.X && yy == dst.Y) b.Append('O');
else if (Map[xx,yy]?.Frac==Fraction.Wall) b.Append('#');
else if (rmap[xx, yy] < int.MaxValue) b.Append(' ');
else if (rmap[xx, yy] == int.MaxValue) b.Append('.');
else b.Append('$');
}
b.AppendLine();
}
b.ToString().Dump();
}
void DumpMap(int gen)
{
StringBuilder b = new StringBuilder();
for (int yy = 0; yy < Height; yy++)
{
List<string> extra = new List<string>();
for (int xx = 0; xx < Width; xx++)
{
if (Map[xx, yy] == null) { b.Append('.'); }
else if (Map[xx, yy].Frac == Fraction.Wall) { b.Append('#'); }
else if (Map[xx, yy].Frac == Fraction.Elf) { b.Append('E'); extra.Add($"E({Map[xx, yy].HitPoints})"); }
else if (Map[xx, yy].Frac == Fraction.Goblin) { b.Append('G'); extra.Add($"G({Map[xx, yy].HitPoints})"); }
else throw new Exception($"[{xx}|{yy}] := {Map[xx,yy]}");
}
b.Append($" {string.Join(", ", extra)}{(extra.Any()?", ":"")}");
b.AppendLine();
}
$"After {gen} rounds:".Dump();
b.ToString().Trim().Dump();
$"{new string(' ', Width)} HP[G] := {Units.Where(p => p.Frac == Fraction.Goblin).Sum(p => p.HitPoints)}".Dump();
$"{new string(' ', Width)} HP[E] := {Units.Where(p => p.Frac == Fraction.Elf).Sum(p => p.HitPoints)}".Dump();
"".Dump();
"".Dump();
"".Dump();
}
void Load(string[] input)
{
Width = input[0].Length;
Height = input.Length;
Map = new UserQuery.Entity[Width, Height];
Units = new List<Entity>();
for (int yy = 0; yy < Height; yy++)
{
for (int xx = 0; xx < Width; xx++)
{
if (input[yy][xx] == '#')
Map[xx, yy] = new Entity { Frac = Fraction.Wall, AttackPower = 0, HitPoints = int.MaxValue, X=xx, Y=yy, Alive=true };
else if (input[yy][xx] == '.')
Map[xx, yy] = null;
else if (input[yy][xx] == 'E')
Units.Add(Map[xx, yy] = new Entity { Frac = Fraction.Elf, AttackPower = 3, HitPoints = 200, X=xx, Y=yy, Alive=true });
else if (input[yy][xx] == 'G')
Units.Add(Map[xx, yy] = new Entity { Frac = Fraction.Goblin, AttackPower = 3, HitPoints = 200, X=xx, Y=yy, Alive=true });
else
throw new Exception($"[{xx}|{yy}] := {input[xx][yy]}");
}
}
}

View File

@ -0,0 +1,287 @@
<Query Kind="Program" />
enum Fraction { Wall, Elf, Goblin }
class Entity
{
public Fraction Frac;
public int AttackPower;
public int HitPoints;
public int X, Y;
public bool Alive;
public override string ToString() => $"{Frac}[{AttackPower};{HitPoints}]";
}
int Width = 0;
int Height = 0;
Entity[,] Map = null;
List<Entity> Units = null;
readonly bool DUMP_REACHABLE = false;
readonly bool DUMP_PATHFINDING = false;
readonly bool DUMP_MAP = false;
void Main()
{
var input = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"15_input.txt"));
for (int pwr = 3; ; pwr++)
{
Load(input, pwr);
try
{
for (int gen = 0; ; gen++)
{
if (DUMP_MAP) DumpMap(gen);
//if (gen==60)Util.Break();
foreach (var u in Units.OrderBy(p => p.Y).ThenBy(p => p.X).ToList())
{
if (!u.Alive) continue;
var success = Tick(u);
if (!success && (Units.Count(q => q.Frac == Fraction.Elf) == 0 || Units.Count(q => q.Frac == Fraction.Goblin) == 0))
{
if (DUMP_MAP) DumpMap(gen + 1);
var winner = Units.Where(q => q.Frac != Fraction.Wall).Select(p => p.Frac).Distinct().Single();
var count = Units.Count(q => q.Frac != Fraction.Wall);
var hpsum = Units.Where(q => q.Frac != Fraction.Wall).Sum(q => q.HitPoints);
$"Finished after {gen} rounds with {count} Units ({winner}), {hpsum} total HP and {pwr} Elf-power. The score is [ {hpsum * gen} ] ".Dump();
return;
}
}
}
}
catch (Exception) {}
}
}
bool Tick(Entity e)
{
var enemyFraction = e.Frac == Fraction.Elf ? Fraction.Goblin : Fraction.Elf;
// [1] Fast Attack
{
Entity target = null;
if (e.Y > 0 && Map[e.X, e.Y - 1] != null && Map[e.X, e.Y - 1].Frac == enemyFraction && (target == null || Map[e.X, e.Y - 1].HitPoints < target.HitPoints)) target = Map[e.X, e.Y - 1];
if (e.X > 0 && Map[e.X - 1, e.Y] != null && Map[e.X - 1, e.Y].Frac == enemyFraction && (target == null || Map[e.X - 1, e.Y].HitPoints < target.HitPoints)) target = Map[e.X - 1, e.Y];
if (e.X < Width - 1 && Map[e.X + 1, e.Y] != null && Map[e.X + 1, e.Y].Frac == enemyFraction && (target == null || Map[e.X + 1, e.Y].HitPoints < target.HitPoints)) target = Map[e.X + 1, e.Y];
if (e.Y < Height - 1 && Map[e.X, e.Y + 1] != null && Map[e.X, e.Y + 1].Frac == enemyFraction && (target == null || Map[e.X, e.Y + 1].HitPoints < target.HitPoints)) target = Map[e.X, e.Y + 1];
if (target != null) { Attack(e, target); return true; }
}
// [2] Path Finding
{
var targetPos = ListTargets(enemyFraction)
.Select(p => (p, GetDistance((e.X, e.Y), (p.x, p.y))))
.Where(p => p.Item2 != null)
.OrderBy(p => p.Item2)
.ThenBy(p => p.p.y)
.ThenBy(p => p.p.x)
.Select(p => p.p)
.FirstOrDefault();
if (targetPos == default) { return false; }
int[,] matrix = DoPathFinding(targetPos.x, targetPos.y);
if (DUMP_PATHFINDING) DumpPathFinding(matrix, e, (targetPos.x, targetPos.y));
Tuple<int, int, int> targetStep = null;
if (e.Y > 0 && matrix[e.X, e.Y - 1] >= 0 && matrix[e.X, e.Y - 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y - 1])) targetStep = Tuple.Create(e.X, e.Y - 1, matrix[e.X, e.Y - 1]);
if (e.X > 0 && matrix[e.X - 1, e.Y] >= 0 && matrix[e.X - 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X - 1, e.Y])) targetStep = Tuple.Create(e.X - 1, e.Y, matrix[e.X - 1, e.Y]);
if (e.X < Width - 1 && matrix[e.X + 1, e.Y] >= 0 && matrix[e.X + 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X + 1, e.Y])) targetStep = Tuple.Create(e.X + 1, e.Y, matrix[e.X + 1, e.Y]);
if (e.Y < Height - 1 && matrix[e.X, e.Y + 1] >= 0 && matrix[e.X, e.Y + 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y + 1])) targetStep = Tuple.Create(e.X, e.Y + 1, matrix[e.X, e.Y + 1]);
//if (e.X > 0 && matrix[e.X - 1, e.Y] >= 0 && matrix[e.X - 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X - 1, e.Y])) targetStep = Tuple.Create(e.X - 1, e.Y, matrix[e.X - 1, e.Y]);
//if (e.Y > 0 && matrix[e.X, e.Y - 1] >= 0 && matrix[e.X, e.Y - 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y - 1])) targetStep = Tuple.Create(e.X, e.Y - 1, matrix[e.X, e.Y - 1]);
//if (e.Y < Height - 1 && matrix[e.X, e.Y + 1] >= 0 && matrix[e.X, e.Y + 1] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X, e.Y + 1])) targetStep = Tuple.Create(e.X, e.Y + 1, matrix[e.X, e.Y + 1]);
//if (e.X < Width - 1 && matrix[e.X + 1, e.Y] >= 0 && matrix[e.X + 1, e.Y] < int.MaxValue && (targetStep == null || targetStep.Item3 > matrix[e.X + 1, e.Y])) targetStep = Tuple.Create(e.X + 1, e.Y, matrix[e.X + 1, e.Y]);
if (targetStep == null) { return false; }
Move(e, targetStep.Item1, targetStep.Item2);
// [3] Normal Attack
if (targetStep.Item3 == 0)
{
Entity att = null;
if (e.Y > 0 && Map[e.X, e.Y - 1] != null && Map[e.X, e.Y - 1].Frac == enemyFraction && (att == null || att.HitPoints > Map[e.X, e.Y - 1].HitPoints)) att = Map[e.X, e.Y - 1];
if (e.X > 0 && Map[e.X - 1, e.Y] != null && Map[e.X - 1, e.Y].Frac == enemyFraction && (att == null || att.HitPoints > Map[e.X - 1, e.Y].HitPoints)) att = Map[e.X - 1, e.Y];
if (e.X < Width - 1 && Map[e.X + 1, e.Y] != null && Map[e.X + 1, e.Y].Frac == enemyFraction && (att == null || att.HitPoints > Map[e.X + 1, e.Y].HitPoints)) att = Map[e.X + 1, e.Y];
if (e.X < Height - 1 && Map[e.X, e.Y + 1] != null && Map[e.X, e.Y + 1].Frac == enemyFraction && (att == null || att.HitPoints > Map[e.X, e.Y + 1].HitPoints)) att = Map[e.X, e.Y + 1];
Attack(e, att);
return true;
}
return true;
}
}
int? GetDistance((int x, int y) p1, (int x, int y) p2)
{
int[,] dmap = new int[Width, Height];
var workload = new Stack<(int, int)>(); // <x,y>
workload.Push((p1.x, p1.y));
for (int yy = 0; yy < Height; yy++) for (int xx = 0; xx < Width; xx++) dmap[xx, yy] = (Map[xx, yy] != null) ? -1 : int.MaxValue;
dmap[p1.x, p1.y] = 0;
dmap[p2.x, p2.y] = int.MaxValue;
while (workload.Any())
{
(var x, var y) = workload.Pop();
if (y > 0 && dmap[x, y - 1] - 1 > dmap[x, y]) { dmap[x, y - 1] = dmap[x, y] + 1; workload.Push((x, y - 1)); } // [N]
if (x < Width - 1 && dmap[x + 1, y] - 1 > dmap[x, y]) { dmap[x + 1, y] = dmap[x, y] + 1; workload.Push((x + 1, y)); } // [E]
if (x > 0 && dmap[x - 1, y] - 1 > dmap[x, y]) { dmap[x - 1, y] = dmap[x, y] + 1; workload.Push((x - 1, y)); } // [W]
if (y < Height - 1 && dmap[x, y + 1] - 1 > dmap[x, y]) { dmap[x, y + 1] = dmap[x, y] + 1; workload.Push((x, y + 1)); } // [S]
}
if (DUMP_REACHABLE) DumpReachable(dmap, p1, p2);
return dmap[p2.x, p2.y] == int.MaxValue ? (int?)null : dmap[p2.x, p2.y];
}
void Move(Entity e, int x, int y)
{
Map[e.X, e.Y] = null;
e.X = x;
e.Y = y;
Map[e.X, e.Y] = e;
}
IEnumerable<(int x, int y, Entity e)> ListTargets(Fraction destFrac)
{
foreach (var u in Units.Where(q => q.Frac == destFrac))
{
if (u.Y > 0 && Map[u.X, u.Y - 1] == null) yield return (u.X, u.Y - 1, u); // [N]
if (u.X < Width - 1 && Map[u.X + 1, u.Y] == null) yield return (u.X + 1, u.Y, u); // [E]
if (u.X > 0 && Map[u.X - 1, u.Y] == null) yield return (u.X - 1, u.Y, u); // [W]
if (u.Y < Height - 1 && Map[u.X, u.Y + 1] == null) yield return (u.X, u.Y + 1, u); // [S]
}
}
int[,] DoPathFinding(int dx, int dy)
{
int[,] dmap = new int[Width, Height];
var workload = new Stack<(int, int)>(); // <x,y>
workload.Push((dx, dy));
for (int yy = 0; yy < Height; yy++) for (int xx = 0; xx < Width; xx++) dmap[xx, yy] = (Map[xx, yy] != null) ? -1 : int.MaxValue;
dmap[dx, dy] = 0;
while (workload.Any())
{
(var x, var y) = workload.Pop();
if (y > 0 && dmap[x, y - 1] - 1 > dmap[x, y]) { dmap[x, y - 1] = dmap[x, y] + 1; workload.Push((x, y - 1)); } // [N]
if (x < Width - 1 && dmap[x + 1, y] - 1 > dmap[x, y]) { dmap[x + 1, y] = dmap[x, y] + 1; workload.Push((x + 1, y)); } // [E]
if (x > 0 && dmap[x - 1, y] - 1 > dmap[x, y]) { dmap[x - 1, y] = dmap[x, y] + 1; workload.Push((x - 1, y)); } // [W]
if (y < Height - 1 && dmap[x, y + 1] - 1 > dmap[x, y]) { dmap[x, y + 1] = dmap[x, y] + 1; workload.Push((x, y + 1)); } // [S]
}
return dmap;
}
void Attack(Entity src, Entity dst)
{
dst.HitPoints -= src.AttackPower;
if (dst.HitPoints <= 0) { dst.Alive = false; Units.Remove(dst); Map[dst.X, dst.Y] = null; if (dst.Frac==Fraction.Elf)throw new Exception("DEATH"); }
}
void DumpPathFinding(int[,] dmap, Entity src, (int X, int Y) dst)
{
StringBuilder b = new StringBuilder();
for (int yy = 0; yy < Height; yy++)
{
b.Append(" ");
for (int xx = 0; xx < Width; xx++)
{
if (xx == src.X && yy == src.Y) b.Append('+');
else if (xx == dst.X && yy == dst.Y) b.Append('O');
else if (dmap[xx, yy] == int.MaxValue) b.Append(' ');
else if (dmap[xx, yy] < 0) b.Append('#');
else if (dmap[xx, yy] <= 9) b.Append(dmap[xx, yy]);
else if (dmap[xx, yy] < 36) b.Append((char)('A' + (dmap[xx, yy] - 10)));
else b.Append('$');
}
b.AppendLine();
}
b.ToString().Dump();
}
void DumpReachable(int[,] rmap, (int X, int Y) src, (int X, int Y) dst)
{
StringBuilder b = new StringBuilder();
for (int yy = 0; yy < Height; yy++)
{
b.Append(": ");
for (int xx = 0; xx < Width; xx++)
{
if (xx == src.X && yy == src.Y) b.Append('+');
else if (xx == dst.X && yy == dst.Y) b.Append('O');
else if (Map[xx, yy]?.Frac == Fraction.Wall) b.Append('#');
else if (rmap[xx, yy] < int.MaxValue) b.Append(' ');
else if (rmap[xx, yy] == int.MaxValue) b.Append('.');
else b.Append('$');
}
b.AppendLine();
}
b.ToString().Dump();
}
void DumpMap(int gen)
{
StringBuilder b = new StringBuilder();
for (int yy = 0; yy < Height; yy++)
{
List<string> extra = new List<string>();
for (int xx = 0; xx < Width; xx++)
{
if (Map[xx, yy] == null) { b.Append('.'); }
else if (Map[xx, yy].Frac == Fraction.Wall) { b.Append('#'); }
else if (Map[xx, yy].Frac == Fraction.Elf) { b.Append('E'); extra.Add($"E({Map[xx, yy].HitPoints})"); }
else if (Map[xx, yy].Frac == Fraction.Goblin) { b.Append('G'); extra.Add($"G({Map[xx, yy].HitPoints})"); }
else throw new Exception($"[{xx}|{yy}] := {Map[xx, yy]}");
}
b.Append($" {string.Join(", ", extra)}{(extra.Any() ? ", " : "")}");
b.AppendLine();
}
$"After {gen} rounds:".Dump();
b.ToString().Trim().Dump();
$"{new string(' ', Width)} HP[G] := {Units.Where(p => p.Frac == Fraction.Goblin).Sum(p => p.HitPoints)}".Dump();
$"{new string(' ', Width)} HP[E] := {Units.Where(p => p.Frac == Fraction.Elf).Sum(p => p.HitPoints)}".Dump();
"".Dump();
"".Dump();
"".Dump();
}
void Load(string[] input, int pwr)
{
Width = input[0].Length;
Height = input.Length;
Map = new UserQuery.Entity[Width, Height];
Units = new List<Entity>();
for (int yy = 0; yy < Height; yy++)
{
for (int xx = 0; xx < Width; xx++)
{
if (input[yy][xx] == '#')
Map[xx, yy] = new Entity { Frac = Fraction.Wall, AttackPower = 0, HitPoints = int.MaxValue, X = xx, Y = yy, Alive = true };
else if (input[yy][xx] == '.')
Map[xx, yy] = null;
else if (input[yy][xx] == 'E')
Units.Add(Map[xx, yy] = new Entity { Frac = Fraction.Elf, AttackPower = pwr, HitPoints = 200, X = xx, Y = yy, Alive = true });
else if (input[yy][xx] == 'G')
Units.Add(Map[xx, yy] = new Entity { Frac = Fraction.Goblin, AttackPower = 3, HitPoints = 200, X = xx, Y = yy, Alive = true });
else
throw new Exception($"[{xx}|{yy}] := {input[xx][yy]}");
}
}
}

View File

@ -0,0 +1,369 @@
--- Day 15: Beverage Bandits ---
Having perfected their hot chocolate, the Elves have a new problem: the Goblins that live in these caves will do anything to steal it. Looks like they're here for a fight.
You scan the area, generating a map of the walls (#), open cavern (.), and starting position of every Goblin (G) and Elf (E) (your puzzle input).
Combat proceeds in rounds; in each round, each unit that is still alive takes a turn, resolving all of its actions before the next unit's turn begins. On each unit's turn, it tries to move into range of an enemy (if it isn't already) and then attack (if it is in range).
All units are very disciplined and always follow very strict combat rules. Units never move or attack diagonally, as doing so would be dishonorable. When multiple choices are equally valid, ties are broken in reading order: top-to-bottom, then left-to-right. For instance, the order in which units take their turns within a round is the reading order of their starting positions in that round, regardless of the type of unit or whether other units have moved after the round started. For example:
would take their
These units: turns in this order:
####### #######
#.G.E.# #.1.2.#
#E.G.E# #3.4.5#
#.G.E.# #.6.7.#
####### #######
Each unit begins its turn by identifying all possible targets (enemy units). If no targets remain, combat ends.
Then, the unit identifies all of the open squares (.) that are in range of each target; these are the squares which are adjacent (immediately up, down, left, or right) to any target and which aren't already occupied by a wall or another unit. Alternatively, the unit might already be in range of a target. If the unit is not already in range of a target, and there are no open squares which are in range of a target, the unit ends its turn.
If the unit is already in range of a target, it does not move, but continues its turn with an attack. Otherwise, since it is not in range of a target, it moves.
To move, the unit first considers the squares that are in range and determines which of those squares it could reach in the fewest steps. A step is a single movement to any adjacent (immediately up, down, left, or right) open (.) square. Units cannot move into walls or other units. The unit does this while considering the current positions of units and does not do any prediction about where units will be later. If the unit cannot reach (find an open path to) any of the squares that are in range, it ends its turn. If multiple squares are in range and tied for being reachable in the fewest steps, the square which is first in reading order is chosen. For example:
Targets: In range: Reachable: Nearest: Chosen:
####### ####### ####### ####### #######
#E..G.# #E.?G?# #E.@G.# #E.!G.# #E.+G.#
#...#.# --> #.?.#?# --> #.@.#.# --> #.!.#.# --> #...#.#
#.G.#G# #?G?#G# #@G@#G# #!G.#G# #.G.#G#
####### ####### ####### ####### #######
In the above scenario, the Elf has three targets (the three Goblins):
Each of the Goblins has open, adjacent squares which are in range (marked with a ? on the map).
Of those squares, four are reachable (marked @); the other two (on the right) would require moving through a wall or unit to reach.
Three of these reachable squares are nearest, requiring the fewest steps (only 2) to reach (marked !).
Of those, the square which is first in reading order is chosen (+).
The unit then takes a single step toward the chosen square along the shortest path to that square. If multiple steps would put the unit equally closer to its destination, the unit chooses the step which is first in reading order. (This requires knowing when there is more than one shortest path so that you can consider the first step of each such path.) For example:
In range: Nearest: Chosen: Distance: Step:
####### ####### ####### ####### #######
#.E...# #.E...# #.E...# #4E212# #..E..#
#...?.# --> #...!.# --> #...+.# --> #32101# --> #.....#
#..?G?# #..!G.# #...G.# #432G2# #...G.#
####### ####### ####### ####### #######
The Elf sees three squares in range of a target (?), two of which are nearest (!), and so the first in reading order is chosen (+). Under "Distance", each open square is marked with its distance from the destination square; the two squares to which the Elf could move on this turn (down and to the right) are both equally good moves and would leave the Elf 2 steps from being in range of the Goblin. Because the step which is first in reading order is chosen, the Elf moves right one square.
Here's a larger example of movement:
Initially:
#########
#G..G..G#
#.......#
#.......#
#G..E..G#
#.......#
#.......#
#G..G..G#
#########
After 1 round:
#########
#.G...G.#
#...G...#
#...E..G#
#.G.....#
#.......#
#G..G..G#
#.......#
#########
After 2 rounds:
#########
#..G.G..#
#...G...#
#.G.E.G.#
#.......#
#G..G..G#
#.......#
#.......#
#########
After 3 rounds:
#########
#.......#
#..GGG..#
#..GEG..#
#G..G...#
#......G#
#.......#
#.......#
#########
Once the Goblins and Elf reach the positions above, they all are either in range of a target or cannot find any square in range of a target, and so none of the units can move until a unit dies.
After moving (or if the unit began its turn in range of a target), the unit attacks.
To attack, the unit first determines all of the targets that are in range of it by being immediately adjacent to it. If there are no such targets, the unit ends its turn. Otherwise, the adjacent target with the fewest hit points is selected; in a tie, the adjacent target with the fewest hit points which is first in reading order is selected.
The unit deals damage equal to its attack power to the selected target, reducing its hit points by that amount. If this reduces its hit points to 0 or fewer, the selected target dies: its square becomes . and it takes no further turns.
Each unit, either Goblin or Elf, has 3 attack power and starts with 200 hit points.
For example, suppose the only Elf is about to attack:
HP: HP:
G.... 9 G.... 9
..G.. 4 ..G.. 4
..EG. 2 --> ..E..
..G.. 2 ..G.. 2
...G. 1 ...G. 1
The "HP" column shows the hit points of the Goblin to the left in the corresponding row. The Elf is in range of three targets: the Goblin above it (with 4 hit points), the Goblin to its right (with 2 hit points), and the Goblin below it (also with 2 hit points). Because three targets are in range, the ones with the lowest hit points are selected: the two Goblins with 2 hit points each (one to the right of the Elf and one below the Elf). Of those, the Goblin first in reading order (the one to the right of the Elf) is selected. The selected Goblin's hit points (2) are reduced by the Elf's attack power (3), reducing its hit points to -1, killing it.
After attacking, the unit's turn ends. Regardless of how the unit's turn ends, the next unit in the round takes its turn. If all units have taken turns in this round, the round ends, and a new round begins.
The Elves look quite outnumbered. You need to determine the outcome of the battle: the number of full rounds that were completed (not counting the round in which combat ends) multiplied by the sum of the hit points of all remaining units at the moment combat ends. (Combat only ends when a unit finds no targets during its turn.)
Below is an entire sample combat. Next to each map, each row's units' hit points are listed from left to right.
Initially:
#######
#.G...# G(200)
#...EG# E(200), G(200)
#.#.#G# G(200)
#..G#E# G(200), E(200)
#.....#
#######
After 1 round:
#######
#..G..# G(200)
#...EG# E(197), G(197)
#.#G#G# G(200), G(197)
#...#E# E(197)
#.....#
#######
After 2 rounds:
#######
#...G.# G(200)
#..GEG# G(200), E(188), G(194)
#.#.#G# G(194)
#...#E# E(194)
#.....#
#######
Combat ensues; eventually, the top Elf dies:
After 23 rounds:
#######
#...G.# G(200)
#..G.G# G(200), G(131)
#.#.#G# G(131)
#...#E# E(131)
#.....#
#######
After 24 rounds:
#######
#..G..# G(200)
#...G.# G(131)
#.#G#G# G(200), G(128)
#...#E# E(128)
#.....#
#######
After 25 rounds:
#######
#.G...# G(200)
#..G..# G(131)
#.#.#G# G(125)
#..G#E# G(200), E(125)
#.....#
#######
After 26 rounds:
#######
#G....# G(200)
#.G...# G(131)
#.#.#G# G(122)
#...#E# E(122)
#..G..# G(200)
#######
After 27 rounds:
#######
#G....# G(200)
#.G...# G(131)
#.#.#G# G(119)
#...#E# E(119)
#...G.# G(200)
#######
After 28 rounds:
#######
#G....# G(200)
#.G...# G(131)
#.#.#G# G(116)
#...#E# E(113)
#....G# G(200)
#######
More combat ensues; eventually, the bottom Elf dies:
After 47 rounds:
#######
#G....# G(200)
#.G...# G(131)
#.#.#G# G(59)
#...#.#
#....G# G(200)
#######
Before the 48th round can finish, the top-left Goblin finds that there are no targets remaining, and so combat ends. So, the number of full rounds that were completed is 47, and the sum of the hit points of all remaining units is 200+131+59+200 = 590. From these, the outcome of the battle is 47 * 590 = 27730.
Here are a few example summarized combats:
####### #######
#G..#E# #...#E# E(200)
#E#E.E# #E#...# E(197)
#G.##.# --> #.E##.# E(185)
#...#E# #E..#E# E(200), E(200)
#...E.# #.....#
####### #######
Combat ends after 37 full rounds
Elves win with 982 total hit points left
Outcome: 37 * 982 = 36334
####### #######
#E..EG# #.E.E.# E(164), E(197)
#.#G.E# #.#E..# E(200)
#E.##E# --> #E.##.# E(98)
#G..#.# #.E.#.# E(200)
#..E#.# #...#.#
####### #######
Combat ends after 46 full rounds
Elves win with 859 total hit points left
Outcome: 46 * 859 = 39514
####### #######
#E.G#.# #G.G#.# G(200), G(98)
#.#G..# #.#G..# G(200)
#G.#.G# --> #..#..#
#G..#.# #...#G# G(95)
#...E.# #...G.# G(200)
####### #######
Combat ends after 35 full rounds
Goblins win with 793 total hit points left
Outcome: 35 * 793 = 27755
####### #######
#.E...# #.....#
#.#..G# #.#G..# G(200)
#.###.# --> #.###.#
#E#G#G# #.#.#.#
#...#G# #G.G#G# G(98), G(38), G(200)
####### #######
Combat ends after 54 full rounds
Goblins win with 536 total hit points left
Outcome: 54 * 536 = 28944
######### #########
#G......# #.G.....# G(137)
#.E.#...# #G.G#...# G(200), G(200)
#..##..G# #.G##...# G(200)
#...##..# --> #...##..#
#...#...# #.G.#...# G(200)
#.G...G.# #.......#
#.....G.# #.......#
######### #########
Combat ends after 20 full rounds
Goblins win with 937 total hit points left
Outcome: 20 * 937 = 18740
What is the outcome of the combat described in your puzzle input?
--- Part Two ---
According to your calculations, the Elves are going to lose badly. Surely, you won't mess up the timeline too much if you give them just a little advanced technology, right?
You need to make sure the Elves not only win, but also suffer no losses: even the death of a single Elf is unacceptable.
However, you can't go too far: larger changes will be more likely to permanently alter spacetime.
So, you need to find the outcome of the battle in which the Elves have the lowest integer attack power (at least 4) that allows them to win without a single death. The Goblins always have an attack power of 3.
In the first summarized example above, the lowest attack power the Elves need to win without losses is 15:
####### #######
#.G...# #..E..# E(158)
#...EG# #...E.# E(14)
#.#.#G# --> #.#.#.#
#..G#E# #...#.#
#.....# #.....#
####### #######
Combat ends after 29 full rounds
Elves win with 172 total hit points left
Outcome: 29 * 172 = 4988
In the second example above, the Elves need only 4 attack power:
####### #######
#E..EG# #.E.E.# E(200), E(23)
#.#G.E# #.#E..# E(200)
#E.##E# --> #E.##E# E(125), E(200)
#G..#.# #.E.#.# E(200)
#..E#.# #...#.#
####### #######
Combat ends after 33 full rounds
Elves win with 948 total hit points left
Outcome: 33 * 948 = 31284
In the third example above, the Elves need 15 attack power:
####### #######
#E.G#.# #.E.#.# E(8)
#.#G..# #.#E..# E(86)
#G.#.G# --> #..#..#
#G..#.# #...#.#
#...E.# #.....#
####### #######
Combat ends after 37 full rounds
Elves win with 94 total hit points left
Outcome: 37 * 94 = 3478
In the fourth example above, the Elves need 12 attack power:
####### #######
#.E...# #...E.# E(14)
#.#..G# #.#..E# E(152)
#.###.# --> #.###.#
#E#G#G# #.#.#.#
#...#G# #...#.#
####### #######
Combat ends after 39 full rounds
Elves win with 166 total hit points left
Outcome: 39 * 166 = 6474
In the last example above, the lone Elf needs 34 attack power:
######### #########
#G......# #.......#
#.E.#...# #.E.#...# E(38)
#..##..G# #..##...#
#...##..# --> #...##..#
#...#...# #...#...#
#.G...G.# #.......#
#.....G.# #.......#
######### #########
Combat ends after 30 full rounds
Elves win with 38 total hit points left
Outcome: 30 * 38 = 1140
After increasing the Elves' attack power until it is just barely enough for them to win without any Elves dying, what is the outcome of the combat described in your puzzle input?

View File

@ -0,0 +1,32 @@
################################
#######..G######################
########.....###################
##########....############.....#
###########...#####..#####.....#
###########G..###GG....G.......#
##########.G#####G...#######..##
###########...G.#...############
#####.#####..........####....###
####.....###.........##.#....###
####.#................G....#####
####......#.................####
##....#G......#####........#####
########....G#######.......#####
########..G.#########.E...######
########....#########.....######
#######.....#########.....######
#######...G.#########....#######
#######...#.#########....#######
####.G.G.....#######...#.#######
##...#...G....#####E...#.#######
###..#.G.##...E....E.......###.#
######...................#....E#
#######...............E.########
#G###...#######....E...#########
#..##.######.E#.#.....##########
#..#....##......##.E...#########
#G......###.#..##......#########
#....#######....G....E.#########
#.##########..........##########
#############.###.......########
################################

View File

@ -0,0 +1,75 @@
<Query Kind="Program" />
class Command
{
public int OpCode, Arg1, Arg2, Dst;
public Command(int[] a) { OpCode=a[0];Arg1=a[1];Arg2=a[2];Dst=a[3]; }
}
(string, Action<int[], Command>)[] Operations = new (string, Action<int[], Command>)[]
{
("addr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + reg[cmd.Arg2]; } ),
("addi", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + cmd.Arg2; } ),
("mulr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * reg[cmd.Arg2]; } ),
("muli", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * cmd.Arg2; } ),
("banr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & reg[cmd.Arg2]; } ),
("bani", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & cmd.Arg2; } ),
("borr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | reg[cmd.Arg2]; } ),
("bori", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | cmd.Arg2; } ),
("setr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1]; } ),
("seti", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1; } ),
("gtir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 > reg[cmd.Arg2] ? 1:0; } ),
("gtri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > cmd.Arg2 ? 1:0; } ),
("gtrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > reg[cmd.Arg2] ? 1:0; } ),
("eqir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 == reg[cmd.Arg2] ? 1:0; } ),
("eqri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == cmd.Arg2 ? 1:0; } ),
("eqrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == reg[cmd.Arg2] ? 1:0; } ),
};
void Main()
{
(var input1, var input2) = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"16_input.txt")));
input1.Count(ip => Operations.Count(op => Test(ip.Item1, op.Item2, ip.Item2, ip.Item3)) >= 3 ).Dump();
}
(List<(int[],Command,int[])>, List<Command>) Load(string[] lines)
{
var r1 = new List<(int[], Command, int[])>();
int i = 0;
for (;; i+=4)
{
if (string.IsNullOrWhiteSpace(lines[i])) break;
var a = lines[i + 0].Substring(8).TrimStart('[').TrimEnd(']').Split(',').Select(p => int.Parse(p.Trim())).ToArray();
var b = new Command(lines[i + 1].Split(' ').Select(p => int.Parse(p.Trim())).ToArray());
var c = lines[i + 2].Substring(8).TrimStart('[').TrimEnd(']').Split(',').Select(p => int.Parse(p.Trim())).ToArray();
r1.Add( (a,b,c) );
}
for (; string.IsNullOrWhiteSpace(lines[i]); i++);
var r2 = new List<Command>();
for (; i < lines.Length; i++) r2.Add(new Command(lines[i].Split(' ').Select(p => int.Parse(p.Trim())).ToArray()));
return (r1, r2);
}
bool ArrayEquals(int[] a, int[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; i++) if (a[i] != b[i]) return false;
return true;
}
bool Test(int[] before, Action<int[], Command> op, Command cmd, int[] after)
{
var register=before.ToArray();
op(register, cmd);
if (register.Length != after.Length) return false;
for (int i = 0; i < register.Length; i++) if (register[i] != after[i]) return false;
return true;
}

View File

@ -0,0 +1,113 @@
<Query Kind="Program" />
class Command
{
public int OpCode, Arg1, Arg2, Dst;
public Command(int[] a) { OpCode = a[0]; Arg1 = a[1]; Arg2 = a[2]; Dst = a[3]; }
}
(string, Action<int[], Command>)[] Operations = new(string, Action<int[], Command>)[]
{
("addr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + reg[cmd.Arg2]; } ),
("addi", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + cmd.Arg2; } ),
("mulr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * reg[cmd.Arg2]; } ),
("muli", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * cmd.Arg2; } ),
("banr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & reg[cmd.Arg2]; } ),
("bani", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & cmd.Arg2; } ),
("borr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | reg[cmd.Arg2]; } ),
("bori", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | cmd.Arg2; } ),
("setr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1]; } ),
("seti", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1; } ),
("gtir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 > reg[cmd.Arg2] ? 1:0; } ),
("gtri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > cmd.Arg2 ? 1:0; } ),
("gtrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > reg[cmd.Arg2] ? 1:0; } ),
("eqir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 == reg[cmd.Arg2] ? 1:0; } ),
("eqri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == cmd.Arg2 ? 1:0; } ),
("eqrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == reg[cmd.Arg2] ? 1:0; } ),
};
void Main()
{
(var input1, var input2) = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"16_input.txt")));
var mapping = GenerateMapping(input1);
int[] data = new int[128];
foreach (var cmd in input2) mapping[cmd.OpCode].Item2(data, cmd);
data[0].Dump();
}
(List<(int[], Command, int[])>, List<Command>) Load(string[] lines)
{
var r1 = new List<(int[], Command, int[])>();
int i = 0;
for (; ; i += 4)
{
if (string.IsNullOrWhiteSpace(lines[i])) break;
var a = lines[i + 0].Substring(8).TrimStart('[').TrimEnd(']').Split(',').Select(p => int.Parse(p.Trim())).ToArray();
var b = new Command(lines[i + 1].Split(' ').Select(p => int.Parse(p.Trim())).ToArray());
var c = lines[i + 2].Substring(8).TrimStart('[').TrimEnd(']').Split(',').Select(p => int.Parse(p.Trim())).ToArray();
r1.Add((a, b, c));
}
for (; string.IsNullOrWhiteSpace(lines[i]); i++) ;
var r2 = new List<Command>();
for (; i < lines.Length; i++) r2.Add(new Command(lines[i].Split(' ').Select(p => int.Parse(p.Trim())).ToArray()));
return (r1, r2);
}
bool ArrayEquals(int[] a, int[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; i++) if (a[i] != b[i]) return false;
return true;
}
bool Test(int[] before, Action<int[], Command> op, Command cmd, int[] after)
{
var register = before.ToArray();
op(register, cmd);
if (register.Length != after.Length) return false;
for (int i = 0; i < register.Length; i++) if (register[i] != after[i]) return false;
return true;
}
(string, Action<int[], Command>)[] GenerateMapping(List<(int[], Command,int[])> input)
{
var mapping = Enumerable
.Range(0,16)
.Select(p => Operations.ToList() )
.ToArray();
foreach (var test in input)
{
if (mapping[test.Item2.OpCode].Count == 1) { Assert(Test(test.Item1, mapping[test.Item2.OpCode][0].Item2, test.Item2, test.Item3)); continue;}
mapping[test.Item2.OpCode] = mapping[test.Item2.OpCode].Where(op => Test(test.Item1, op.Item2, test.Item2, test.Item3)).ToList();
Assert(mapping[test.Item2.OpCode].Any());
}
for (int i_ = 0; i_ < 16; i_++)
{
foreach (var rm in mapping.Select((m,i) => (m,i)).Where(m => m.m.Count==1).ToList())
{
for (int j = 0; j < 16; j++)
{
if (j==rm.i) continue;
mapping[j] = mapping[j].Where(m => m.Item1 != rm.m.Single().Item1).ToList();
}
}
}
Assert(mapping.Select(p => p.Single().Item1).Distinct().Count() == 16);
return mapping.Select(p => p.Single()).ToArray();
}
void Assert(bool b) { if (!b) throw new Exception(); }

View File

@ -0,0 +1,76 @@
--- Day 16: Chronal Classification ---
As you see the Elves defend their hot chocolate successfully, you go back to falling through time. This is going to become a problem.
If you're ever going to return to your own time, you need to understand how this device on your wrist works. You have a little while before you reach your next destination, and with a bit of trial and error, you manage to pull up a programming manual on the device's tiny screen.
According to the manual, the device has four registers (numbered 0 through 3) that can be manipulated by instructions containing one of 16 opcodes. The registers start with the value 0.
Every instruction consists of four values: an opcode, two inputs (named A and B), and an output (named C), in that order. The opcode specifies the behavior of the instruction and how the inputs are interpreted. The output, C, is always treated as a register.
In the opcode descriptions below, if something says "value A", it means to take the number given as A literally. (This is also called an "immediate" value.) If something says "register A", it means to use the number given as A to read from (or write to) the register with that number. So, if the opcode addi adds register A and value B, storing the result in register C, and the instruction addi 0 7 3 is encountered, it would add 7 to the value contained by register 0 and store the sum in register 3, never modifying registers 0, 1, or 2 in the process.
Many opcodes are similar except for how they interpret their arguments. The opcodes fall into seven general categories:
Addition:
addr (add register) stores into register C the result of adding register A and register B.
addi (add immediate) stores into register C the result of adding register A and value B.
Multiplication:
mulr (multiply register) stores into register C the result of multiplying register A and register B.
muli (multiply immediate) stores into register C the result of multiplying register A and value B.
Bitwise AND:
banr (bitwise AND register) stores into register C the result of the bitwise AND of register A and register B.
bani (bitwise AND immediate) stores into register C the result of the bitwise AND of register A and value B.
Bitwise OR:
borr (bitwise OR register) stores into register C the result of the bitwise OR of register A and register B.
bori (bitwise OR immediate) stores into register C the result of the bitwise OR of register A and value B.
Assignment:
setr (set register) copies the contents of register A into register C. (Input B is ignored.)
seti (set immediate) stores value A into register C. (Input B is ignored.)
Greater-than testing:
gtir (greater-than immediate/register) sets register C to 1 if value A is greater than register B. Otherwise, register C is set to 0.
gtri (greater-than register/immediate) sets register C to 1 if register A is greater than value B. Otherwise, register C is set to 0.
gtrr (greater-than register/register) sets register C to 1 if register A is greater than register B. Otherwise, register C is set to 0.
Equality testing:
eqir (equal immediate/register) sets register C to 1 if value A is equal to register B. Otherwise, register C is set to 0.
eqri (equal register/immediate) sets register C to 1 if register A is equal to value B. Otherwise, register C is set to 0.
eqrr (equal register/register) sets register C to 1 if register A is equal to register B. Otherwise, register C is set to 0.
Unfortunately, while the manual gives the name of each opcode, it doesn't seem to indicate the number. However, you can monitor the CPU to see the contents of the registers before and after instructions are executed to try to work them out. Each opcode has a number from 0 through 15, but the manual doesn't say which is which. For example, suppose you capture the following sample:
Before: [3, 2, 1, 1]
9 2 1 2
After: [3, 2, 2, 1]
This sample shows the effect of the instruction 9 2 1 2 on the registers. Before the instruction is executed, register 0 has value 3, register 1 has value 2, and registers 2 and 3 have value 1. After the instruction is executed, register 2's value becomes 2.
The instruction itself, 9 2 1 2, means that opcode 9 was executed with A=2, B=1, and C=2. Opcode 9 could be any of the 16 opcodes listed above, but only three of them behave in a way that would cause the result shown in the sample:
Opcode 9 could be mulr: register 2 (which has a value of 1) times register 1 (which has a value of 2) produces 2, which matches the value stored in the output register, register 2.
Opcode 9 could be addi: register 2 (which has a value of 1) plus value 1 produces 2, which matches the value stored in the output register, register 2.
Opcode 9 could be seti: value 2 matches the value stored in the output register, register 2; the number given for B is irrelevant.
None of the other opcodes produce the result captured in the sample. Because of this, the sample above behaves like three opcodes.
You collect many of these samples (the first section of your puzzle input). The manual also includes a small test program (the second section of your puzzle input) - you can ignore it for now.
Ignoring the opcode numbers, how many samples in your puzzle input behave like three or more opcodes?
--- Part Two ---
Using the samples you collected, work out the number of each opcode and execute the test program (the second section of your puzzle input).
What value is contained in register 0 after executing the test program?

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
<Query Kind="Program">
<Namespace>System.Drawing</Namespace>
</Query>
bool[,] Map;
int X0;
int Y0;
int Width;
int Height;
enum Water { None=0, Flow=1, Rest=2 }
Water[,] water;
void Main()
{
Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"17_input.txt")));
//DumpMap();
water[500, 0] = Water.Flow;
int w = 0;
Water[,] wold = new Water[Width, Height];
for (int gen = 0; ; gen++)
{
Tick();
//Util.ClearResults();
//DumpMap();
int w0 = WaterCount();
if (w == w0 && WaterEquals(wold,water)) { $"{w0} after {gen} generations".Dump(); DumpMap(); /* DumpMapASCII(); */ return; }
w=w0;
for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) wold[x,y]=water[x,y];
}
}
bool WaterEquals(Water[,] a, Water[,] b)
{
for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) if (a[x,y] != b[x,y])return false;
return true;
}
int WaterCount()
{
int c = 0;
for (int y = Y0; y < Height; y++) for (int x = 0; x < Width; x++) if (water[x,y] != Water.None)c++;
return c;
}
void Tick()
{
for (int xx = 0; xx < Width; xx++) for (int yy = 0; yy < Height; yy++) if (water[xx,yy]==Water.Flow) water[xx,yy]=Water.None;
var next = new Stack<(int,int)>();
next.Push( (500, 0) );
while (next.Any())
{
(var x, var y) = next.Pop();
if (y==Height-1) continue;
if (Map[x,y+1] || water[x,y+1]==Water.Rest)
{
var l = false;
var r = false;
if (!Map[x - 1, y] && water[x - 1, y] == Water.None) { l = true; water[x - 1, y] = Water.Flow; next.Push((x - 1, y)); }
if (!Map[x + 1, y] && water[x + 1, y] == Water.None) { r = true; water[x + 1, y] = Water.Flow; next.Push((x + 1, y)); }
if (!l && !r)
{
var solid = true;
for (int xx = x; solid; xx++)
{
if (water[xx, y] == Water.None) { solid = false; break; }
if (!(Map[xx, y + 1] || water[xx, y + 1] == Water.Rest)) { solid = false; break; }
if (Map[xx + 1, y]) break;
}
for (int xx = x; solid; xx--)
{
if (water[xx, y] == Water.None) { solid = false; break; }
if (!(Map[xx, y + 1] || water[xx, y + 1] == Water.Rest)) { solid = false; break; }
if (Map[xx - 1, y]) break;
}
if (solid)
{
for (int xx = x; !Map[xx, y]; xx++) { Assert(water[xx, y] != Water.None); water[xx, y] = Water.Rest; }
for (int xx = x; !Map[xx, y]; xx--) { Assert(water[xx, y] != Water.None); water[xx, y] = Water.Rest; }
}
}
}
else
{
water[x, y+1] = Water.Flow;
next.Push( (x,y+1) );
}
}
}
void Load(string[] v)
{
var data = new List<(int,int,int,int)>();
for (int i = 0; i < v.Length; i++)
{
var a = v[i].Split(',')[0].Trim();
var b = v[i].Split(',')[1].Trim();
if (b.StartsWith("x")) { var t = b; b = a; a = t; }
a = a.Substring(2);
b = b.Substring(2);
var a1 = int.Parse(a.Contains("..") ? a.Split('.')[0] : a);
var a2 = int.Parse(a.Contains("..") ? a.Split('.')[2] : a);
var b1 = int.Parse(b.Contains("..") ? b.Split('.')[0] : b);
var b2 = int.Parse(b.Contains("..") ? b.Split('.')[2] : b);
data.Add( (a1,a2,b1,b2) );
}
Width = data.Max(m => Math.Max(m.Item1, m.Item2)) + 1;
Height = data.Max(m => Math.Max(m.Item3, m.Item4)) + 1;
X0 = data.Min(m => Math.Min(m.Item1, m.Item2)) - 1;
Y0 = data.Min(m => Math.Min(m.Item3, m.Item4));
Map = new bool[Width, Height];
foreach ((var x1, var x2, var y1, var y2) in data)
{
for (int x = x1; x <= x2; x++) for (int y = y1; y <= y2; y++) Map[x, y] = true;
}
water = new Water[Width, Height];
}
void DumpMap()
{
Bitmap bmp = new Bitmap(Width - X0, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Wheat);
for (int y = 0; y < Height; y++)
{
for (int x = X0; x < Width; x++)
{
if (x == (500 - X0) && y == 0) g.FillRectangle(Brushes.DarkBlue, x - X0, y, 1, 1);
else if (Map[x, y]) g.FillRectangle(Brushes.Black, x - X0, y, 1, 1);
else if (water[x, y] == Water.Flow) g.FillRectangle(Brushes.Cyan, x - X0, y, 1, 1);
else if (water[x, y] == Water.Rest) g.FillRectangle(Brushes.LightBlue, x - X0, y, 1, 1);
}
}
}
bmp.Dump();
}
void DumpMapASCII()
{
StringBuilder b = new StringBuilder();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
if (x == (500) && y == 0) b.Append('+');
else if (Map[x, y]) b.Append('#');
else if (water[x, y] == Water.Flow) b.Append('|');
else if (water[x, y] == Water.Rest) b.Append('~');
else b.Append('.');
}
b.AppendLine();
}
b.ToString().Dump();
}
void Assert(bool b) { if (!b) throw new Exception(); }

View File

@ -0,0 +1,180 @@
<Query Kind="Program">
<Namespace>System.Drawing</Namespace>
</Query>
bool[,] Map;
int X0;
int Y0;
int Width;
int Height;
enum Water { None=0, Flow=1, Rest=2 }
Water[,] water;
void Main()
{
Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"17_input.txt")));
//DumpMap();
water[500, 0] = Water.Flow;
int w = 0;
Water[,] wold = new Water[Width, Height];
for (int gen = 0; ; gen++)
{
Tick();
//Util.ClearResults();
//DumpMap();
int w0 = WaterCount();
if (w == w0 && WaterEquals(wold, water)) { $"{StillWaterCount()} after {gen} generations (all = {w0})".Dump(); DumpMap(); /* DumpMapASCII(); */ return; }
w=w0;
for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) wold[x,y]=water[x,y];
}
}
bool WaterEquals(Water[,] a, Water[,] b)
{
for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) if (a[x,y] != b[x,y])return false;
return true;
}
int WaterCount()
{
int c = 0;
for (int y = Y0; y < Height; y++) for (int x = 0; x < Width; x++) if (water[x, y] != Water.None) c++;
return c;
}
int StillWaterCount()
{
int c = 0;
for (int y = Y0; y < Height; y++) for (int x = 0; x < Width; x++) if (water[x, y] == Water.Rest) c++;
return c;
}
void Tick()
{
for (int xx = 0; xx < Width; xx++) for (int yy = 0; yy < Height; yy++) if (water[xx,yy]==Water.Flow) water[xx,yy]=Water.None;
var next = new Stack<(int,int)>();
next.Push( (500, 0) );
while (next.Any())
{
(var x, var y) = next.Pop();
if (y==Height-1) continue;
if (Map[x,y+1] || water[x,y+1]==Water.Rest)
{
var l = false;
var r = false;
if (!Map[x - 1, y] && water[x - 1, y] == Water.None) { l = true; water[x - 1, y] = Water.Flow; next.Push((x - 1, y)); }
if (!Map[x + 1, y] && water[x + 1, y] == Water.None) { r = true; water[x + 1, y] = Water.Flow; next.Push((x + 1, y)); }
if (!l && !r)
{
var solid = true;
for (int xx = x; solid; xx++)
{
if (water[xx, y] == Water.None) { solid = false; break; }
if (!(Map[xx, y + 1] || water[xx, y + 1] == Water.Rest)) { solid = false; break; }
if (Map[xx + 1, y]) break;
}
for (int xx = x; solid; xx--)
{
if (water[xx, y] == Water.None) { solid = false; break; }
if (!(Map[xx, y + 1] || water[xx, y + 1] == Water.Rest)) { solid = false; break; }
if (Map[xx - 1, y]) break;
}
if (solid)
{
for (int xx = x; !Map[xx, y]; xx++) { Assert(water[xx, y] != Water.None); water[xx, y] = Water.Rest; }
for (int xx = x; !Map[xx, y]; xx--) { Assert(water[xx, y] != Water.None); water[xx, y] = Water.Rest; }
}
}
}
else
{
water[x, y+1] = Water.Flow;
next.Push( (x,y+1) );
}
}
}
void Load(string[] v)
{
var data = new List<(int,int,int,int)>();
for (int i = 0; i < v.Length; i++)
{
var a = v[i].Split(',')[0].Trim();
var b = v[i].Split(',')[1].Trim();
if (b.StartsWith("x")) { var t = b; b = a; a = t; }
a = a.Substring(2);
b = b.Substring(2);
var a1 = int.Parse(a.Contains("..") ? a.Split('.')[0] : a);
var a2 = int.Parse(a.Contains("..") ? a.Split('.')[2] : a);
var b1 = int.Parse(b.Contains("..") ? b.Split('.')[0] : b);
var b2 = int.Parse(b.Contains("..") ? b.Split('.')[2] : b);
data.Add( (a1,a2,b1,b2) );
}
Width = data.Max(m => Math.Max(m.Item1, m.Item2)) + 1;
Height = data.Max(m => Math.Max(m.Item3, m.Item4)) + 1;
X0 = data.Min(m => Math.Min(m.Item1, m.Item2)) - 1;
Y0 = data.Min(m => Math.Min(m.Item3, m.Item4));
Map = new bool[Width, Height];
foreach ((var x1, var x2, var y1, var y2) in data)
{
for (int x = x1; x <= x2; x++) for (int y = y1; y <= y2; y++) Map[x, y] = true;
}
water = new Water[Width, Height];
}
void DumpMap()
{
Bitmap bmp = new Bitmap(Width - X0, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Wheat);
for (int y = 0; y < Height; y++)
{
for (int x = X0; x < Width; x++)
{
if (x == (500 - X0) && y == 0) g.FillRectangle(Brushes.DarkBlue, x - X0, y, 1, 1);
else if (Map[x, y]) g.FillRectangle(Brushes.Black, x - X0, y, 1, 1);
else if (water[x, y] == Water.Flow) g.FillRectangle(Brushes.Cyan, x - X0, y, 1, 1);
else if (water[x, y] == Water.Rest) g.FillRectangle(Brushes.LightBlue, x - X0, y, 1, 1);
}
}
}
bmp.Dump();
}
void DumpMapASCII()
{
StringBuilder b = new StringBuilder();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
if (x == (500) && y == 0) b.Append('+');
else if (Map[x, y]) b.Append('#');
else if (water[x, y] == Water.Flow) b.Append('|');
else if (water[x, y] == Water.Rest) b.Append('~');
else b.Append('.');
}
b.AppendLine();
}
b.ToString().Dump();
}
void Assert(bool b) { if (!b) throw new Exception(); }

View File

@ -0,0 +1,161 @@
--- Day 17: Reservoir Research ---
You arrive in the year 18. If it weren't for the coat you got in 1018, you would be very cold: the North Pole base hasn't even been constructed.
Rather, it hasn't been constructed yet. The Elves are making a little progress, but there's not a lot of liquid water in this climate, so they're getting very dehydrated. Maybe there's more underground?
You scan a two-dimensional vertical slice of the ground nearby and discover that it is mostly sand with veins of clay. The scan only provides data with a granularity of square meters, but it should be good enough to determine how much water is trapped there. In the scan, x represents the distance to the right, and y represents the distance down. There is also a spring of water near the surface at x=500, y=0. The scan identifies which square meters are clay (your puzzle input).
For example, suppose your scan shows the following veins of clay:
x=495, y=2..7
y=7, x=495..501
x=501, y=3..7
x=498, y=2..4
x=506, y=1..2
x=498, y=10..13
x=504, y=10..13
y=13, x=498..504
Rendering clay as #, sand as ., and the water spring as +, and with x increasing to the right and y increasing downward, this becomes:
44444455555555
99999900000000
45678901234567
0 ......+.......
1 ............#.
2 .#..#.......#.
3 .#..#..#......
4 .#..#..#......
5 .#.....#......
6 .#.....#......
7 .#######......
8 ..............
9 ..............
10 ....#.....#...
11 ....#.....#...
12 ....#.....#...
13 ....#######...
The spring of water will produce water forever. Water can move through sand, but is blocked by clay. Water always moves down when possible, and spreads to the left and right otherwise, filling space that has clay on both sides and falling out otherwise.
For example, if five squares of water are created, they will flow downward until they reach the clay and settle there. Water that has come to rest is shown here as ~, while sand through which water has passed (but which is now dry again) is shown as |:
......+.......
......|.....#.
.#..#.|.....#.
.#..#.|#......
.#..#.|#......
.#....|#......
.#~~~~~#......
.#######......
..............
..............
....#.....#...
....#.....#...
....#.....#...
....#######...
Two squares of water can't occupy the same location. If another five squares of water are created, they will settle on the first five, filling the clay reservoir a little more:
......+.......
......|.....#.
.#..#.|.....#.
.#..#.|#......
.#..#.|#......
.#~~~~~#......
.#~~~~~#......
.#######......
..............
..............
....#.....#...
....#.....#...
....#.....#...
....#######...
Water pressure does not apply in this scenario. If another four squares of water are created, they will stay on the right side of the barrier, and no water will reach the left side:
......+.......
......|.....#.
.#..#.|.....#.
.#..#~~#......
.#..#~~#......
.#~~~~~#......
.#~~~~~#......
.#######......
..............
..............
....#.....#...
....#.....#...
....#.....#...
....#######...
At this point, the top reservoir overflows. While water can reach the tiles above the surface of the water, it cannot settle there, and so the next five squares of water settle like this:
......+.......
......|.....#.
.#..#||||...#.
.#..#~~#|.....
.#..#~~#|.....
.#~~~~~#|.....
.#~~~~~#|.....
.#######|.....
........|.....
........|.....
....#...|.#...
....#...|.#...
....#~~~~~#...
....#######...
Note especially the leftmost |: the new squares of water can reach this tile, but cannot stop there. Instead, eventually, they all fall to the right and settle in the reservoir below.
After 10 more squares of water, the bottom reservoir is also full:
......+.......
......|.....#.
.#..#||||...#.
.#..#~~#|.....
.#..#~~#|.....
.#~~~~~#|.....
.#~~~~~#|.....
.#######|.....
........|.....
........|.....
....#~~~~~#...
....#~~~~~#...
....#~~~~~#...
....#######...
Finally, while there is nowhere left for the water to settle, it can reach a few more tiles before overflowing beyond the bottom of the scanned data:
......+....... (line not counted: above minimum y value)
......|.....#.
.#..#||||...#.
.#..#~~#|.....
.#..#~~#|.....
.#~~~~~#|.....
.#~~~~~#|.....
.#######|.....
........|.....
...|||||||||..
...|#~~~~~#|..
...|#~~~~~#|..
...|#~~~~~#|..
...|#######|..
...|.......|.. (line not counted: below maximum y value)
...|.......|.. (line not counted: below maximum y value)
...|.......|.. (line not counted: below maximum y value)
How many tiles can be reached by the water? To prevent counting forever, ignore tiles with a y coordinate smaller than the smallest y coordinate in your scan data or larger than the largest one. Any x coordinate is valid. In this example, the lowest y coordinate given is 1, and the highest is 13, causing the water spring (in row 0) and the water falling off the bottom of the render (in rows 14 through infinity) to be ignored.
So, in the example above, counting both water at rest (~) and other sand tiles the water can hypothetically reach (|), the total number of tiles the water can reach is 57.
How many tiles can the water reach within the range of y values in your scan?
--- Part Two ---
After a very long time, the water spring will run dry. How much water will be retained?
In the example above, water that won't eventually drain out is shown as ~, a total of 29 tiles.
How many water tiles are left after the water spring stops producing water and all remaining water not at rest has drained?

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
<Query Kind="Program" />
enum Field { Empty, Tree, Lumber }
Dictionary<char, Field> mapping = new Dictionary<char, Field>
{
{'.', Field.Empty},
{'|', Field.Tree},
{'#', Field.Lumber},
};
int Width;
int Height;
Field[,] Map;
void Main()
{
Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"18_input.txt")));
//$"Initial state:".Dump();Dump();
int i;
for (i = 0; i < 10; i++)
{
Tick();
//$"After {i+1} minutes:".Dump();Dump();
}
$"After {i} ticks there are {FullCount(Field.Tree)} trees and {FullCount(Field.Lumber)} lumbers, the score is {FullCount(Field.Tree) * FullCount(Field.Lumber)}".Dump();
}
void Dump()
{
StringBuilder b = new StringBuilder();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
if (Map[x, y] == Field.Empty) b.Append('.');
else if (Map[x, y] == Field.Tree) b.Append('|');
else if (Map[x, y] == Field.Lumber) b.Append('#');
else throw new Exception(".");
}
b.AppendLine();
}
b.ToString().Dump();
"".Dump();
}
void Load(string[] input)
{
Width = input[0].Length;
Height = input.Length;
Map = new Field[Width, Height];
for (var x = 0; x < Width; x++) for (var y = 0; y < Height; y++) Map[x, y] = mapping[input[y][x]];
}
void Tick()
{
var d = new Field[Width,Height];
for (var y = 0; y < Height; y++) for (var x = 0; x < Width; x++) Tick(ref Map, ref d, x, y);
Map=d;
}
void Tick(ref Field[,] src, ref Field[,] dst, int x, int y)
{
if (src[x,y] == Field.Empty)
{
dst[x,y] = (Count(ref src, x, y, Field.Tree) >= 3) ? Field.Tree : Field.Empty;
}
else if (src[x, y] == Field.Tree)
{
dst[x, y] = (Count(ref src, x, y, Field.Lumber) >= 3) ? Field.Lumber : Field.Tree;
}
else if (src[x, y] == Field.Lumber)
{
dst[x, y] = (Count(ref src, x, y, Field.Lumber) >= 1 && Count(ref src, x, y, Field.Tree) >= 1) ? Field.Lumber : Field.Empty;
}
}
int Count(ref Field[,] fld, int x, int y, Field s)
{
int c = 0;
if (x > 0 && y > 0 && fld[x - 1, y - 1] == s) c++;
if (x > 0 && fld[x - 1, y ] == s) c++;
if (x > 0 && y < Height-1 && fld[x - 1, y + 1] == s) c++;
if ( y < Height-1 && fld[x , y + 1] == s) c++;
if (x < Width-1 && y < Height-1 && fld[x + 1, y + 1] == s) c++;
if (x < Width-1 && fld[x + 1, y ] == s) c++;
if (x < Width-1 && y > 0 && fld[x + 1, y - 1] == s) c++;
if ( y > 0 && fld[x , y - 1] == s) c++;
return c;
}
int FullCount(Field f)
{
int c = 0;
for (var x = 0; x < Width; x++) for (var y = 0; y < Height; y++) if (Map[x,y]==f)c++;
return c;
}

View File

@ -0,0 +1,129 @@
<Query Kind="Program" />
enum Field { Empty, Tree, Lumber }
Dictionary<char, Field> mapping = new Dictionary<char, Field>
{
{'.', Field.Empty},
{'|', Field.Tree},
{'#', Field.Lumber},
};
int Width;
int Height;
Field[,] Map;
void Main()
{
Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"18_input.txt")));
//$"Initial state:".Dump();Dump();
var history = new Dictionary<string, int>();
int gen=0;
for (;;)
{
Tick();
gen++;
var hash = Hash();
if (history.ContainsKey(hash))
{
int loop = gen-history[hash];
int remaining = 1000000000;
remaining -= gen;
var off = remaining % loop;
for (int i = 0; i < off; i++) { Tick(); gen++; }
$"After {gen} ticks there are {FullCount(Field.Tree)} trees and {FullCount(Field.Lumber)} lumbers, the score is {FullCount(Field.Tree) * FullCount(Field.Lumber)} and the state loops every {loop} generations".Dump();
return;
}
else
{
history[hash] = gen;
}
}
}
string Hash()
{
StringBuilder b = new StringBuilder();
for (int y = 0; y < Height; y++) for (int x = 0; x < Width; x++) b.Append((char)('0'+(int)Map[x, y]));
return b.ToString();
}
void Dump()
{
StringBuilder b = new StringBuilder();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
if (Map[x, y] == Field.Empty) b.Append('.');
else if (Map[x, y] == Field.Tree) b.Append('|');
else if (Map[x, y] == Field.Lumber) b.Append('#');
else throw new Exception(".");
}
b.AppendLine();
}
b.ToString().Dump();
"".Dump();
}
void Load(string[] input)
{
Width = input[0].Length;
Height = input.Length;
Map = new Field[Width, Height];
for (var x = 0; x < Width; x++) for (var y = 0; y < Height; y++) Map[x, y] = mapping[input[y][x]];
}
void Tick()
{
var d = new Field[Width,Height];
for (var y = 0; y < Height; y++) for (var x = 0; x < Width; x++) Tick(ref Map, ref d, x, y);
Map=d;
}
void Tick(ref Field[,] src, ref Field[,] dst, int x, int y)
{
if (src[x,y] == Field.Empty)
{
dst[x,y] = (Count(ref src, x, y, Field.Tree) >= 3) ? Field.Tree : Field.Empty;
}
else if (src[x, y] == Field.Tree)
{
dst[x, y] = (Count(ref src, x, y, Field.Lumber) >= 3) ? Field.Lumber : Field.Tree;
}
else if (src[x, y] == Field.Lumber)
{
dst[x, y] = (Count(ref src, x, y, Field.Lumber) >= 1 && Count(ref src, x, y, Field.Tree) >= 1) ? Field.Lumber : Field.Empty;
}
}
int Count(ref Field[,] fld, int x, int y, Field s)
{
int c = 0;
if (x > 0 && y > 0 && fld[x - 1, y - 1] == s) c++;
if (x > 0 && fld[x - 1, y ] == s) c++;
if (x > 0 && y < Height-1 && fld[x - 1, y + 1] == s) c++;
if ( y < Height-1 && fld[x , y + 1] == s) c++;
if (x < Width-1 && y < Height-1 && fld[x + 1, y + 1] == s) c++;
if (x < Width-1 && fld[x + 1, y ] == s) c++;
if (x < Width-1 && y > 0 && fld[x + 1, y - 1] == s) c++;
if ( y > 0 && fld[x , y - 1] == s) c++;
return c;
}
int FullCount(Field f)
{
int c = 0;
for (var x = 0; x < Width; x++) for (var y = 0; y < Height; y++) if (Map[x,y]==f)c++;
return c;
}

View File

@ -0,0 +1,161 @@
--- Day 18: Settlers of The North Pole ---
On the outskirts of the North Pole base construction project, many Elves are collecting lumber.
The lumber collection area is 50 acres by 50 acres; each acre can be either open ground (.), trees (|), or a lumberyard (#). You take a scan of the area (your puzzle input).
Strange magic is at work here: each minute, the landscape looks entirely different. In exactly one minute, an open acre can fill with trees, a wooded acre can be converted to a lumberyard, or a lumberyard can be cleared to open ground (the lumber having been sent to other projects).
The change to each acre is based entirely on the contents of that acre as well as the number of open, wooded, or lumberyard acres adjacent to it at the start of each minute. Here, "adjacent" means any of the eight acres surrounding that acre. (Acres on the edges of the lumber collection area might have fewer than eight adjacent acres; the missing acres aren't counted.)
In particular:
An open acre will become filled with trees if three or more adjacent acres contained trees. Otherwise, nothing happens.
An acre filled with trees will become a lumberyard if three or more adjacent acres were lumberyards. Otherwise, nothing happens.
An acre containing a lumberyard will remain a lumberyard if it was adjacent to at least one other lumberyard and at least one acre containing trees. Otherwise, it becomes open.
These changes happen across all acres simultaneously, each of them using the state of all acres at the beginning of the minute and changing to their new form by the end of that same minute. Changes that happen during the minute don't affect each other.
For example, suppose the lumber collection area is instead only 10 by 10 acres with this initial configuration:
Initial state:
.#.#...|#.
.....#|##|
.|..|...#.
..|#.....#
#.#|||#|#|
...#.||...
.|....|...
||...#|.#|
|.||||..|.
...#.|..|.
After 1 minute:
.......##.
......|###
.|..|...#.
..|#||...#
..##||.|#|
...#||||..
||...|||..
|||||.||.|
||||||||||
....||..|.
After 2 minutes:
.......#..
......|#..
.|.|||....
..##|||..#
..###|||#|
...#|||||.
|||||||||.
||||||||||
||||||||||
.|||||||||
After 3 minutes:
.......#..
....|||#..
.|.||||...
..###|||.#
...##|||#|
.||##|||||
||||||||||
||||||||||
||||||||||
||||||||||
After 4 minutes:
.....|.#..
...||||#..
.|.#||||..
..###||||#
...###||#|
|||##|||||
||||||||||
||||||||||
||||||||||
||||||||||
After 5 minutes:
....|||#..
...||||#..
.|.##||||.
..####|||#
.|.###||#|
|||###||||
||||||||||
||||||||||
||||||||||
||||||||||
After 6 minutes:
...||||#..
...||||#..
.|.###|||.
..#.##|||#
|||#.##|#|
|||###||||
||||#|||||
||||||||||
||||||||||
||||||||||
After 7 minutes:
...||||#..
..||#|##..
.|.####||.
||#..##||#
||##.##|#|
|||####|||
|||###||||
||||||||||
||||||||||
||||||||||
After 8 minutes:
..||||##..
..|#####..
|||#####|.
||#...##|#
||##..###|
||##.###||
|||####|||
||||#|||||
||||||||||
||||||||||
After 9 minutes:
..||###...
.||#####..
||##...##.
||#....###
|##....##|
||##..###|
||######||
|||###||||
||||||||||
||||||||||
After 10 minutes:
.||##.....
||###.....
||##......
|##.....##
|##.....##
|##....##|
||##.####|
||#####|||
||||#|||||
||||||||||
After 10 minutes, there are 37 wooded acres and 31 lumberyards. Multiplying the number of wooded acres by the number of lumberyards gives the total resource value after ten minutes: 37 * 31 = 1147.
What will the total resource value of the lumber collection area be after 10 minutes?
--- Part Two ---
This important natural resource will need to last for at least thousands of years. Are the Elves collecting this lumber sustainably?
What will the total resource value of the lumber collection area be after 1000000000 minutes?

View File

@ -0,0 +1,50 @@
.|||.#..|##.#||..#.|..|..||||..#|##.##..#...|.....
.|#.|#..##...|#.........#.#..#..|#.|#|##..#.#|..#.
#....#|.#|.###||..#.|...|.|.#........#.|.#.#|..#..
|..|#....|#|...#.#..||.#..||......#.........|....|
.|.|..#|...#.|.###.|...||.|.|..|...|#|.#..|.|..|.|
#.....||.#..|..|..||#.||#..|.||..||##.......#.....
||.#..........|....##...|..#.|..#..#|#.#....#..#.#
.#.#|.|.|.##|..#......|...#||..#.||..|..|#....|##.
#.#..||.|...#|...|..#.#.||#.||.#.|.....|##.|....#.
.#......||.|#......#|#.|...||...||##...#...####.#.
.....#..|..#..#|..#...#.|#...||...#.##.||.|..|.||.
.#|.#.|.....|#..#||..|...|...##.#.###|..|.###.|#..
..#.......#.|#.##....#..|##.#......#|......#..#...
.|..#|.#.....#..||..#.#.|##..|#.||#..|.#..|.|##|#|
##|.#........|#.#.#|..|....|.......#..#|.#.|....#.
....##...|....#..............||.|..#........|.....
##||.|.#...|.#|..#....#..|...|..#..#..|##||.....|.
.|.#...|#.......#...#.#..|#....#|#|#..#|...##..||.
.|..|.|..#...##...||#..##|#|..|...#.....#||...##..
.|...|..||#..#|.|.#...|||.|#.||#|......|#|.#..|#..
|##.....|.|#...#||.....#..#.|.#..|.....||....||..#
|.|#|||.....|||..#......#..||........||.#.#..||#||
#.|.|.#.....#....#.#..#||.||..|.#.|....|...#.#...#
|.|....#.#||...#.....#|#|.|.#......##.|.||...#.||.
|...|...|##........|.|...#...|.........|..##..|.##
|.||..|.#.#|.#||...|.|.....#...#.####|.||||..|||.|
.....#..##..|..#|.||#...|..##...##|....##||.##....
#|##..#|.#..|##...|..#.##.|##.....###.|..#.|..#.|.
|.##..|#...|.|.||.......#..#||.....#|..#||##..#|..
..|.#.#.....##.|#|...#........##......#...#...||..
|.#....###|..|##.#...#|....|..#.....#.##.|..|...||
.....#..#.....|.##......#......|..|...##|.|.#..#||
...##.#.......#|.#..||.#|..#|...#...|||.#.......|#
#|..#|....|||...|..#|....#......#..#...|#.......||
...#|##|..........|..###||..#|...|.##.|.#.#...#...
#|##|.#|#...|..#......||..#.|#|..#..|..#|..#......
#||#.#.....|...|..|##|..#|...##.||..#|.|#||.|..|..
#..#..|.|.||...#|.|.|..|..|..|....#.#||.#.....|#.#
#.|.#..##...|..#.|..#..#..#.#||.#.............#...
..|##|.#|.|......|#...|#.#.....|#|#.#.|...|#......
.|.|.|...#..##..#|###..|#....#..#.#..|||.###|##...
|#...|......|...##..|.|#...#..|.#.........#..##.#.
.|...##||#.....#..#..|..#..#.|#.|.||.##.|....|..#|
|#..|..|.#..||...#...#|..##|||##..|.##||#.#.|....|
.......#......|.....||.#..|#.#.#|#.##....|...|.#..
.....#..|...|..##.....|...#...|.|||.##..|.#||.##|.
..#||...|#.#|#|....#..|||.|##..#|.|.........|....#
..#...|.#...|#..#........#...###..##..##||...|..#.
..|.||.#.....|#..|.##...#.|...|#...#||..####..#.|.
.|.....#....||.#...#.......#|........#...#|#|...|#

View File

@ -0,0 +1,55 @@
<Query Kind="Program" />
class Command { public String Name; public (string, Action<int[], Command>) Op; public int[] Args; public int Dst => Args[2]; public int Arg1 => Args[0]; public int Arg2 => Args[1]; }
(string, Action<int[], Command>)[] Operations = new(string, Action<int[], Command>)[]
{
("addr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + reg[cmd.Arg2]; } ),
("addi", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + cmd.Arg2; } ),
("mulr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * reg[cmd.Arg2]; } ),
("muli", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * cmd.Arg2; } ),
("banr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & reg[cmd.Arg2]; } ),
("bani", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & cmd.Arg2; } ),
("borr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | reg[cmd.Arg2]; } ),
("bori", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | cmd.Arg2; } ),
("setr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1]; } ),
("seti", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1; } ),
("gtir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 > reg[cmd.Arg2] ? 1:0; } ),
("gtri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > cmd.Arg2 ? 1:0; } ),
("gtrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > reg[cmd.Arg2] ? 1:0; } ),
("eqir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 == reg[cmd.Arg2] ? 1:0; } ),
("eqri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == cmd.Arg2 ? 1:0; } ),
("eqrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == reg[cmd.Arg2] ? 1:0; } ),
};
void Main()
{
(var commands, var ipidx) = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"19_input.txt")));
int[] data = new int[255];
for (int gen=0;;gen++)
{
if (data[ipidx]<0 || data[ipidx]>=commands.Count) break;
var cmd = commands[data[ipidx]];
cmd.Op.Item2(data, cmd);
//string.Join(" ", data.Take(6)).Dump();
data[ipidx]++;
}
data[0].Dump();
}
(List<Command>, int) Load(string[] input)
{
return
(
input
.Skip(1)
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => new Command { Name = i.Split(' ')[0], Op=Operations.Single(o => o.Item1.ToLower() == i.Split(' ')[0].ToLower()), Args = i.Split(' ').Skip(1).Select(int.Parse).ToArray() })
.ToList(),
int.Parse(input[0].Split(' ')[1])
);
}

View File

@ -0,0 +1,149 @@
<Query Kind="Program" />
void Main()
{
var b = 10551358;
var a = 0;
for (int i = 1; i <= b; i++) if (b % i == 0)a+=i;
a.Dump();
}
/*
[1-ORIGINAL] [2-SYNTAX] [3-RESOLVE] [4-SIMPLIFY]
#ip 1
00: addi 1 16 1 [1]=[1]+16 JMP 17 JMP 17
01: seti 1 5 5 [5]=1 1: E := 1
02: seti 1 2 3 [3]=1 2: C := 1
03: mulr 5 3 2 [2]=[3]*[5] 3: B := C*E
04: eqrr 2 4 2 [2]=([2]==[4]) if (B = D) A += E
05: addr 2 1 1 [1]=[2]+[1] JMP 6+[2]
06: addi 1 1 1 [1]++ JMP 8
07: addr 5 0 0 [0]=[5]+[0]
08: addi 3 1 3 [3]++ C++
09: gtrr 3 4 2 [2]=([3]>[4]) if (C<=D) JUMP 3
10: addr 1 2 1 [1]=[1]+[2] JMP 11+[2]
11: seti 2 6 1 [1]=2 JMP 3
12: addi 5 1 5 [5]++ E++
13: gtrr 5 4 2 [2]=([5]>[4]) if (E<=D) JUMP 2 else EXIT
14: addr 2 1 1 [1]=[1]+[2] JMP 15+[2]
15: seti 1 8 1 [1]=1 JMP 2
16: mulr 1 1 1 [1]=[1]*[1] EXIT
17: addi 4 2 4 [4]+=2 17: D+=2 |
18: mulr 4 4 4 [4]=[4]*[4] D=D*D |
19: mulr 1 4 4 [4]=[4]*[1] [4]=[4]*19 D*=19 |
20: muli 4 11 4 [4]=[4]*[1] [4]=[4]*11 D*=11 |
21: addi 2 5 2 [2]=[2]+5 B+=5 |
22: mulr 2 1 2 [2]=[2]*[1] [2]=[2]*22 B*=22 |
23: addi 2 12 2 [2]=[2]+12 B+=12 |B := 122
24: addr 4 2 4 [4]=[4]+[2] D+=B |D := 958
25: addr 1 0 1 [1]=[1]+[0] JMP 26+[0] if (A = 0) JUMP 1
26: seti 0 4 1 [1]=0 JMP 1
27: setr 1 4 2 [2]=[1] [2]=[4]+27 B=27 |
28: mulr 2 1 2 [2]=[2]*[1] [2]=[2]*28 B=B*28 |
29: addr 1 2 2 [2]=[2]+[1] [2]=[2]+29 B=B+29 |
30: mulr 1 2 2 [2]=[2]*[1] [2]=[2]*30 B=B*30 |
31: muli 2 14 2 [2]=[2]*14 B=B*14 |
32: mulr 2 1 2 [2]=[2]*[1] [2]=[2]*32 B=B*32 |B := 10550400
33: addr 4 2 4 [4]=[2]+[4] D=B+D |D := 10551358
34: seti 0 3 0 [0]=0 A=0
35: seti 0 7 1 [1]=0 JMP 1 JUMP 1
[5-SHRINK]
JMP 17
1: E := 1
2: C := 1
3: B := C*E
if (B = D) A += E
C++
if (C<=D) JUMP 3
E++
if (E<=D) JUMP 2 else EXIT
17: B = 122
D = 958
if (A = 0) JUMP 1
B = 10550400
D = 10551358
A=0
JUMP 1
[6-REORDER]
if (A=0)
B = 122; D = 958
else
B = 10550400; D = 10551358;
A = 0
E=1
S1: C=1
S2: B=C*E
if (B = D) A += E
C++
if (C<=D) JUMP S2
E++
if (E<=D)
JUMP S1
else
EXIT
[7-LANGUAGE]
if (A=0) B = 122; D = 958;
else B = 10550400; D = 10551358;
A = 0
E = 1
do
{
C=1
do
{
B=C*E
if (B == D) A += E
C++
}
while (C <= D)
E++
}
while (E <= D)
[8-SUGAR]
if (A=0) B = 122; D = 958;
else B = 10550400; D = 10551358;
A = 0
E = 1
for(E=1; E<=D; E++)
{
for(C=1; C<=D; C++)
{
if (C*E == D) A += E
}
}
[9-EXPLANATION]
THE SUM OF ALL FACTORS OF 10551358
*/

View File

@ -0,0 +1,51 @@
--- Day 19: Go With The Flow ---
With the Elves well on their way constructing the North Pole base, you turn your attention back to understanding the inner workings of programming the device.
You can't help but notice that the device's opcodes don't contain any flow control like jump instructions. The device's manual goes on to explain:
"In programs where flow control is required, the instruction pointer can be bound to a register so that it can be manipulated directly. This way, setr/seti can function as absolute jumps, addr/addi can function as relative jumps, and other opcodes can cause truly fascinating effects."
This mechanism is achieved through a declaration like #ip 1, which would modify register 1 so that accesses to it let the program indirectly access the instruction pointer itself. To compensate for this kind of binding, there are now six registers (numbered 0 through 5); the five not bound to the instruction pointer behave as normal. Otherwise, the same rules apply as the last time you worked with this device.
When the instruction pointer is bound to a register, its value is written to that register just before each instruction is executed, and the value of that register is written back to the instruction pointer immediately after each instruction finishes execution. Afterward, move to the next instruction by adding one to the instruction pointer, even if the value in the instruction pointer was just updated by an instruction. (Because of this, instructions must effectively set the instruction pointer to the instruction before the one they want executed next.)
The instruction pointer is 0 during the first instruction, 1 during the second, and so on. If the instruction pointer ever causes the device to attempt to load an instruction outside the instructions defined in the program, the program instead immediately halts. The instruction pointer starts at 0.
It turns out that this new information is already proving useful: the CPU in the device is not very powerful, and a background process is occupying most of its time. You dump the background process' declarations and instructions to a file (your puzzle input), making sure to use the names of the opcodes rather than the numbers.
For example, suppose you have the following program:
#ip 0
seti 5 0 1
seti 6 0 2
addi 0 1 0
addr 1 2 3
setr 1 0 0
seti 8 0 4
seti 9 0 5
When executed, the following instructions are executed. Each line contains the value of the instruction pointer at the time the instruction started, the values of the six registers before executing the instructions (in square brackets), the instruction itself, and the values of the six registers after executing the instruction (also in square brackets).
ip=0 [0, 0, 0, 0, 0, 0] seti 5 0 1 [0, 5, 0, 0, 0, 0]
ip=1 [1, 5, 0, 0, 0, 0] seti 6 0 2 [1, 5, 6, 0, 0, 0]
ip=2 [2, 5, 6, 0, 0, 0] addi 0 1 0 [3, 5, 6, 0, 0, 0]
ip=4 [4, 5, 6, 0, 0, 0] setr 1 0 0 [5, 5, 6, 0, 0, 0]
ip=6 [6, 5, 6, 0, 0, 0] seti 9 0 5 [6, 5, 6, 0, 0, 9]
In detail, when running this program, the following events occur:
The first line (#ip 0) indicates that the instruction pointer should be bound to register 0 in this program. This is not an instruction, and so the value of the instruction pointer does not change during the processing of this line.
The instruction pointer contains 0, and so the first instruction is executed (seti 5 0 1). It updates register 0 to the current instruction pointer value (0), sets register 1 to 5, sets the instruction pointer to the value of register 0 (which has no effect, as the instruction did not modify register 0), and then adds one to the instruction pointer.
The instruction pointer contains 1, and so the second instruction, seti 6 0 2, is executed. This is very similar to the instruction before it: 6 is stored in register 2, and the instruction pointer is left with the value 2.
The instruction pointer is 2, which points at the instruction addi 0 1 0. This is like a relative jump: the value of the instruction pointer, 2, is loaded into register 0. Then, addi finds the result of adding the value in register 0 and the value 1, storing the result, 3, back in register 0. Register 0 is then copied back to the instruction pointer, which will cause it to end up 1 larger than it would have otherwise and skip the next instruction (addr 1 2 3) entirely. Finally, 1 is added to the instruction pointer.
The instruction pointer is 4, so the instruction setr 1 0 0 is run. This is like an absolute jump: it copies the value contained in register 1, 5, into register 0, which causes it to end up in the instruction pointer. The instruction pointer is then incremented, leaving it at 6.
The instruction pointer is 6, so the instruction seti 9 0 5 stores 9 into register 5. The instruction pointer is incremented, causing it to point outside the program, and so the program ends.
What value is left in register 0 when the background process halts?
--- Part Two ---
A new background process immediately spins up in its place. It appears identical, but on closer inspection, you notice that this time, register 0 started with the value 1.
What value is left in register 0 when this new background process halts?

View File

@ -0,0 +1,37 @@
#ip 1
addi 1 16 1
seti 1 5 5
seti 1 2 3
mulr 5 3 2
eqrr 2 4 2
addr 2 1 1
addi 1 1 1
addr 5 0 0
addi 3 1 3
gtrr 3 4 2
addr 1 2 1
seti 2 6 1
addi 5 1 5
gtrr 5 4 2
addr 2 1 1
seti 1 8 1
mulr 1 1 1
addi 4 2 4
mulr 4 4 4
mulr 1 4 4
muli 4 11 4
addi 2 5 2
mulr 2 1 2
addi 2 12 2
addr 4 2 4
addr 1 0 1
seti 0 4 1
setr 1 4 2
mulr 2 1 2
addr 1 2 2
mulr 1 2 2
muli 2 14 2
mulr 2 1 2
addr 4 2 4
seti 0 3 0
seti 0 7 1

View File

@ -0,0 +1,268 @@
<Query Kind="Program">
<Namespace>System.Drawing</Namespace>
</Query>
class Step { public int X,Y; public char Dir; public int P; public List<Step> NextSteps = new List<Step>(); }
int StartX;
int StartY;
int Width;
int Height;
bool[,] Doors;
bool[,] Reachable;
int?[,] Distance;
Step RootStep;
void Main()
{
Load(File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"20_input.txt")).Trim());
//DumpSteps();
//int c=0; VerifySteps(RootStep, ref c);c.Dump();
Walk();
Flood().Dump();
DumpMap();
}
void VerifySteps(Step s, ref int c)
{
for(;;)
{
c++;
if (s.NextSteps.Any(ns => ns.P>=s.P)) throw new Exception();
if (s.NextSteps.Count == 1) { s = s.NextSteps.Single(); continue; }
foreach (var ns in s.NextSteps) VerifySteps(ns, ref c);
return;
}
}
void DumpSteps()
{
StringBuilder b = new StringBuilder();
DumpSteps(RootStep, ref b, 0);
}
void DumpSteps(Step s, ref StringBuilder b, int w)
{
int sscc = 0;
for (; ; )
{
if (s.NextSteps.Count == 0) { b.Append(sscc.ToString()); w += sscc.ToString().Length; break; }
if (s.NextSteps.Count == 1) { sscc++; s = s.NextSteps.Single(); continue; }
{b.Append(sscc.ToString()); w += sscc.ToString().Length;}
int l = w;
foreach (var ns in s.NextSteps)
{
b.ToString().Dump();b.Clear();
b.Append(new string(' ', l));
DumpSteps(ns, ref b, w);
}
return;
}
}
void Load(string d)
{
d = d.Substring(1, d.Length - 2);
StartX = d.Count(c => c == 'W') + 16;
StartY = d.Count(c => c == 'N') + 16;
Width = d.Count(c => c == 'E') + 16 + StartX;
Height = d.Count(c => c == 'S') + 16 + StartY;
Doors = new bool[Width * 2 + 16, Height + 16];
Distance = new int?[Width, Height];
Reachable = new bool[Width, Height];
RootStep = Parse(d);
}
Step Parse(string d)
{
Step r = null;
Parse(new List<Step>(), ref d, ref r);
if (d!="")throw new Exception();
return r;
}
List<Step> Parse(List<Step> source, ref string d, ref Step root)
{
List<Step> lasts = source;
for (; ; )
{
if (d == "") return lasts;
if (d[0] == '|') return lasts;
if (d[0] == ')') return lasts;
if (d[0] == '(')
{
d = d.Substring(1);
List<Step> newlasts = new List<Step>();
for (;;)
{
if (d[0] == ')') { d = d.Substring(1); newlasts.AddRange(lasts); break; }
var n = Parse(lasts, ref d, ref root);
newlasts.AddRange(n);
if (d[0] == '|') { d = d.Substring(1); continue; }
if (d[0] == ')') { d = d.Substring(1); break; }
throw new Exception();
}
lasts = newlasts;
continue;
}
if (d[0] == 'N') { var s = new Step { X = 00, Y = -1, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
if (d[0] == 'E') { var s = new Step { X = +1, Y = 00, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
if (d[0] == 'S') { var s = new Step { X = 00, Y = +1, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
if (d[0] == 'W') { var s = new Step { X = -1, Y = 00, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
throw new Exception();
}
}
void Walk()
{
Stack<(int x,int y, Step s)> work = new Stack<(int x, int y, Step s)>();
work.Push((StartX, StartY, RootStep));
while(work.Any())
{
(var x, var y, var step) = work.Pop();
Reachable[x,y]=true;
if (step.Dir == 'N') Doors[x*2, y] = true;
if (step.Dir == 'E') Doors[x*2+1,y] = true;
if (step.Dir == 'S') Doors[x*2,y+1] = true;
if (step.Dir == 'W') Doors[x*2-1,y] = true;
x += step.X;
y += step.Y;
foreach (var ns in step.NextSteps) work.Push((x,y,ns));
//DumpMap();
}
}
int Flood()
{
Stack<(int x, int y)> work = new Stack<(int x, int y)>();
work.Push((StartX, StartY));
Distance[StartX, StartY]=0;
while (work.Any())
{
(var x, var y) = work.Pop();
if (Doors[x * 2, y ] && (Distance[x+0,y-1] == null || Distance[x+0,y-1].Value > Distance[x,y]+1)) { Distance[x+0,y-1] = Distance[x,y]+1; work.Push((x+0,y-1)); }
if (Doors[x * 2 + 1, y ] && (Distance[x+1,y+0] == null || Distance[x+1,y+0].Value > Distance[x,y]+1)) { Distance[x+1,y+0] = Distance[x,y]+1; work.Push((x+1,y+0)); }
if (Doors[x * 2, y + 1] && (Distance[x+0,y+1] == null || Distance[x+0,y+1].Value > Distance[x,y]+1)) { Distance[x+0,y+1] = Distance[x,y]+1; work.Push((x+0,y+1)); }
if (Doors[x * 2 - 1, y ] && (Distance[x-1,y+0] == null || Distance[x-1,y+0].Value > Distance[x,y]+1)) { Distance[x-1,y+0] = Distance[x,y]+1; work.Push((x-1,y+0)); }
}
int max = int.MinValue;
for (int xx = 0; xx < Width; xx++) for (int yy = 0; yy < Height; yy++) max = Math.Max(Distance[xx,yy] ?? max, max);
return max;
}
void DumpMap()
{
var ww = Width * 2 + 1;
var hh = Height * 2 + 1;
char[,] m = new char[Width * 2 + 1, Height * 2 + 1];
int _sx = 0;
int _ex = 0;
int _sy = 0;
int _ey = 0;
for (int xx = 0; ; xx++)
{
var ff = false;
for (int yy = 0; yy < Height; yy++) if (Reachable[xx, yy]) ff = true;
if (ff) { _sx = xx; break; }
}
for (int xx = Width - 1; ; xx--)
{
var ff = false;
for (int yy = 0; yy < Height; yy++) if (Reachable[xx, yy]) ff = true;
if (ff) { _ex = xx; break; }
}
for (int yy = 0; ; yy++)
{
var ff = false;
for (int xx = 0; xx < Width; xx++) if (Reachable[xx, yy]) ff = true;
if (ff) { _sy = yy; break; }
}
for (int yy = Height - 1; ; yy--)
{
var ff = false;
for (int xx = 0; xx < Width; xx++) if (Reachable[xx, yy]) ff = true;
if (ff) { _ey = yy; break; }
}
var sy = _sy * 2 + 1 - 1;
var sx = _sx * 2 + 1 - 1;
var ey = _ey * 2 + 1 + 2;
var ex = _ex * 2 + 1 + 2;
for (int xx = sx; xx <= ex; xx++) for (int yy = sy; yy <= ey; yy++) m[xx, yy] = '?';
for (int xx = sx; xx <= ex; xx += 2) for (int yy = sy; yy <= ey; yy++) m[xx, yy] = '#';
for (int yy = sy; yy <= ey; yy += 2) for (int xx = sx; xx <= ex; xx++) m[xx, yy] = '#';
for (int ix = _sx; ix <= _ex; ix++)
{
for (int iy = _sy; iy <= _ey; iy++)
{
if (Doors[ix * 2, iy ]) { m[ix*2+1, iy*2+1-1]='-'; }
if (Doors[ix * 2 + 1, iy ]) { m[ix*2+1+1, iy*2+1]='|'; }
if (Doors[ix * 2, iy + 1]) { m[ix*2+1, iy*2+1+1]='-'; }
if (Doors[ix * 2 - 1, iy ]) { m[ix*2+1-1, iy*2+1]='|'; }
if (Distance[ix,iy]!= null) { m[ix*2+1, iy*2+1]='.'; }
}
}
m[StartX * 2 + 1, StartY * 2 + 1] = 'X';
int f = 6;
Bitmap bmp = new Bitmap(((ex-sx)+(ex-sx)/2) * f, ((ey-sy)+(ey-sy)/2) * f, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Magenta);
for (int yy = sy; yy < ey; yy++)
{
for (int xx = sx; xx < ex; xx++)
{
int rx = (xx - sx) + (xx - sx) / 2;
int ry = (yy - sy) + (yy - sy) / 2;
int rw = ((xx - sx) % 2) + 1;
int rh = ((yy - sy) % 2) + 1;
var c = Brushes.Magenta;
if (m[xx, yy] == '#') c = Brushes.Black;
if (m[xx, yy] == 'X') c = Brushes.Green;
if (m[xx, yy] == '.') c = Brushes.White;
if (m[xx, yy] == '?') c = Brushes.DarkGray;
if (m[xx, yy] == '|') c = Brushes.LightGray;
if (m[xx, yy] == '-') c = Brushes.LightGray;
g.FillRectangle(c, rx*f, ry*f, rw*f, rh*f);
}
}
}
bmp.Dump();
//StringBuilder b = new StringBuilder();
//for (int yy = 0; yy < hh; yy++)
//{
// for (int xx = 0; xx < ww; xx++)
// {
// b.Append(m[xx,yy]);
// }
// b.AppendLine();
//}
//b.ToString().Dump();
}

View File

@ -0,0 +1,268 @@
<Query Kind="Program">
<Namespace>System.Drawing</Namespace>
</Query>
class Step { public int X,Y; public char Dir; public int P; public List<Step> NextSteps = new List<Step>(); }
int StartX;
int StartY;
int Width;
int Height;
bool[,] Doors;
bool[,] Reachable;
int?[,] Distance;
Step RootStep;
void Main()
{
Load(File.ReadAllText(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"20_input.txt")).Trim());
//DumpSteps();
//int c=0; VerifySteps(RootStep, ref c);c.Dump();
Walk();
Flood().Dump();
DumpMap();
}
void VerifySteps(Step s, ref int c)
{
for(;;)
{
c++;
if (s.NextSteps.Any(ns => ns.P>=s.P)) throw new Exception();
if (s.NextSteps.Count == 1) { s = s.NextSteps.Single(); continue; }
foreach (var ns in s.NextSteps) VerifySteps(ns, ref c);
return;
}
}
void DumpSteps()
{
StringBuilder b = new StringBuilder();
DumpSteps(RootStep, ref b, 0);
}
void DumpSteps(Step s, ref StringBuilder b, int w)
{
int sscc = 0;
for (; ; )
{
if (s.NextSteps.Count == 0) { b.Append(sscc.ToString()); w += sscc.ToString().Length; break; }
if (s.NextSteps.Count == 1) { sscc++; s = s.NextSteps.Single(); continue; }
{b.Append(sscc.ToString()); w += sscc.ToString().Length;}
int l = w;
foreach (var ns in s.NextSteps)
{
b.ToString().Dump();b.Clear();
b.Append(new string(' ', l));
DumpSteps(ns, ref b, w);
}
return;
}
}
void Load(string d)
{
d = d.Substring(1, d.Length - 2);
StartX = d.Count(c => c == 'W') + 16;
StartY = d.Count(c => c == 'N') + 16;
Width = d.Count(c => c == 'E') + 16 + StartX;
Height = d.Count(c => c == 'S') + 16 + StartY;
Doors = new bool[Width * 2 + 16, Height + 16];
Distance = new int?[Width, Height];
Reachable = new bool[Width, Height];
RootStep = Parse(d);
}
Step Parse(string d)
{
Step r = null;
Parse(new List<Step>(), ref d, ref r);
if (d!="")throw new Exception();
return r;
}
List<Step> Parse(List<Step> source, ref string d, ref Step root)
{
List<Step> lasts = source;
for (; ; )
{
if (d == "") return lasts;
if (d[0] == '|') return lasts;
if (d[0] == ')') return lasts;
if (d[0] == '(')
{
d = d.Substring(1);
List<Step> newlasts = new List<Step>();
for (;;)
{
if (d[0] == ')') { d = d.Substring(1); newlasts.AddRange(lasts); break; }
var n = Parse(lasts, ref d, ref root);
newlasts.AddRange(n);
if (d[0] == '|') { d = d.Substring(1); continue; }
if (d[0] == ')') { d = d.Substring(1); break; }
throw new Exception();
}
lasts = newlasts;
continue;
}
if (d[0] == 'N') { var s = new Step { X = 00, Y = -1, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
if (d[0] == 'E') { var s = new Step { X = +1, Y = 00, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
if (d[0] == 'S') { var s = new Step { X = 00, Y = +1, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
if (d[0] == 'W') { var s = new Step { X = -1, Y = 00, Dir=d[0], P=d.Length, }; lasts.ForEach(l => l.NextSteps.Add(s)); lasts = new List<Step> {s}; d = d.Substring(1); root = root ?? s; continue; }
throw new Exception();
}
}
void Walk()
{
Stack<(int x,int y, Step s)> work = new Stack<(int x, int y, Step s)>();
work.Push((StartX, StartY, RootStep));
while(work.Any())
{
(var x, var y, var step) = work.Pop();
Reachable[x,y]=true;
if (step.Dir == 'N') Doors[x*2, y] = true;
if (step.Dir == 'E') Doors[x*2+1,y] = true;
if (step.Dir == 'S') Doors[x*2,y+1] = true;
if (step.Dir == 'W') Doors[x*2-1,y] = true;
x += step.X;
y += step.Y;
foreach (var ns in step.NextSteps) work.Push((x,y,ns));
//DumpMap();
}
}
int Flood()
{
Stack<(int x, int y)> work = new Stack<(int x, int y)>();
work.Push((StartX, StartY));
Distance[StartX, StartY]=0;
while (work.Any())
{
(var x, var y) = work.Pop();
if (Doors[x * 2, y ] && (Distance[x+0,y-1] == null || Distance[x+0,y-1].Value > Distance[x,y]+1)) { Distance[x+0,y-1] = Distance[x,y]+1; work.Push((x+0,y-1)); }
if (Doors[x * 2 + 1, y ] && (Distance[x+1,y+0] == null || Distance[x+1,y+0].Value > Distance[x,y]+1)) { Distance[x+1,y+0] = Distance[x,y]+1; work.Push((x+1,y+0)); }
if (Doors[x * 2, y + 1] && (Distance[x+0,y+1] == null || Distance[x+0,y+1].Value > Distance[x,y]+1)) { Distance[x+0,y+1] = Distance[x,y]+1; work.Push((x+0,y+1)); }
if (Doors[x * 2 - 1, y ] && (Distance[x-1,y+0] == null || Distance[x-1,y+0].Value > Distance[x,y]+1)) { Distance[x-1,y+0] = Distance[x,y]+1; work.Push((x-1,y+0)); }
}
int c = 0;
for (int xx = 0; xx < Width; xx++) for (int yy = 0; yy < Height; yy++) if ((Distance[xx,yy] ?? 0) >= 1000)c++;
return c;
}
void DumpMap()
{
var ww = Width * 2 + 1;
var hh = Height * 2 + 1;
char[,] m = new char[Width * 2 + 1, Height * 2 + 1];
int _sx = 0;
int _ex = 0;
int _sy = 0;
int _ey = 0;
for (int xx = 0; ; xx++)
{
var ff = false;
for (int yy = 0; yy < Height; yy++) if (Reachable[xx, yy]) ff = true;
if (ff) { _sx = xx; break; }
}
for (int xx = Width - 1; ; xx--)
{
var ff = false;
for (int yy = 0; yy < Height; yy++) if (Reachable[xx, yy]) ff = true;
if (ff) { _ex = xx; break; }
}
for (int yy = 0; ; yy++)
{
var ff = false;
for (int xx = 0; xx < Width; xx++) if (Reachable[xx, yy]) ff = true;
if (ff) { _sy = yy; break; }
}
for (int yy = Height - 1; ; yy--)
{
var ff = false;
for (int xx = 0; xx < Width; xx++) if (Reachable[xx, yy]) ff = true;
if (ff) { _ey = yy; break; }
}
var sy = _sy * 2 + 1 - 1;
var sx = _sx * 2 + 1 - 1;
var ey = _ey * 2 + 1 + 2;
var ex = _ex * 2 + 1 + 2;
for (int xx = sx; xx <= ex; xx++) for (int yy = sy; yy <= ey; yy++) m[xx, yy] = '?';
for (int xx = sx; xx <= ex; xx += 2) for (int yy = sy; yy <= ey; yy++) m[xx, yy] = '#';
for (int yy = sy; yy <= ey; yy += 2) for (int xx = sx; xx <= ex; xx++) m[xx, yy] = '#';
for (int ix = _sx; ix <= _ex; ix++)
{
for (int iy = _sy; iy <= _ey; iy++)
{
if (Doors[ix * 2, iy ]) { m[ix*2+1, iy*2+1-1]='-'; }
if (Doors[ix * 2 + 1, iy ]) { m[ix*2+1+1, iy*2+1]='|'; }
if (Doors[ix * 2, iy + 1]) { m[ix*2+1, iy*2+1+1]='-'; }
if (Doors[ix * 2 - 1, iy ]) { m[ix*2+1-1, iy*2+1]='|'; }
if (Distance[ix,iy]!= null) { m[ix*2+1, iy*2+1]='.'; }
}
}
m[StartX * 2 + 1, StartY * 2 + 1] = 'X';
int f = 6;
Bitmap bmp = new Bitmap(((ex-sx)+(ex-sx)/2) * f, ((ey-sy)+(ey-sy)/2) * f, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Magenta);
for (int yy = sy; yy < ey; yy++)
{
for (int xx = sx; xx < ex; xx++)
{
int rx = (xx - sx) + (xx - sx) / 2;
int ry = (yy - sy) + (yy - sy) / 2;
int rw = ((xx - sx) % 2) + 1;
int rh = ((yy - sy) % 2) + 1;
var c = Brushes.Magenta;
if (m[xx, yy] == '#') c = Brushes.Black;
if (m[xx, yy] == 'X') c = Brushes.Green;
if (m[xx, yy] == '.') c = Brushes.White;
if (m[xx, yy] == '?') c = Brushes.DarkGray;
if (m[xx, yy] == '|') c = Brushes.LightGray;
if (m[xx, yy] == '-') c = Brushes.LightGray;
g.FillRectangle(c, rx*f, ry*f, rw*f, rh*f);
}
}
}
bmp.Dump();
//StringBuilder b = new StringBuilder();
//for (int yy = 0; yy < hh; yy++)
//{
// for (int xx = 0; xx < ww; xx++)
// {
// b.Append(m[xx,yy]);
// }
// b.AppendLine();
//}
//b.ToString().Dump();
}

View File

@ -0,0 +1,144 @@
--- Day 20: A Regular Map ---
While you were learning about instruction pointers, the Elves made considerable progress. When you look up, you discover that the North Pole base construction project has completely surrounded you.
The area you are in is made up entirely of rooms and doors. The rooms are arranged in a grid, and rooms only connect to adjacent rooms when a door is present between them.
For example, drawing rooms as ., walls as #, doors as | or -, your current position as X, and where north is up, the area you're in might look like this:
#####
#.|.#
#-###
#.|X#
#####
You get the attention of a passing construction Elf and ask for a map. "I don't have time to draw out a map of this place - it's huge. Instead, I can give you directions to every room in the facility!" He writes down some directions on a piece of parchment and runs off. In the example above, the instructions might have been ^WNE$, a regular expression or "regex" (your puzzle input).
The regex matches routes (like WNE for "west, north, east") that will take you from your current room through various doors in the facility. In aggregate, the routes will take you through every door in the facility at least once; mapping out all of these routes will let you build a proper map and find your way around.
^ and $ are at the beginning and end of your regex; these just mean that the regex doesn't match anything outside the routes it describes. (Specifically, ^ matches the start of the route, and $ matches the end of it.) These characters will not appear elsewhere in the regex.
The rest of the regex matches various sequences of the characters N (north), S (south), E (east), and W (west). In the example above, ^WNE$ matches only one route, WNE, which means you can move west, then north, then east from your current position. Sequences of letters like this always match that exact route in the same order.
Sometimes, the route can branch. A branch is given by a list of options separated by pipes (|) and wrapped in parentheses. So, ^N(E|W)N$ contains a branch: after going north, you must choose to go either east or west before finishing your route by going north again. By tracing out the possible routes after branching, you can determine where the doors are and, therefore, where the rooms are in the facility.
For example, consider this regex: ^ENWWW(NEEE|SSE(EE|N))$
This regex begins with ENWWW, which means that from your current position, all routes must begin by moving east, north, and then west three times, in that order. After this, there is a branch. Before you consider the branch, this is what you know about the map so far, with doors you aren't sure about marked with a ?:
#?#?#?#?#
?.|.|.|.?
#?#?#?#-#
?X|.?
#?#?#
After this point, there is (NEEE|SSE(EE|N)). This gives you exactly two options: NEEE and SSE(EE|N). By following NEEE, the map now looks like this:
#?#?#?#?#
?.|.|.|.?
#-#?#?#?#
?.|.|.|.?
#?#?#?#-#
?X|.?
#?#?#
Now, only SSE(EE|N) remains. Because it is in the same parenthesized group as NEEE, it starts from the same room NEEE started in. It states that starting from that point, there exist doors which will allow you to move south twice, then east; this ends up at another branch. After that, you can either move east twice or north once. This information fills in the rest of the doors:
#?#?#?#?#
?.|.|.|.?
#-#?#?#?#
?.|.|.|.?
#-#?#?#-#
?.?.?X|.?
#-#-#?#?#
?.|.|.|.?
#?#?#?#?#
Once you've followed all possible routes, you know the remaining unknown parts are all walls, producing a finished map of the facility:
#########
#.|.|.|.#
#-#######
#.|.|.|.#
#-#####-#
#.#.#X|.#
#-#-#####
#.|.|.|.#
#########
Sometimes, a list of options can have an empty option, like (NEWS|WNSE|). This means that routes at this point could effectively skip the options in parentheses and move on immediately. For example, consider this regex and the corresponding map:
^ENNWSWW(NEWS|)SSSEEN(WNSE|)EE(SWEN|)NNN$
###########
#.|.#.|.#.#
#-###-#-#-#
#.|.|.#.#.#
#-#####-#-#
#.#.#X|.#.#
#-#-#####-#
#.#.|.|.|.#
#-###-###-#
#.|.|.#.|.#
###########
This regex has one main route which, at three locations, can optionally include additional detours and be valid: (NEWS|), (WNSE|), and (SWEN|). Regardless of which option is taken, the route continues from the position it is left at after taking those steps. So, for example, this regex matches all of the following routes (and more that aren't listed here):
ENNWSWWSSSEENEENNN
ENNWSWWNEWSSSSEENEENNN
ENNWSWWNEWSSSSEENEESWENNNN
ENNWSWWSSSEENWNSEEENNN
By following the various routes the regex matches, a full map of all of the doors and rooms in the facility can be assembled.
To get a sense for the size of this facility, you'd like to determine which room is furthest from you: specifically, you would like to find the room for which the shortest path to that room would require passing through the most doors.
In the first example (^WNE$), this would be the north-east corner 3 doors away.
In the second example (^ENWWW(NEEE|SSE(EE|N))$), this would be the south-east corner 10 doors away.
In the third example (^ENNWSWW(NEWS|)SSSEEN(WNSE|)EE(SWEN|)NNN$), this would be the north-east corner 18 doors away.
Here are a few more examples:
Regex: ^ESSWWN(E|NNENN(EESS(WNSE|)SSS|WWWSSSSE(SW|NNNE)))$
Furthest room requires passing 23 doors
#############
#.|.|.|.|.|.#
#-#####-###-#
#.#.|.#.#.#.#
#-#-###-#-#-#
#.#.#.|.#.|.#
#-#-#-#####-#
#.#.#.#X|.#.#
#-#-#-###-#-#
#.|.#.|.#.#.#
###-#-###-#-#
#.|.#.|.|.#.#
#############
Regex: ^WSSEESWWWNW(S|NENNEEEENN(ESSSSW(NWSW|SSEN)|WSWWN(E|WWS(E|SS))))$
Furthest room requires passing 31 doors
###############
#.|.|.|.#.|.|.#
#-###-###-#-#-#
#.|.#.|.|.#.#.#
#-#########-#-#
#.#.|.|.|.|.#.#
#-#-#########-#
#.#.#.|X#.|.#.#
###-#-###-#-#-#
#.|.#.#.|.#.|.#
#-###-#####-###
#.|.#.|.|.#.#.#
#-#-#####-#-#-#
#.#.|.|.|.#.|.#
###############
What is the largest number of doors you would be required to pass through to reach a room? That is, find the room for which the shortest path from your starting location to that room would require passing through the most doors; what is the fewest doors you can pass through to reach it?
--- Part Two ---
Okay, so the facility is big.
How many rooms have a shortest path from your current location that pass through at least 1000 doors?

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,97 @@
<Query Kind="Program" />
class Command { public String Name; public (string, Action<long[], Command>) Op; public int[] Args; public int Dst => Args[2]; public int Arg1 => Args[0]; public int Arg2 => Args[1]; }
(string, Action<long[], Command>)[] Operations = new(string, Action<long[], Command>)[]
{
("addr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + reg[cmd.Arg2]; } ),
("addi", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + cmd.Arg2; } ),
("mulr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * reg[cmd.Arg2]; } ),
("muli", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * cmd.Arg2; } ),
("banr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & reg[cmd.Arg2]; } ),
("bani", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & cmd.Arg2; } ),
("borr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | reg[cmd.Arg2]; } ),
("bori", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | cmd.Arg2; } ),
("setr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1]; } ),
("seti", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1; } ),
("gtir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 > reg[cmd.Arg2] ? 1:0; } ),
("gtri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > cmd.Arg2 ? 1:0; } ),
("gtrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > reg[cmd.Arg2] ? 1:0; } ),
("eqir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 == reg[cmd.Arg2] ? 1:0; } ),
("eqri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == cmd.Arg2 ? 1:0; } ),
("eqrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == reg[cmd.Arg2] ? 1:0; } ),
};
void Main()
{
(var commands, var ipidx) = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"21_input.txt")));
long[] data = new long[6];
data[0]=2;
for (int gen=0;;gen++)
{
if (data[ipidx] < 0 || data[ipidx] >= commands.Count) { break; }
var ip=data[ipidx];
var cmd = commands[(int)ip];
if (ip == 28) { data[4].Dump(); return; } // this is if ([4] == [0]) EXIT
cmd.Op.Item2(data, cmd);
data[ipidx]++;
//$"{ip:00}:{cmd.Op.Item1} [{string.Join(" ", data.Take(6).Select(i => $"{i,-10}"))}]".Dump();
}
data[0].Dump();
}
(List<Command>, int) Load(string[] input)
{
return
(
input
.Skip(1)
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => new Command { Name = i.Split(' ')[0], Op=Operations.Single(o => o.Item1.ToLower() == i.Split(' ')[0].ToLower()), Args = i.Split(' ').Skip(1).Select(int.Parse).ToArray() })
.ToList(),
int.Parse(input[0].Split(' ')[1])
);
}
/*
#ip 1
00: seti 123 0 4 [4] = 123 [4] = 123 [4] = 123
01: bani 4 456 4 [4] = [4] & 456 1: [4] &= 456 [4] &= 456
02: eqri 4 72 4 [4] = [4] == 72 if ([4]<>72) JMP 1 if ([4]<>72) ERROR
03: addr 4 1 1 IP = IP + [4]
04: seti 0 0 1 IP = 0
05: seti 0 2 4 [4] = 0 [4] = 0 [4] = 0 for([4] = 0;;)
06: bori 4 65536 3 [3] = [4] | 65536 6: [3] = [4] | 65536 6: [3] = [4] | 65536 [3] = [4] | 65536
07: seti 10552971 1 4 [4] = 10552971 [4] = 10552971 [4] = 10552971 [4] = 10552971
08: bani 3 255 5 [5] = [3] & 255 8: [5] = [3] & 255 8: [5] = [3] & 255 for(;;)
09: addr 4 5 4 [4] = [4] + [5] [4] += [5] [4] += [5] [5] = [3] & 255
10: bani 4 16777215 4 [4] = [4] & 16777215 [4] &= 16777215 [4] &= 16777215 [4] += [5]
11: muli 4 65899 4 [4] = [4] * 65899 [4] *= 65899 [4] *= 65899 [4] &= 16777215
12: bani 4 16777215 4 [4] = [4] & 16777215 [4] &= 16777215 [4] &= 16777215 [4] *= 65899
13: gtir 256 3 5 [5] = 256 > [3] if (256 > [3]) JMP 28 if (256 > [3] && [4] == [0]) EXIT [4] &= 16777215
14: addr 5 1 1 IP = IP + [5] if (256 > [3]) JMP 6 if (256 > [3] && [4] == [0]) EXIT
15: addi 1 1 1 IP = IP + 1 if (256 > [3]) BREAK
16: seti 27 7 1 IP = 27
17: seti 0 1 5 [5] = 0 [5] = 0 [5] = 0
18: addi 5 1 2 [2] = [5] + 1 18: [2] = [5] + 1 18: [2] = [5] + 1 for([5]=0;;)
19: muli 2 256 2 [2] = [2] * 256 [2] *= 256 [2] *= 256 [2] = ([5] + 1) * 256
20: gtrr 2 3 2 [2] = [2] > [3] if ([2] > [3]) JMP 26 if ([2] > [3]) [3] = [5]; JMP 8
21: addr 2 1 1 IP = IP + [2] else [5]++ JMP 18 if ([2] > [3]) [3] = [5]; BREAK
22: addi 1 1 1 IP = IP + 1 else [5]++ CONTINUE
23: seti 25 0 1 IP = 25
24: addi 5 1 5 [5] = [5] + 1 [5]++
25: seti 17 2 1 IP = 17 JMP 18
26: setr 5 7 3 [3] = [5] 26: [3] = [5]
27: seti 7 8 1 IP = 7 JMP 8
28: eqrr 4 0 5 [5] = [4] == [0] 28: if ([4] == [0]) EXIT
29: addr 5 1 1 IP = IP + [5] else JMP 6
30: seti 5 0 1 IP = 5
*/

View File

@ -0,0 +1,112 @@
<Query Kind="Program" />
class Command { public String Name; public (string, Action<long[], Command>) Op; public int[] Args; public int Dst => Args[2]; public int Arg1 => Args[0]; public int Arg2 => Args[1]; }
(string, Action<long[], Command>)[] Operations = new(string, Action<long[], Command>)[]
{
("addr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + reg[cmd.Arg2]; } ),
("addi", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] + cmd.Arg2; } ),
("mulr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * reg[cmd.Arg2]; } ),
("muli", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] * cmd.Arg2; } ),
("banr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & reg[cmd.Arg2]; } ),
("bani", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] & cmd.Arg2; } ),
("borr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | reg[cmd.Arg2]; } ),
("bori", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] | cmd.Arg2; } ),
("setr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1]; } ),
("seti", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1; } ),
("gtir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 > reg[cmd.Arg2] ? 1:0; } ),
("gtri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > cmd.Arg2 ? 1:0; } ),
("gtrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] > reg[cmd.Arg2] ? 1:0; } ),
("eqir", (reg, cmd) => { reg[cmd.Dst] = cmd.Arg1 == reg[cmd.Arg2] ? 1:0; } ),
("eqri", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == cmd.Arg2 ? 1:0; } ),
("eqrr", (reg, cmd) => { reg[cmd.Dst] = reg[cmd.Arg1] == reg[cmd.Arg2] ? 1:0; } ),
};
void Main()
{
(var commands, var ipidx) = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"21_input.txt")));
long best = -1;
HashSet<long> possibilities = new HashSet<long>();
HashSet<string> history = new HashSet<string>();
long[] data = new long[6];
data[0]=2;
for (long gen=0;;gen++)
{
if (data[ipidx] < 0 || data[ipidx] >= commands.Count) { break; }
var ip=data[ipidx];
var cmd = commands[(int)ip];
if (ip == 28)
{
if (!possibilities.Contains(data[4]))
{
possibilities.Add(data[4]);
//data[4].Dump();
best = data[4];
}
var statehash = string.Join(";", data);
if (history.Contains(statehash)) { $"Loop after {gen} generations. Best is {best} with {possibilities.Count} candidates and a history size of {history.Count}".Dump(); return; }
history.Add(statehash);
} // this is if ([4] == [0]) EXIT
cmd.Op.Item2(data, cmd);
data[ipidx]++;
//$"{ip:00}:{cmd.Op.Item1} [{string.Join(" ", data.Take(6).Select(i => $"{i,-10}"))}]".Dump();
}
data[0].Dump();
}
(List<Command>, int) Load(string[] input)
{
return
(
input
.Skip(1)
.Where(i => !string.IsNullOrWhiteSpace(i))
.Select(i => new Command { Name = i.Split(' ')[0], Op=Operations.Single(o => o.Item1.ToLower() == i.Split(' ')[0].ToLower()), Args = i.Split(' ').Skip(1).Select(int.Parse).ToArray() })
.ToList(),
int.Parse(input[0].Split(' ')[1])
);
}
/*
#ip 1
00: seti 123 0 4 [4] = 123 [4] = 123 [4] = 123
01: bani 4 456 4 [4] = [4] & 456 1: [4] &= 456 [4] &= 456
02: eqri 4 72 4 [4] = [4] == 72 if ([4]<>72) JMP 1 if ([4]<>72) ERROR
03: addr 4 1 1 IP = IP + [4]
04: seti 0 0 1 IP = 0
05: seti 0 2 4 [4] = 0 [4] = 0 [4] = 0 for([4] = 0;;)
06: bori 4 65536 3 [3] = [4] | 65536 6: [3] = [4] | 65536 6: [3] = [4] | 65536 [3] = [4] | 65536
07: seti 10552971 1 4 [4] = 10552971 [4] = 10552971 [4] = 10552971 [4] = 10552971
08: bani 3 255 5 [5] = [3] & 255 8: [5] = [3] & 255 8: [5] = [3] & 255 for(;;)
09: addr 4 5 4 [4] = [4] + [5] [4] += [5] [4] += [5] [5] = [3] & 255
10: bani 4 16777215 4 [4] = [4] & 16777215 [4] &= 16777215 [4] &= 16777215 [4] += [5]
11: muli 4 65899 4 [4] = [4] * 65899 [4] *= 65899 [4] *= 65899 [4] &= 16777215
12: bani 4 16777215 4 [4] = [4] & 16777215 [4] &= 16777215 [4] &= 16777215 [4] *= 65899
13: gtir 256 3 5 [5] = 256 > [3] if (256 > [3]) JMP 28 if (256 > [3] && [4] == [0]) EXIT [4] &= 16777215
14: addr 5 1 1 IP = IP + [5] if (256 > [3]) JMP 6 if (256 > [3] && [4] == [0]) EXIT
15: addi 1 1 1 IP = IP + 1 if (256 > [3]) BREAK
16: seti 27 7 1 IP = 27
17: seti 0 1 5 [5] = 0 [5] = 0 [5] = 0
18: addi 5 1 2 [2] = [5] + 1 18: [2] = [5] + 1 18: [2] = [5] + 1 for([5]=0;;)
19: muli 2 256 2 [2] = [2] * 256 [2] *= 256 [2] *= 256 [2] = ([5] + 1) * 256
20: gtrr 2 3 2 [2] = [2] > [3] if ([2] > [3]) JMP 26 if ([2] > [3]) [3] = [5]; JMP 8
21: addr 2 1 1 IP = IP + [2] else [5]++ JMP 18 if ([2] > [3]) [3] = [5]; BREAK
22: addi 1 1 1 IP = IP + 1 else [5]++ CONTINUE
23: seti 25 0 1 IP = 25
24: addi 5 1 5 [5] = [5] + 1 [5]++
25: seti 17 2 1 IP = 17 JMP 18
26: setr 5 7 3 [3] = [5] 26: [3] = [5]
27: seti 7 8 1 IP = 7 JMP 8
28: eqrr 4 0 5 [5] = [4] == [0] 28: if ([4] == [0]) EXIT
29: addr 5 1 1 IP = IP + [5] else JMP 6
30: seti 5 0 1 IP = 5
*/

View File

@ -0,0 +1,29 @@
--- Day 21: Chronal Conversion ---
You should have been watching where you were going, because as you wander the new North Pole base, you trip and fall into a very deep hole!
Just kidding. You're falling through time again.
If you keep up your current pace, you should have resolved all of the temporal anomalies by the next time the device activates. Since you have very little interest in browsing history in 500-year increments for the rest of your life, you need to find a way to get back to your present time.
After a little research, you discover two important facts about the behavior of the device:
First, you discover that the device is hard-wired to always send you back in time in 500-year increments. Changing this is probably not feasible.
Second, you discover the activation system (your puzzle input) for the time travel module. Currently, it appears to run forever without halting.
If you can cause the activation system to halt at a specific moment, maybe you can make the device send you so far back in time that you cause an integer underflow in time itself and wrap around back to your current time!
The device executes the program as specified in manual section one and manual section two.
Your goal is to figure out how the program works and cause it to halt. You can only control register 0; every other register begins at 0 as usual.
Because time travel is a dangerous activity, the activation system begins with a few instructions which verify that bitwise AND (via bani) does a numeric operation and not an operation as if the inputs were interpreted as strings. If the test fails, it enters an infinite loop re-running the test instead of allowing the program to execute normally. If the test passes, the program continues, and assumes that all other bitwise operations (banr, bori, and borr) also interpret their inputs as numbers. (Clearly, the Elves who wrote this system were worried that someone might introduce a bug while trying to emulate this system with a scripting language.)
What is the lowest non-negative integer value for register 0 that causes the program to halt after executing the fewest instructions? (Executing the same instruction multiple times counts as multiple instructions executed.)
--- Part Two ---
In order to determine the timing window for your underflow exploit, you also need an upper bound:
What is the lowest non-negative integer value for register 0 that causes the program to halt after executing the most instructions? (The program must actually halt; running forever does not count as halting.)

View File

@ -0,0 +1,32 @@
#ip 1
seti 123 0 4
bani 4 456 4
eqri 4 72 4
addr 4 1 1
seti 0 0 1
seti 0 2 4
bori 4 65536 3
seti 10552971 1 4
bani 3 255 5
addr 4 5 4
bani 4 16777215 4
muli 4 65899 4
bani 4 16777215 4
gtir 256 3 5
addr 5 1 1
addi 1 1 1
seti 27 7 1
seti 0 1 5
addi 5 1 2
muli 2 256 2
gtrr 2 3 2
addr 2 1 1
addi 1 1 1
seti 25 0 1
addi 5 1 5
seti 17 2 1
setr 5 7 3
seti 7 8 1
eqrr 4 0 5
addr 5 1 1
seti 5 0 1

View File

@ -0,0 +1,50 @@
<Query Kind="Program" />
void Main()
{
var input = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"22_input.txt"));
var depth = long.Parse(input[0].Split(' ')[1]);
(long x, long y) target = ( long.Parse(input[1].Split(' ')[1].Split(',')[0]), long.Parse(input[1].Split(' ')[1].Split(',')[1]) );
long[,] map = new long[target.x + 1, target.y + 1]; // erosion_level
for (long xx = 0; xx <= target.x; xx++) map[xx, 0] = ((xx * 16807) + depth) % 20183;
for (long yy = 0; yy <= target.y; yy++) map[0, yy] = ((yy * 48271) + depth) % 20183;
for (long x = 1; x <= target.x; x++)
{
for (long y = 1; y <= target.y; y++)
{
map[x, y] = ((map[x-1, y] * map[x, y-1]) + depth) % 20183;
}
}
long risk = 0;
for (long x = 0; x <= target.x; x++)
{
for (long y = 0; y <= target.y; y++)
{
if (x == 0 && y == 0) continue;
if (x == target.x && y == target.y) continue;
risk += map[x,y] % 3;
}
}
risk.Dump();
//StringBuilder b = new StringBuilder();
//for (long y = 0; y <= target.y; y++)
//{
// for (long x = 0; x <= target.x; x++)
// {
// if (map[x, y] % 3 == 0) b.Append('.');
// if (map[x, y] % 3 == 1) b.Append('=');
// if (map[x, y] % 3 == 2) b.Append('|');
// }
// b.AppendLine();
//}
//b.ToString().Dump();
}
// Define other methods and classes here

View File

@ -0,0 +1,88 @@
<Query Kind="Program" />
void Main()
{
const int expand = 2;
const int T_ROCKY = 0;
const int T_WET = 1;
const int T_NARROW = 2;
const int G_NEITHER = 0;
const int G_TORCH = 1;
const int G_GEAR = 2;
var input = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"22_input.txt"));
var depth = long.Parse(input[0].Split(' ')[1]);
(long x, long y) target = (long.Parse(input[1].Split(' ')[1].Split(',')[0]), long.Parse(input[1].Split(' ')[1].Split(',')[1]));
var width = target.x * expand + 1;
var height = target.y * expand + 1;
long[,] map = new long[width, height]; // erosion_level
for (long xx = 0; xx < width; xx++) map[xx, 0] = ((xx * 16807) + depth) % 20183;
for (long yy = 0; yy < height; yy++) map[0, yy] = ((yy * 48271) + depth) % 20183;
for (long x = 1; x < width; x++)
{
for (long y = 1; y < height; y++)
{
map[x, y] = ((map[x-1, y] * map[x, y-1]) + depth) % 20183;
}
}
int[,] typemap = new int[width, height];
for (long x = 0; x < width; x++) for (long y = 0; y < height; y++) typemap[x,y] = (int)(map[x,y] % 3);
typemap[target.x, target.y] = 0;
bool[,] validity = new bool[3, 3];
validity[T_ROCKY, G_NEITHER] = false;
validity[T_ROCKY, G_TORCH] = true;
validity[T_ROCKY, G_GEAR] = true;
validity[T_WET, G_NEITHER] = true;
validity[T_WET, G_TORCH] = false;
validity[T_WET, G_GEAR] = true;
validity[T_NARROW, G_NEITHER] = true;
validity[T_NARROW, G_TORCH] = true;
validity[T_NARROW, G_GEAR] = false;
int?[,,] distance = new int?[width, height, 3];
distance[0,0,1] = 0;
var work = new Queue<(int, int, int)>();
work.Enqueue((0,0,1));
while (work.Any())
{
(var px, var py, var pt) = work.Dequeue();
var dist = distance[px,py,pt];
//LEFT
if (px>0 && validity[typemap[px-1,py], pt] && (distance[px-1,py,pt]==null || distance[px-1,py,pt]>dist+1)) { distance[px-1,py,pt]=dist+1; if (!work.Contains((px-1,py,pt))) work.Enqueue((px-1,py,pt)); }
//UP
if (py>0 && validity[typemap[px,py-1], pt] && (distance[px,py-1,pt]==null || distance[px,py-1,pt]>dist+1)) { distance[px,py-1,pt]=dist+1; if (!work.Contains((px,py-1,pt))) work.Enqueue((px,py-1,pt)); }
//RIGHT
if (px+1<width && validity[typemap[px+1,py], pt] && (distance[px+1,py,pt]==null || distance[px+1,py,pt]>dist+1)) { distance[px+1,py,pt]=dist+1; if (!work.Contains((px+1,py,pt))) work.Enqueue((px+1,py,pt)); }
//DOWN
if (py+1<height && validity[typemap[px,py+1], pt] && (distance[px,py+1,pt]==null || distance[px,py+1,pt]>dist+1)) { distance[px,py+1,pt]=dist+1; if (!work.Contains((px,py+1,pt))) work.Enqueue((px,py+1,pt)); }
//NEITHER
if (pt != G_NEITHER && validity[typemap[px,py], G_NEITHER] && (distance[px,py,G_NEITHER]==null || distance[px,py,G_NEITHER]>dist+7)) { distance[px,py,G_NEITHER]=dist+7; if (!work.Contains((px,py,G_NEITHER))) work.Enqueue((px,py,G_NEITHER)); }
//TORCH
if (pt != G_TORCH && validity[typemap[px,py], G_TORCH] && (distance[px,py,G_TORCH]==null || distance[px,py,G_TORCH]>dist+7)) { distance[px,py,G_TORCH]=dist+7; if (!work.Contains((px,py,G_TORCH))) work.Enqueue((px,py,G_TORCH)); }
//GEAR
if (pt != G_GEAR && validity[typemap[px,py], G_GEAR] && (distance[px,py,G_GEAR]==null || distance[px,py,G_GEAR]>dist+7)) { distance[px,py,G_GEAR]=dist+7; if (!work.Contains((px,py,G_GEAR))) work.Enqueue((px,py,G_GEAR)); }
}
distance[target.x, target.y, G_TORCH].Dump();
}
// Define other methods and classes here

View File

@ -0,0 +1,336 @@
--- Day 22: Mode Maze ---
This is it, your final stop: the year -483. It's snowing and dark outside; the only light you can see is coming from a small cottage in the distance. You make your way there and knock on the door.
A portly man with a large, white beard answers the door and invites you inside. For someone living near the North Pole in -483, he must not get many visitors, but he doesn't act surprised to see you. Instead, he offers you some milk and cookies.
After talking for a while, he asks a favor of you. His friend hasn't come back in a few hours, and he's not sure where he is. Scanning the region briefly, you discover one life signal in a cave system nearby; his friend must have taken shelter there. The man asks if you can go there to retrieve his friend.
The cave is divided into square regions which are either dominantly rocky, narrow, or wet (called its type). Each region occupies exactly one coordinate in X,Y format where X and Y are integers and zero or greater. (Adjacent regions can be the same type.)
The scan (your puzzle input) is not very detailed: it only reveals the depth of the cave system and the coordinates of the target. However, it does not reveal the type of each region. The mouth of the cave is at 0,0.
The man explains that due to the unusual geology in the area, there is a method to determine any region's type based on its erosion level. The erosion level of a region can be determined from its geologic index. The geologic index can be determined using the first rule that applies from the list below:
The region at 0,0 (the mouth of the cave) has a geologic index of 0.
The region at the coordinates of the target has a geologic index of 0.
If the region's Y coordinate is 0, the geologic index is its X coordinate times 16807.
If the region's X coordinate is 0, the geologic index is its Y coordinate times 48271.
Otherwise, the region's geologic index is the result of multiplying the erosion levels of the regions at X-1,Y and X,Y-1.
A region's erosion level is its geologic index plus the cave system's depth, all modulo 20183. Then:
If the erosion level modulo 3 is 0, the region's type is rocky.
If the erosion level modulo 3 is 1, the region's type is wet.
If the erosion level modulo 3 is 2, the region's type is narrow.
For example, suppose the cave system's depth is 510 and the target's coordinates are 10,10. Using % to represent the modulo operator, the cavern would look as follows:
At 0,0, the geologic index is 0. The erosion level is (0 + 510) % 20183 = 510. The type is 510 % 3 = 0, rocky.
At 1,0, because the Y coordinate is 0, the geologic index is 1 * 16807 = 16807. The erosion level is (16807 + 510) % 20183 = 17317. The type is 17317 % 3 = 1, wet.
At 0,1, because the X coordinate is 0, the geologic index is 1 * 48271 = 48271. The erosion level is (48271 + 510) % 20183 = 8415. The type is 8415 % 3 = 0, rocky.
At 1,1, neither coordinate is 0 and it is not the coordinate of the target, so the geologic index is the erosion level of 0,1 (8415) times the erosion level of 1,0 (17317), 8415 * 17317 = 145722555. The erosion level is (145722555 + 510) % 20183 = 1805. The type is 1805 % 3 = 2, narrow.
At 10,10, because they are the target's coordinates, the geologic index is 0. The erosion level is (0 + 510) % 20183 = 510. The type is 510 % 3 = 0, rocky.
Drawing this same cave system with rocky as ., wet as =, narrow as |, the mouth as M, the target as T, with 0,0 in the top-left corner, X increasing to the right, and Y increasing downward, the top-left corner of the map looks like this:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Before you go in, you should determine the risk level of the area. For the the rectangle that has a top-left corner of region 0,0 and a bottom-right corner of the region containing the target, add up the risk level of each individual region: 0 for rocky regions, 1 for wet regions, and 2 for narrow regions.
In the cave system above, because the mouth is at 0,0 and the target is at 10,10, adding up the risk level of all regions with an X coordinate from 0 to 10 and a Y coordinate from 0 to 10, this total is 114.
What is the total risk level for the smallest rectangle that includes 0,0 and the target's coordinates?
--- Part Two ---
Okay, it's time to go rescue the man's friend.
As you leave, he hands you some tools: a torch and some climbing gear. You can't equip both tools at once, but you can choose to use neither.
Tools can only be used in certain regions:
In rocky regions, you can use the climbing gear or the torch. You cannot use neither (you'll likely slip and fall).
In wet regions, you can use the climbing gear or neither tool. You cannot use the torch (if it gets wet, you won't have a light source).
In narrow regions, you can use the torch or neither tool. You cannot use the climbing gear (it's too bulky to fit).
You start at 0,0 (the mouth of the cave) with the torch equipped and must reach the target coordinates as quickly as possible. The regions with negative X or Y are solid rock and cannot be traversed. The fastest route might involve entering regions beyond the X or Y coordinate of the target.
You can move to an adjacent region (up, down, left, or right; never diagonally) if your currently equipped tool allows you to enter that region. Moving to an adjacent region takes one minute. (For example, if you have the torch equipped, you can move between rocky and narrow regions, but cannot enter wet regions.)
You can change your currently equipped tool or put both away if your new equipment would be valid for your current region. Switching to using the climbing gear, torch, or neither always takes seven minutes, regardless of which tools you start with. (For example, if you are in a rocky region, you can switch from the torch to the climbing gear, but you cannot switch to neither.)
Finally, once you reach the target, you need the torch equipped before you can find him in the dark. The target is always in a rocky region, so if you arrive there with climbing gear equipped, you will need to spend seven minutes switching to your torch.
For example, using the same cave system as above, starting in the top left corner (0,0) and moving to the bottom right corner (the target, 10,10) as quickly as possible, one possible route is as follows, with your current position marked X:
Initially:
X=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Down:
M=.|=.|.|=.|=|=.
X|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Right:
M=.|=.|.|=.|=|=.
.X=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Switch from using the torch to neither tool:
M=.|=.|.|=.|=|=.
.X=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Right 3:
M=.|=.|.|=.|=|=.
.|=|X|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Switch from using neither tool to the climbing gear:
M=.|=.|.|=.|=|=.
.|=|X|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Down 7:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..X==..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Right:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..=X=..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Down 3:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||.X.|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Right:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||..X|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Down:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.X..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Right 4:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===T===||
=|||...|==..|=.|
=.=|=.=..=X||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Up 2:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===X===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
Switch from using the climbing gear to the torch:
M=.|=.|.|=.|=|=.
.|=|=|||..|.=...
.==|....||=..|==
=.|....|.==.|==.
=|..==...=.|==..
=||.=.=||=|=..|=
|.=.===|||..=..|
|..==||=.|==|===
.=..===..=|.|||.
.======|||=|=.|=
.===|=|===X===||
=|||...|==..|=.|
=.=|=.=..=.||==|
||=|=...|==.=|==
|=.=||===.|||===
||.|==.|.|.||=||
This is tied with other routes as the fastest way to reach the target: 45 minutes. In it, 21 minutes are spent switching tools (three times, seven minutes each) and the remaining 24 minutes are spent moving.
What is the fewest number of minutes you can take to reach the target?

View File

@ -0,0 +1,2 @@
depth: 5355
target: 14,796

View File

@ -0,0 +1,16 @@
<Query Kind="Statements" />
var input = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"23_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(p => new
{
X = int.Parse(p.Split(' ')[0].Substring(5).Split(',')[0].Trim(new[] { ' ', '<', '>', ',' })),
Y = int.Parse(p.Split(' ')[0].Substring(5).Split(',')[1].Trim(new[] { ' ', '<', '>', ',' })),
Z = int.Parse(p.Split(' ')[0].Substring(5).Split(',')[2].Trim(new[] { ' ', '<', '>', ',' })),
R = int.Parse(p.Split(' ')[1].Substring(2).Trim(new[] { ' ', '<', '>', ',' })),
})
.ToList();
var max = input.OrderByDescending(p => p.R).First();
input.Count(i => Math.Abs(i.X-max.X) + Math.Abs(i.Y-max.Y) + Math.Abs(i.Z-max.Z) <= max.R).Dump();

View File

@ -0,0 +1,77 @@
<Query Kind="Statements" />
class C3 { public long X, Y, Z; }
void Main()
{
var input = File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"23_input.txt"))
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(p => new
{
X = long.Parse(p.Split(' ')[0].Substring(5).Split(',')[0].Trim(new[] { ' ', '<', '>', ',' })),
Y = long.Parse(p.Split(' ')[0].Substring(5).Split(',')[1].Trim(new[] { ' ', '<', '>', ',' })),
Z = long.Parse(p.Split(' ')[0].Substring(5).Split(',')[2].Trim(new[] { ' ', '<', '>', ',' })),
R = long.Parse(p.Split(' ')[1].Substring(2).Trim(new[] { ' ', '<', '>', ',' })),
})
.ToList();
var x_min = input.Min(i => i.X);
var x_max = input.Max(i => i.X);
var y_min = input.Min(i => i.Y);
var y_max = input.Max(i => i.Y);
var z_min = input.Min(i => i.Z);
var z_max = input.Max(i => i.Z);
long dist = 1;
for (; dist < x_max - x_min; dist *= 2) ;
long target_count = 0;
C3 best = null;
long? best_val = null;
for (; dist > 0;)
{
target_count = 0;
best = null;
best_val = null;
for (long x = x_min; x <= x_max; x += dist)
{
for (long y = y_min; y <= y_max; y += dist)
{
for (long z = z_min; z <= z_max; z += dist)
{
var count = 0;
foreach (var b in input)
{
var calc = Math.Abs(x - b.X) + Math.Abs(y - b.Y) + Math.Abs(z - b.Z);
if ((calc - b.R) / dist <= 0) count++;
}
if (count > target_count)
{
target_count = count;
best_val = Math.Abs(x) + Math.Abs(y) + Math.Abs(z);
best = new C3 { X = x, Y = y, Z = z };
}
else if (count == target_count)
{
if (Math.Abs(x) + Math.Abs(y) + Math.Abs(z) < best_val)
{
best_val = Math.Abs(x) + Math.Abs(y) + Math.Abs(z);
best = new C3 { X = x, Y = y, Z = z };
}
}
}
}
}
x_min = best.X - dist;
x_max = best.X + dist;
y_min = best.Y - dist;
y_max = best.Y + dist;
z_min = best.Z - dist;
z_max = best.Z + dist;
dist /= 2;
}
best_val.Dump();
}

View File

@ -0,0 +1,60 @@
--- Day 23: Experimental Emergency Teleportation ---
Using your torch to search the darkness of the rocky cavern, you finally locate the man's friend: a small reindeer.
You're not sure how it got so far in this cave. It looks sick - too sick to walk - and too heavy for you to carry all the way back. Sleighs won't be invented for another 1500 years, of course.
The only option is experimental emergency teleportation.
You hit the "experimental emergency teleportation" button on the device and push I accept the risk on no fewer than 18 different warning messages. Immediately, the device deploys hundreds of tiny nanobots which fly around the cavern, apparently assembling themselves into a very specific formation. The device lists the X,Y,Z position (pos) for each nanobot as well as its signal radius (r) on its tiny screen (your puzzle input).
Each nanobot can transmit signals to any integer coordinate which is a distance away from it less than or equal to its signal radius (as measured by Manhattan distance). Coordinates a distance away of less than or equal to a nanobot's signal radius are said to be in range of that nanobot.
Before you start the teleportation process, you should determine which nanobot is the strongest (that is, which has the largest signal radius) and then, for that nanobot, the total number of nanobots that are in range of it, including itself.
For example, given the following nanobots:
pos=<0,0,0>, r=4
pos=<1,0,0>, r=1
pos=<4,0,0>, r=3
pos=<0,2,0>, r=1
pos=<0,5,0>, r=3
pos=<0,0,3>, r=1
pos=<1,1,1>, r=1
pos=<1,1,2>, r=1
pos=<1,3,1>, r=1
The strongest nanobot is the first one (position 0,0,0) because its signal radius, 4 is the largest. Using that nanobot's location and signal radius, the following nanobots are in or out of range:
The nanobot at 0,0,0 is distance 0 away, and so it is in range.
The nanobot at 1,0,0 is distance 1 away, and so it is in range.
The nanobot at 4,0,0 is distance 4 away, and so it is in range.
The nanobot at 0,2,0 is distance 2 away, and so it is in range.
The nanobot at 0,5,0 is distance 5 away, and so it is not in range.
The nanobot at 0,0,3 is distance 3 away, and so it is in range.
The nanobot at 1,1,1 is distance 3 away, and so it is in range.
The nanobot at 1,1,2 is distance 4 away, and so it is in range.
The nanobot at 1,3,1 is distance 5 away, and so it is not in range.
In this example, in total, 7 nanobots are in range of the nanobot with the largest signal radius.
Find the nanobot with the largest signal radius. How many nanobots are in range of its signals?
--- Part Two ---
Now, you just need to figure out where to position yourself so that you're actually teleported when the nanobots activate.
To increase the probability of success, you need to find the coordinate which puts you in range of the largest number of nanobots. If there are multiple, choose one closest to your position (0,0,0, measured by manhattan distance).
For example, given the following nanobot formation:
pos=<10,12,12>, r=2
pos=<12,14,12>, r=2
pos=<16,12,12>, r=4
pos=<14,14,14>, r=6
pos=<50,50,50>, r=200
pos=<10,10,10>, r=5
Many coordinates are in range of some of the nanobots in this formation. However, only the coordinate 12,12,12 is in range of the most nanobots: it is in range of the first five, but is not in range of the nanobot at 10,10,10. (All other coordinates are in range of fewer than five nanobots.) This coordinate's distance from 0,0,0 is 36.
Find the coordinates that are in range of the largest number of nanobots. What is the shortest manhattan distance between any of those points and 0,0,0?

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
<Query Kind="Program" />
class ArmyGroup
{
public string Source;
public int UnitCount;
public int HitPoints;
public List<string> Weakness = new List<string>();
public List<string> Immunity = new List<string>();
public string DamageType;
public int DamageValue;
public int Initiative;
public int EffectivePower => UnitCount * DamageValue;
public bool Alive => UnitCount>0;
public ArmyGroup NextTarget = null;
public bool IsTargeted = false;
}
void Main()
{
var groups = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"24_input.txt")));
for(int gen=0;;gen++)
{
if (!(groups.GroupBy(g => g.Source).All(g => g.Any(a => a.Alive)))) break;
Fight(groups);
}
groups
.GroupBy(g => g.Source)
.Select(g => (g.Key, g.Sum(a => a.UnitCount)))
.Dump();
}
List<ArmyGroup> Load(string[] lines)
{
return new[]
{
lines.Skip(1).TakeWhile(l => !string.IsNullOrWhiteSpace(l)).Select(l => LoadSingle("ImmuneSystem", l)),
lines.Skip(1).SkipWhile(l => !string.IsNullOrWhiteSpace(l)).Skip(2).Select(l => LoadSingle("Infection", l))
}
.SelectMany(p=>p)
.ToList();
}
ArmyGroup LoadSingle(string src, string line)
{
var ag = new ArmyGroup { Source=src };
var i1 = line.IndexOf(' ');
ag.UnitCount = int.Parse(line.Substring(0, i1).Trim());
line = line.Substring(i1).Trim().Substring(15).Trim();
var i6 = line.IndexOf(' ');
ag.HitPoints = int.Parse(line.Substring(0, i6).Trim());
line = line.Substring(i6).Trim().Substring(11).Trim();
var i2 = line.IndexOf(')');
if (i2 != -1)
{
var spec0 = line.Substring(1, i2).TrimEnd(')');
line = line.Substring(1).Substring(i2).Substring(1).Trim();
foreach (var spc in spec0.Split(';').Select(p => p.Trim()))
{
if (spc.StartsWith("weak to")) ag.Weakness.AddRange(spc.Substring("weak to".Length).Trim().Split(',').Select(p => p.Trim()));
if (spc.StartsWith("immune to")) ag.Immunity.AddRange(spc.Substring("immune to".Length).Trim().Split(',').Select(p => p.Trim()));
}
}
line = line.Substring(25).Trim();
var i3 = line.IndexOf(' ');
ag.DamageValue = int.Parse(line.Substring(0, i3).Trim());
line = line.Substring(i3).Trim().Trim();
var i4 = line.IndexOf(' ');
ag.DamageType = line.Substring(0, i4).Trim();
line = line.Substring(i4).Trim().Trim();
var i5 = line.LastIndexOf(' ');
ag.Initiative = int.Parse(line.Substring(i5).Trim());
return ag;
}
void Fight(List<ArmyGroup> groups)
{
// PHASE 1
foreach (var g in groups) { g.IsTargeted=false; g.NextTarget=null; }
foreach (var g in groups.Where(g => g.Alive).OrderByDescending(g => g.EffectivePower).ThenByDescending(g => g.Initiative))
{
SelectTarget(g, groups);
}
// PHASE 2
foreach (var g in groups.Where(g => g.Alive).OrderByDescending(g => g.Initiative).Where(g => g.NextTarget!=null))
{
Attack(g, g.NextTarget);
}
}
void SelectTarget(ArmyGroup att, List<ArmyGroup> groups)
{
att.NextTarget = groups
.Where(g => g.Alive)
.Where(g => g.Source != att.Source)
.Where(g => !g.IsTargeted)
.Where(g => CalcFullDamage(att, g)>0)
.OrderByDescending(g => CalcFullDamage(att, g))
.ThenByDescending(g => g.EffectivePower)
.ThenByDescending(g => g.Initiative)
.FirstOrDefault();
if (att.NextTarget != null) att.NextTarget.IsTargeted = true;
}
int CalcFullDamage(ArmyGroup att, ArmyGroup def)
{
if (att.UnitCount==0) return 0;
var dmg = att.EffectivePower;
if (def.Immunity.Contains(att.DamageType)) return 0;
if (def.Weakness.Contains(att.DamageType)) return dmg*2;
return dmg;
}
void Attack(ArmyGroup att, ArmyGroup def)
{
var dmg = CalcFullDamage(att, def);
def.UnitCount -= (int)(dmg / def.HitPoints);
if (def.UnitCount<0) def.UnitCount = 0;
}

View File

@ -0,0 +1,148 @@
<Query Kind="Program" />
class ArmyGroup
{
public string Source;
public int UnitCount;
public int HitPoints;
public List<string> Weakness = new List<string>();
public List<string> Immunity = new List<string>();
public string DamageType;
public int DamageValue;
public int Initiative;
public int EffectivePower => UnitCount * DamageValue;
public bool Alive => UnitCount>0;
public ArmyGroup NextTarget = null;
public bool IsTargeted = false;
}
void Main() => Enumerable.Range(0, int.MaxValue).Select(Calc).First(b=>b>0).Dump();
int Calc(int boost)
{
var groups = Load(File.ReadAllLines(Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), @"24_input.txt")));
foreach(var g in groups.Where(p => p.Source=="ImmuneSystem")) g.DamageValue += boost;
for (int gen = 0; ; gen++)
{
if (!(groups.GroupBy(g => g.Source).All(g => g.Any(a => a.Alive)))) break;
bool dmg = Fight(groups);
if (!dmg) return 0;
}
return groups.Where(p => p.Source=="ImmuneSystem").Sum(p => p.UnitCount) - groups.Where(p => p.Source=="Infection").Sum(p => p.UnitCount);
}
List<ArmyGroup> Load(string[] lines)
{
return new[]
{
lines.Skip(1).TakeWhile(l => !string.IsNullOrWhiteSpace(l)).Select(l => LoadSingle("ImmuneSystem", l)),
lines.Skip(1).SkipWhile(l => !string.IsNullOrWhiteSpace(l)).Skip(2).Select(l => LoadSingle("Infection", l))
}
.SelectMany(p=>p)
.ToList();
}
ArmyGroup LoadSingle(string src, string line)
{
var ag = new ArmyGroup { Source=src };
var i1 = line.IndexOf(' ');
ag.UnitCount = int.Parse(line.Substring(0, i1).Trim());
line = line.Substring(i1).Trim().Substring(15).Trim();
var i6 = line.IndexOf(' ');
ag.HitPoints = int.Parse(line.Substring(0, i6).Trim());
line = line.Substring(i6).Trim().Substring(11).Trim();
var i2 = line.IndexOf(')');
if (i2 != -1)
{
var spec0 = line.Substring(1, i2).TrimEnd(')');
line = line.Substring(1).Substring(i2).Substring(1).Trim();
foreach (var spc in spec0.Split(';').Select(p => p.Trim()))
{
if (spc.StartsWith("weak to")) ag.Weakness.AddRange(spc.Substring("weak to".Length).Trim().Split(',').Select(p => p.Trim()));
if (spc.StartsWith("immune to")) ag.Immunity.AddRange(spc.Substring("immune to".Length).Trim().Split(',').Select(p => p.Trim()));
}
}
line = line.Substring(25).Trim();
var i3 = line.IndexOf(' ');
ag.DamageValue = int.Parse(line.Substring(0, i3).Trim());
line = line.Substring(i3).Trim().Trim();
var i4 = line.IndexOf(' ');
ag.DamageType = line.Substring(0, i4).Trim();
line = line.Substring(i4).Trim().Trim();
var i5 = line.LastIndexOf(' ');
ag.Initiative = int.Parse(line.Substring(i5).Trim());
return ag;
}
bool Fight(List<ArmyGroup> groups)
{
// PHASE 1
foreach (var g in groups) { g.IsTargeted=false; g.NextTarget=null; }
foreach (var g in groups.Where(g => g.Alive).OrderByDescending(g => g.EffectivePower).ThenByDescending(g => g.Initiative))
{
SelectTarget(g, groups);
}
// PHASE 2
bool d = false;
foreach (var g in groups.Where(g => g.Alive).OrderByDescending(g => g.Initiative).Where(g => g.NextTarget!=null))
{
var a = Attack(g, g.NextTarget);
if (a)d=true;
}
return d;
}
void SelectTarget(ArmyGroup att, List<ArmyGroup> groups)
{
att.NextTarget = groups
.Where(g => g.Alive)
.Where(g => g.Source != att.Source)
.Where(g => !g.IsTargeted)
.Where(g => CalcFullDamage(att, g)>0)
.OrderByDescending(g => CalcFullDamage(att, g))
.ThenByDescending(g => g.EffectivePower)
.ThenByDescending(g => g.Initiative)
.FirstOrDefault();
if (att.NextTarget != null) att.NextTarget.IsTargeted = true;
}
int CalcFullDamage(ArmyGroup att, ArmyGroup def)
{
if (att.UnitCount==0) return 0;
var dmg = att.EffectivePower;
if (def.Immunity.Contains(att.DamageType)) return 0;
if (def.Weakness.Contains(att.DamageType)) return dmg*2;
return dmg;
}
bool Attack(ArmyGroup att, ArmyGroup def)
{
var dmg = CalcFullDamage(att, def);
var kill = (int)(dmg / def.HitPoints);
def.UnitCount -= kill;
if (def.UnitCount<0) def.UnitCount = 0;
return kill>0;
}

View File

@ -0,0 +1,311 @@
--- Day 24: Immune System Simulator 20XX ---
After a weird buzzing noise, you appear back at the man's cottage. He seems relieved to see his friend, but quickly notices that the little reindeer caught some kind of cold while out exploring.
The portly man explains that this reindeer's immune system isn't similar to regular reindeer immune systems:
The immune system and the infection each have an army made up of several groups; each group consists of one or more identical units. The armies repeatedly fight until only one army has units remaining.
Units within a group all have the same hit points (amount of damage a unit can take before it is destroyed), attack damage (the amount of damage each unit deals), an attack type, an initiative (higher initiative units attack first and win ties), and sometimes weaknesses or immunities. Here is an example group:
18 units each with 729 hit points (weak to fire; immune to cold, slashing)
with an attack that does 8 radiation damage at initiative 10
Each group also has an effective power: the number of units in that group multiplied by their attack damage. The above group has an effective power of 18 * 8 = 144. Groups never have zero or negative units; instead, the group is removed from combat.
Each fight consists of two phases: target selection and attacking.
During the target selection phase, each group attempts to choose one target. In decreasing order of effective power, groups choose their targets; in a tie, the group with the higher initiative chooses first. The attacking group chooses to target the group in the enemy army to which it would deal the most damage (after accounting for weaknesses and immunities, but not accounting for whether the defending group has enough units to actually receive all of that damage).
If an attacking group is considering two defending groups to which it would deal equal damage, it chooses to target the defending group with the largest effective power; if there is still a tie, it chooses the defending group with the highest initiative. If it cannot deal any defending groups damage, it does not choose a target. Defending groups can only be chosen as a target by one attacking group.
At the end of the target selection phase, each group has selected zero or one groups to attack, and each group is being attacked by zero or one groups.
During the attacking phase, each group deals damage to the target it selected, if any. Groups attack in decreasing order of initiative, regardless of whether they are part of the infection or the immune system. (If a group contains no units, it cannot attack.)
The damage an attacking group deals to a defending group depends on the attacking group's attack type and the defending group's immunities and weaknesses. By default, an attacking group would deal damage equal to its effective power to the defending group. However, if the defending group is immune to the attacking group's attack type, the defending group instead takes no damage; if the defending group is weak to the attacking group's attack type, the defending group instead takes double damage.
The defending group only loses whole units from damage; damage is always dealt in such a way that it kills the most units possible, and any remaining damage to a unit that does not immediately kill it is ignored. For example, if a defending group contains 10 units with 10 hit points each and receives 75 damage, it loses exactly 7 units and is left with 3 units at full health.
After the fight is over, if both armies still contain units, a new fight begins; combat only ends once one army has lost all of its units.
For example, consider the following armies:
Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with
an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning,
slashing) with an attack that does 25 slashing damage at initiative 3
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack
that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire,
cold) with an attack that does 12 slashing damage at initiative 4
If these armies were to enter combat, the following fights, including details during the target selection and attacking phases, would take place:
Immune System:
Group 1 contains 17 units
Group 2 contains 989 units
Infection:
Group 1 contains 801 units
Group 2 contains 4485 units
Infection group 1 would deal defending group 1 185832 damage
Infection group 1 would deal defending group 2 185832 damage
Infection group 2 would deal defending group 2 107640 damage
Immune System group 1 would deal defending group 1 76619 damage
Immune System group 1 would deal defending group 2 153238 damage
Immune System group 2 would deal defending group 1 24725 damage
Infection group 2 attacks defending group 2, killing 84 units
Immune System group 2 attacks defending group 1, killing 4 units
Immune System group 1 attacks defending group 2, killing 51 units
Infection group 1 attacks defending group 1, killing 17 units
Immune System:
Group 2 contains 905 units
Infection:
Group 1 contains 797 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 184904 damage
Immune System group 2 would deal defending group 1 22625 damage
Immune System group 2 would deal defending group 2 22625 damage
Immune System group 2 attacks defending group 1, killing 4 units
Infection group 1 attacks defending group 2, killing 144 units
Immune System:
Group 2 contains 761 units
Infection:
Group 1 contains 793 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 183976 damage
Immune System group 2 would deal defending group 1 19025 damage
Immune System group 2 would deal defending group 2 19025 damage
Immune System group 2 attacks defending group 1, killing 4 units
Infection group 1 attacks defending group 2, killing 143 units
Immune System:
Group 2 contains 618 units
Infection:
Group 1 contains 789 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 183048 damage
Immune System group 2 would deal defending group 1 15450 damage
Immune System group 2 would deal defending group 2 15450 damage
Immune System group 2 attacks defending group 1, killing 3 units
Infection group 1 attacks defending group 2, killing 143 units
Immune System:
Group 2 contains 475 units
Infection:
Group 1 contains 786 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 182352 damage
Immune System group 2 would deal defending group 1 11875 damage
Immune System group 2 would deal defending group 2 11875 damage
Immune System group 2 attacks defending group 1, killing 2 units
Infection group 1 attacks defending group 2, killing 142 units
Immune System:
Group 2 contains 333 units
Infection:
Group 1 contains 784 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 181888 damage
Immune System group 2 would deal defending group 1 8325 damage
Immune System group 2 would deal defending group 2 8325 damage
Immune System group 2 attacks defending group 1, killing 1 unit
Infection group 1 attacks defending group 2, killing 142 units
Immune System:
Group 2 contains 191 units
Infection:
Group 1 contains 783 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 181656 damage
Immune System group 2 would deal defending group 1 4775 damage
Immune System group 2 would deal defending group 2 4775 damage
Immune System group 2 attacks defending group 1, killing 1 unit
Infection group 1 attacks defending group 2, killing 142 units
Immune System:
Group 2 contains 49 units
Infection:
Group 1 contains 782 units
Group 2 contains 4434 units
Infection group 1 would deal defending group 2 181424 damage
Immune System group 2 would deal defending group 1 1225 damage
Immune System group 2 would deal defending group 2 1225 damage
Immune System group 2 attacks defending group 1, killing 0 units
Infection group 1 attacks defending group 2, killing 49 units
Immune System:
No groups remain.
Infection:
Group 1 contains 782 units
Group 2 contains 4434 units
In the example above, the winning army ends up with 782 + 4434 = 5216 units.
You scan the reindeer's condition (your puzzle input); the white-bearded man looks nervous. As it stands now, how many units would the winning army have?
--- Part Two ---
Things aren't looking good for the reindeer. The man asks whether more milk and cookies would help you think.
If only you could give the reindeer's immune system a boost, you might be able to change the outcome of the combat.
A boost is an integer increase in immune system units' attack damage. For example, if you were to boost the above example's immune system's units by 1570, the armies would instead look like this:
Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with
an attack that does 6077 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning,
slashing) with an attack that does 1595 slashing damage at initiative 3
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack
that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire,
cold) with an attack that does 12 slashing damage at initiative 4
With this boost, the combat proceeds differently:
Immune System:
Group 2 contains 989 units
Group 1 contains 17 units
Infection:
Group 1 contains 801 units
Group 2 contains 4485 units
Infection group 1 would deal defending group 2 185832 damage
Infection group 1 would deal defending group 1 185832 damage
Infection group 2 would deal defending group 1 53820 damage
Immune System group 2 would deal defending group 1 1577455 damage
Immune System group 2 would deal defending group 2 1577455 damage
Immune System group 1 would deal defending group 2 206618 damage
Infection group 2 attacks defending group 1, killing 9 units
Immune System group 2 attacks defending group 1, killing 335 units
Immune System group 1 attacks defending group 2, killing 32 units
Infection group 1 attacks defending group 2, killing 84 units
Immune System:
Group 2 contains 905 units
Group 1 contains 8 units
Infection:
Group 1 contains 466 units
Group 2 contains 4453 units
Infection group 1 would deal defending group 2 108112 damage
Infection group 1 would deal defending group 1 108112 damage
Infection group 2 would deal defending group 1 53436 damage
Immune System group 2 would deal defending group 1 1443475 damage
Immune System group 2 would deal defending group 2 1443475 damage
Immune System group 1 would deal defending group 2 97232 damage
Infection group 2 attacks defending group 1, killing 8 units
Immune System group 2 attacks defending group 1, killing 306 units
Infection group 1 attacks defending group 2, killing 29 units
Immune System:
Group 2 contains 876 units
Infection:
Group 2 contains 4453 units
Group 1 contains 160 units
Infection group 2 would deal defending group 2 106872 damage
Immune System group 2 would deal defending group 2 1397220 damage
Immune System group 2 would deal defending group 1 1397220 damage
Infection group 2 attacks defending group 2, killing 83 units
Immune System group 2 attacks defending group 2, killing 427 units
After a few fights...
Immune System:
Group 2 contains 64 units
Infection:
Group 2 contains 214 units
Group 1 contains 19 units
Infection group 2 would deal defending group 2 5136 damage
Immune System group 2 would deal defending group 2 102080 damage
Immune System group 2 would deal defending group 1 102080 damage
Infection group 2 attacks defending group 2, killing 4 units
Immune System group 2 attacks defending group 2, killing 32 units
Immune System:
Group 2 contains 60 units
Infection:
Group 1 contains 19 units
Group 2 contains 182 units
Infection group 1 would deal defending group 2 4408 damage
Immune System group 2 would deal defending group 1 95700 damage
Immune System group 2 would deal defending group 2 95700 damage
Immune System group 2 attacks defending group 1, killing 19 units
Immune System:
Group 2 contains 60 units
Infection:
Group 2 contains 182 units
Infection group 2 would deal defending group 2 4368 damage
Immune System group 2 would deal defending group 2 95700 damage
Infection group 2 attacks defending group 2, killing 3 units
Immune System group 2 attacks defending group 2, killing 30 units
After a few more fights...
Immune System:
Group 2 contains 51 units
Infection:
Group 2 contains 40 units
Infection group 2 would deal defending group 2 960 damage
Immune System group 2 would deal defending group 2 81345 damage
Infection group 2 attacks defending group 2, killing 0 units
Immune System group 2 attacks defending group 2, killing 27 units
Immune System:
Group 2 contains 51 units
Infection:
Group 2 contains 13 units
Infection group 2 would deal defending group 2 312 damage
Immune System group 2 would deal defending group 2 81345 damage
Infection group 2 attacks defending group 2, killing 0 units
Immune System group 2 attacks defending group 2, killing 13 units
Immune System:
Group 2 contains 51 units
Infection:
No groups remain.
This boost would allow the immune system's armies to win! It would be left with 51 units.
You don't even know how you could boost the reindeer's immune system or what effect it might have, so you need to be cautious and find the smallest boost that would allow the immune system to win.
How many units does the immune system have left after getting the smallest boost it needs to win?

Some files were not shown because too many files have changed in this diff Show More