Blog

Advent of Code Tag 20: eine kleine (Neu-)Vision

Bastiaan Bakker

Bastiaan Bakker

Aktualisiert Oktober 21, 2025
5 Minuten

Logbuch des Coders, AoC-Sternzeit 2019.40. Mit Hilfe des Traktorstrahls ist es uns gelungen, dem Sicherheitsperimeter des Planeten Neptun zu entkommen. Wir sind auf Sol IX gelandet, in der Nähe einer donutförmigen Struktur. Sie scheint ein labyrinthartiges Inneres zu haben, das wir durchqueren müssen....

Nur bin ich mit dem Einsammeln der Sterne für die vergangenen Tage furchtbar im Rückstand, ganz zu schweigen davon, dass unser Finanzkontrolleur gerade eine E-Mail geschickt hat: "Geben Sie Ihre Ausgaben für 2019 so schnell wie möglich ein". Zeit für... mehr Prokrastination, und einen kleinen Blick hin und her.

Für mich als Abendmensch ging es bei Advent of Code nie darum, die täglichen Rätsel so schnell wie möglich zu lösen. Ohne Geschwindigkeit als Hauptfaktor gibt es mehr Raum für Erkundung und Lernen. Und für einige selbst auferlegte Beschränkungen für zusätzlichen Spaß.

Als ich 2016 bei AoC anfing, kam ich zu dem (peinlichen?) Schluss, dass ich keine "richtige" Sprache mehr hatte: In meiner täglichen Arbeit als Plattform- und DevOps-Entwickler verwendete ich hauptsächlich Bash. Aber das würde doch nicht funktionieren, oder? Nun, AoC hat mir das Gegenteil bewiesen! Fast alle Rätsel waren lösbar und ich habe dabei einige nette Tricks gelernt. In meinem früheren Beitrag von 2017 finden Sie eine Zusammenfassung. Von nun an bin ich als"BashtiaanBakker" bekannt (zumindest auf unserem Xebia Slack).

Es folgten andere Sprachen wie Haskell und Go, und um einige meiner Kollegen zu ärgern, ein bisschen Brainf*ck und Whitespace.

Da mein jüngster Sohn angefangen hat, in Scratch zu programmieren, musste ich das auch ausprobieren.

https://twitter.com/NakedN3rd/status/1069004144873283584

Auch die großartige Open-Source-Spielengine Godot war eine gute Alternative zur ASCII-Kunst für die Visualisierung.

https://twitter.com/NakedN3rd/status/1080942693650481152

Aber das Beste waren die Diskussionen mit Kollegen und Freunden und das Sehen ihrer Lösungen und die der weltweiten Teilnehmer.

Ein neues Jahr, eine neue Gelegenheit, AoC zu nutzen, um zu lernen und zu erforschen, dieses Mal die Google Cloud Platform. Zuerst habe ich mit Google Cloud DataFlow / Apache Beam gespielt. Z.B. Star 2:

with beam.Pipeline(options=pipeline_options) as p:
    def fuel(f):
        x = int(f)
        while x > 0:
            x =  max(0, math.floor(x / 3) - 2)
            yield x
    texts = (p
        | 'Read'  >> ReadFromText(known_args.input)
        | 'Fuel'  >> beam.FlatMap(fuel)
        | 'Sum'   >> beam.CombineGlobally(sum)
        | 'Write' >> WriteToText(known_args.output)
    )

Eine nette Eigenschaft ist, dass Sie denselben Code lokal, aber auch hochparallel auf automatisch bereitgestellten und skalierten Knoten ausführen können, die von Cloud DataFlow verwaltet werden - klasse!

Danach erschwerte die sequentielle Natur der meisten IntCode-basierten Rätsel die Parallelisierung.

An Tag 11 gab es dann eine andere Art von Herausforderung, die bereits in Edos Blog vorgestellt wurde: Der Code generiert eine Bitmap, die einen Text darstellt. Dann wenden wir eine Wetware OCR, unser Gehirn, an, um die eigentliche Lösung zu erhalten.

Wäre es jetzt nicht schön, wenn unser Code die OCR selbst durchführen würde? Zufälligerweise bietet die GCP Machine Learning Vision API genau das, was wir brauchen. Lassen Sie uns zunächst ein tatsächliches Bild anstelle von ASCII-Kunst erstellen. Mit der Python Pillow Bibliothek ist das ganz einfach:

# dict panels contains all painted panels...
from PIL import Image
img = Image.new('RGB', (x_size, y_size), 'black')
pixels = img.load()
for p in panels.keys():
    if panels[p] == 1:
        pixels[p[0],p[1]] = (255, 255, 255)
img.save("star23.png")

Jetzt müssen wir es nur noch an GCP zur Texterkennung übermitteln. Als 'Basher' wähle ich dafür das Befehlszeilentool gcloud:

$ gcloud ml vision detect-text star23.png

Wow, das sind ja eine Menge Informationen!

{
  "responses": [
    {
      "fullTextAnnotation": {
        "pages": [
...
        ]
      },
      "textAnnotations": [
        {
...
        },
        {
          "boundingPoly": {
            "vertices": [
              {
                "x": 1
              },
              {
                "x": 39
              },
              {
                "x": 39,
                "y": 5
              },
              {
                "x": 1,
                "y": 5
              }
            ]
          },
          "description": "BLULZJLZ"
        }
      ]
    }
  ]
}

Wir sind nur an der "Beschreibung" interessiert. Mit ein wenig jq kommen wir zu unserer Antwort:

$ gcloud ml vision detect-text star23.png | 
   jq -r '.responses[0].textAnnotations[].description' | sort -u
BLULZJLZ

Großartig, das ist gelöst! Zurück an die Arbeit, all die Parkquittungen warten darauf, ins Spesensystem eingegeben zu werden :-(

Hmmm... Ich brauche nur das Datum und den Betrag von ihnen. Nun, das richtige Datum und den richtigen Betrag. Ah, das neueste Datum und der höchste Betrag sollten in Ordnung sein. Wieder zurück zu Bash!

Hier ist es also, mein hochentwickeltes receipt2json.sh Skript. Warten wir auf ein anderes Mal, um es in eine schöne Google Cloud-Funktion zu verwandeln. Zunächst einmal: Wo sind all die fehlenden Sterne...?

#!/usr/bin/env bash
textfile=$(mktemp)
gcloud ml vision detect-text "$1" | jq -r '.responses[0].textAnnotations[].description' > $textfile
amount=$(egrep -o 'b[0-9]+,[0-9]{2}b' $textfile | sort -n | tail -1 | tr , .)
date=$(egrep -o '[0-9]{2}-[0-9]{2}-(20)?(19|20)' $textfile | sort | tail -1)
rm $textfile
cat <<EOF
{ 'amount': $amount, 'date': '$date' }
EOF

Advent des Codes 2019 Serie

Verfasst von

Bastiaan Bakker

Contact

Let’s discuss how we can support your journey.