Advent of Code – második nap
A 2. nap történetében az első folytatása, ott folytatódik, ahol az első nap abbamaradt. Fent vagyunk a tengeralattjárón és rá kell jönnünk, hogy hogyan kell vezetni.
Van egy elÅ‘re megadott navigációs utasÃtássorozat, ami a következÅ‘képpen néz ki:
forward 5
down 5
forward 8
up 3
down 8
forward 2
A forward utasÃtás a horizontális tengelyen mozgat bennünket a megadott egységgel, az up és down utasÃtás pedig a mélységet szabályozza. Az elsÅ‘ feladatban egy ilyen navigációs utasÃtássort kapunk, amit végrehajtva hozzájutunk egy horizontális és egy mélység koordinátához. Az elsÅ‘ feladat helyes megoldása ennek a két számnak a szorzata lesz.
A feladat megoldásához készÃtettem egy új ReadLinesOfFile változatot, ami egy szöveges fájl egy sorából egy objektum létrehozását támogatja. A metódus tokenizer paramétere az elválasztó karakter, a objectFactory meg egy olyan lambda metódus, aminek a bemenete egy string tömb, amibÅ‘l majd T tÃpust állÃtunk elÅ‘.
public static IEnumerable<T> ReadLinesOfFile<T>(string filename, char tokenizer, Func<string[], T> objectFactory)
{
using (var file = File.OpenText(filename))
{
string? line;
do
{
line = file.ReadLine();
if (!string.IsNullOrEmpty(line))
{
string [] parts = line.Split(tokenizer, StringSplitOptions.RemoveEmptyEntries);
yield return objectFactory.Invoke(parts);
}
}
while (line != null);
}
}
A fenti metódus elÅ‘nye, hogy objektumorientált leÃrást tesz lehetÅ‘vé és van egy olyan tippem, hogy késÅ‘bbi napi feladatok esetén jól fog jönni.
A megoldáshoz további domain osztályokat és tÃpusokat definiáltam. A Day2Instruction a lehetséges utasÃtásokat tartalmazza, mÃg a Day2PositionData a fájl egy sorát Ãrja le.
public enum Day2Instruction
{
Forward,
Down,
Up,
}
public class Day2PositionData
{
public Day2Instruction Instruction { get; set; }
public int Value { get; set; }
}
Ezeknek a segÃtségével a megoldás a következÅ‘:
internal class Day02 : IPuzzleSolution
{
public void Execute()
{
var positions = FileReader.ReadLinesOfFile<Day2PositionData>("inputs\\02.txt", ' ', (tokens) =>
{
return new Day2PositionData
{
Instruction = Enum.Parse<Day2Instruction>(tokens[0], true),
Value = int.Parse(tokens[1])
};
}).ToArray();
int finalDepth = 0;
int finalHorizontal = 0;
foreach (var position in positions)
{
(int depth, int horizontal) = DecodePosition(position);
finalDepth += depth;
finalHorizontal += horizontal;
}
Console.WriteLine(finalDepth * finalHorizontal);
}
private static (int depth, int horizontal) DecodePosition(Day2PositionData position)
{
return position.Instruction switch
{
Day2Instruction.Forward => (0, position.Value),
Day2Instruction.Down => (position.Value, 0),
Day2Instruction.Up => (-position.Value, 0),
_ => (0, 0),
};
}
}
A megoldás lényegi része abból áll, hogy soronként végigmegyünk a fájlon és eldöntjük, hogy az utasÃtás milyen hatással lesz a végsÅ‘ mélységre és horizontális koordinátára. Ehhez készÃtettem egy DecodePosition metódust, ami egy ValueTuple-ban visszaadja, hogy mennyivel kell növelni a sor feldolgozása után a mélység és horizontális koordinátákat. Végezetül kiÃrja a két szám szorzatát, ami a megoldás lesz. A DecodePosition egy switch kifejezéssel lett megoldva, de megoldható lett volna egy komplexebb if-else utasÃtáspárossal is.
2. feladat
A második feladat lényegében az elsÅ‘ variánsa. Kiderül, hogy az utasÃtások nem azt jelentik, mint amit gondoltunk. Az up és down utasÃtások nem kilencven fokban emelik a tengeralattjárót, hanem az orr cél szögét (aim) állÃtják. A forward utasÃtás meg valójában két dolgot végez: elÅ‘ször is növeli a horizontális pozÃciót, a mélységet pedig az cél szög és a horizontális elmozdulás szorzata alapján módosÃtja.
A tengeralattjáró ebben a feladatban is 0,0 pozÃcióról indul és 0 a célszög. A feladatban az új információk alapján ki kell számolni a végsÅ‘ mélység és horizontális pozÃciót és a megoldás ismét a kettÅ‘nek a szorzata lesz.
A megoldás nagyon hasonlÃt az elsÅ‘ feladatra:
internal class Day02B : IPuzzleSolution
{
public void Execute()
{
var positions = FileReader.ReadLinesOfFile<Day2PositionData>("inputs\\02.txt", ' ', (tokens) =>
{
return new Day2PositionData
{
Instruction = Enum.Parse<Day2Instruction>(tokens[0], true),
Value = int.Parse(tokens[1])
};
}).ToArray();
int finalDepth = 0;
int finalHorizontal = 0;
int currentAim = 0;
foreach (var position in positions)
{
(int depth, int horizontal, int newAim) = DecodePosition(position, currentAim);
finalDepth += depth;
finalHorizontal += horizontal;
currentAim = newAim;
}
Console.WriteLine(finalDepth * finalHorizontal);
}
private static (int depth, int horizontal, int newAim) DecodePosition(Day2PositionData position, int currentAim)
{
return position.Instruction switch
{
Day2Instruction.Forward => (currentAim * position.Value, position.Value, currentAim),
Day2Instruction.Down => (0, 0, currentAim + position.Value),
Day2Instruction.Up => (0, 0, currentAim - position.Value),
_ => (0, 0, 0),
};
}
}
Igazából a lényegi különbség annyi, hogy a DecodePosition metódus másként néz ki. Két bemenete van: a sor és a jelenlegi cél szög. Kimenete pedig három érték: a mélység, a horizontális pozÃció és az új célszög. A mélység és horizontális pozÃció összegzésre kerül, mÃg a célszöget szimplán felülÃrjuk.
Mindenkinek kellemes Advent of Code idÅ‘szakot kÃvánunk. Holnap folytatása következik. 🎄