LogoWCF

janka102's Solution

2025

Ruby again… I’m starting to get familiar with the docs now. I couldn’t get debugging in VSCode working, so irb was my friend this time.

This goes though character-by-character and keeps track of the previous along with the current. That is enough to keep all the state you need to decode the message. I belive this streams the file reading, and it prints each character as it finds it, so it streams the output too.

require 'optparse'

class Day02
  def initialize(filename)
    @filename = filename
  end

  def solve
    mapping = ('a'..'z').chain('A'..'Z').to_a

    prev = nil
    File.open(@filename).each_char do |c|
      if prev.nil?
        if c == '\\'
          prev = c
        else
          $stdout.write c
        end
      elsif prev == '\\'
        if c == '\\'
          $stdout.write c
        else
          prev = c
        end
      else
        symbol = Integer(prev + c, 10)
        raise "Invalid encoding: #{prev + c}" if symbol.negative? || (symbol >= mapping.size)

        $stdout.write mapping[symbol]
        prev = nil
      end
    end.close
  end
end

# Expects the input files to be named 02.txt and 02_challenge.txt and in the current working directory
if __FILE__ == $PROGRAM_NAME
  options = {}
  OptionParser.new do |parser|
    parser.banner = "Usage: #{$PROGRAM_NAME} [options]"

    parser.on('-c', '--challenge', 'Solve with the challenge input') do
      options[:challenge] = true
    end
  end.parse!

  challenge = Day02.new("02#{options[:challenge] ? '_challenge' : ''}.txt")
  challenge.solve
end

2024

Show Gleam Solution

This uses a state machine to parse the input.

import gleam/io
import gleam/int
import gleam/string
import gleam/list
import gleam/result
import gleam/string_builder.{type StringBuilder}

// State machine
// Encapsulates when something is being escaped with a backslash
type Parser {
  Normal(decoded: StringBuilder) // Not escaping
  Escape(decoded: StringBuilder) // Previous char was a backslash
  EscapeChar(decoded: StringBuilder, first: String) // Currently parsing a backslash escape sequence
}

fn try_decode(str: String) {
  use num <- result.try(result.map_error(int.parse(str), fn (_) { "Not a number" }))
  use codepoint_num <- result.try(case num {
    _ if num < 0 || num > 51 -> Error("Invalid escape code")
    n if num > 25 -> Ok(65 + n - 26)
    n -> Ok(97 + n)
  })

  use codepoint <- result.try(result.map_error(string.utf_codepoint(codepoint_num), fn (_) { "Not a codepoint" }))
  Ok(string.from_utf_codepoints([codepoint]))
}

pub fn main() {
  // Copy/paste the contents of the file into the string here
  let input = "..."

  let output = input
  |> string.split("")
  |> list.fold(Normal(string_builder.new()), fn (acc: Parser, c) {
    case c {
      "\\" -> case acc {
        Normal(prev) -> Escape(prev)
        Escape(prev) -> Normal(prev |> string_builder.append(c))
        EscapeChar(_, _) -> panic as "Invalid escape sequence"
      }
      _ -> case acc {
        Normal(prev) -> Normal(prev |> string_builder.append(c))
        Escape(prev) -> EscapeChar(prev, c)
        EscapeChar(prev, first) -> case try_decode(first <> c) {
          Ok(decoded) -> Normal(prev |> string_builder.append(decoded))
          Error(e) -> panic as e
         }
      }
    }
  })

  case output {
    Normal(decoded) -> io.println(decoded |> string_builder.to_string)
    Escape(_) -> panic as "Invalid escape sequence"
    EscapeChar(_, _) -> panic as "Invalid escape sequence"
  }
}

Day 2: Counting Santa's Letters

To help keep things efficient at the North Pole, Santa uses a text intercom system to pass messages. But right now that system is bugging out! It isn’t correctly decoding messages!

Problem: Create a decoder for the following encoding scheme:

There are two files: small_message_encoded.txt, which you can test on, and large_message_encoded.txt, which you should also decode and then share if you can hear “it” to prove you completed the challenge.

Example:
Input:

\33\04\11\11\14, \48\14\17\11\03!

Output:

Hello, World!