Advent of Code 2023

Siarhei Siamashka siarhei.siamashka at gmail.com
Sun Dec 3 14:51:37 UTC 2023


On Saturday, 2 December 2023 at 13:33:33 UTC, Johannes 
Miesenhardt wrote:
> I am a bloody beginner so if there are any things that are very 
> wrong with this please point them out.

Everything is fine as long as it works and does the job.

> The fact that I need a template for accepting both a string and 
> a char[] is very weird but I went with it.

The `string` type is not the same as `char[]`. It's actually 
`immutable(char)[]`. The differences are explained in the D 
language spec: 
https://dlang.org/spec/const3.html#const_and_immutable

Strings can be safely passed around between functions and 
multiple instances of the same string can share the same memory 
location, the characters inside of a string are read-only. 
Whereas character arrays allow read/write access.

Casting between character arrays and strings is a bad idea by 
design. Converting between strings and character arrays involves 
allocating memory for a new copy and this happens under the hood 
when calling `.dup`, `.idup`, `.to!string` or `.byLineCopy`.

> I am also curious if there is a better way for the reversible 
> for-loop to happen. I saw foreach and foreach_reverse but I 
> don't think that helps me here, since I swap them out based on 
> a runtime argument.

Below is my solution for the day 1 puzzle, which used 
https://dlang.org/library/std/range/retro.html to search 
characters starting from the end:

```D
import std;
void main() {
   auto input = stdin.byLineCopy.array;

   try {
     input.map!(s => (s.find!"a >= '0' && a <= '9'".front - '0') * 
10 +
                      s.retro.find!"a >= '0' && a <= '9'".front - 
'0')
          .sum.writefln!"Part1: %d";
   } catch (Error e) {
     writefln!"Part1: malformed input";
   }

   auto words = "one, two, three, four, five, six, seven, eight, 
nine"
        .split(", ").zip(iota(1, 10))
        .map!(x => tuple(x[0], x[0] ~ x[1].to!string ~ 
x[0])).array;
   input.map!(s => words.fold!((a, subst) => 
a.replace(subst[]))(s))
        .map!(s => (s.find!"a >= '0' && a <= '9'".front - '0') * 
10 +
                    s.retro.find!"a >= '0' && a <= '9'".front - 
'0')
        .sum.writefln!"Part2: %d";
}
```

The key features are:
* I'm using `import std;` and this makes the source code smaller 
(at the expense of a bit longer compile time). This is a bad 
style in real applications, but suitable here.
* I'm reading the input data from `stdin`, because it's a common 
convention for solving algorithmic puzzles on various websites 
(such as codeforces or atcoder).
* The string "one, two, three, four, five, six, seven, eight, 
nine" was copy-pasted from the puzzle text and then parsed by the 
code. This was it's a bit faster to implement and less prone to 
typos.
* For part2 the input text was preprocessed ("one" is replaced by 
"one1one", "two" is replaced by "two2two" and so on). And after 
such search & replace is complete, the whole task becomes the 
same as the already solved part1. Such approach makes the code 
slower, but the code is simpler and faster to implement. A useful 
tradeoff for the Advent of Code puzzles.



More information about the Digitalmars-d-learn mailing list