Advent of Code 2023

Hi Swift Community!

Anyone interested in having a little Swift coding challenge fun?

Every December, Eric Wastl publishes a playful set of coding exercises called the “Advent of Code”. He publishes one challenge per day, every day, from December 1st to December 25th. The challenges start out easy and increase in difficulty each day. Here’s the first exercise from last year for example.

Participants can use whatever programming language and techniques they’d like to complete the exercises, but of course, we use Swift!

Participating is a great way to sharpen your Swift skills and have a little fun with the community.

This year, some folks from the Swift team have put together a starter template to help you get going.

If you’re interested in participating:

  1. Clone the template (optional)
    a) If you’re using Xcode, you can open this template by selecting File > Open and navigating to its base directory.
    b) If you’re using the Swift CLI, change directory to this template and type swift run to run your code or swift test to run your tests.

  2. Create an account at Advent of Code (required to use the leaderboard)

  3. Join our Swift community leaderboard to participate with others. Use ID 3315857-f51d8ce5.

  4. Every day (or as often as you prefer), attempt the challenge using Swift!

The leaderboard will automatically update to show who has completed the challenge and provide a score based on how long it took you. You can always ignore the score of course - it’s just for fun!

The leaderboard can also include a link to your GitHub account where you can (optionally) post your solution. It’s really neat to see how everyone attempts each day’s challenge - I often learn new tricks and techniques by doing so.

Hope you all consider joining, and have a great December!

(Please note Advent of Code is a community event and is not affiliated with the Swift project. We take no responsibility for your coding challenge frustrations when it gets tough!)

48 Likes

Thank you for putting this together! I've been participating in AoC for a while and always used Swift. I will definitely use this template this year!:christmas_tree:

4 Likes

This is awesome! Always looking forward to December just for AoC. Nice to see some discussion here on the forums about it!

1 Like

For me is the AoC an opportunity to try something new and get out of my comfort zone. I can’t recommend it enough even though Swift is my primary language. E.g. last year I solved AoC exercises in Python + PyCharm.

1 Like

Big fan of Advent of Code! I really appreciate the well-thought out problem descriptions, examples / test inputs that cover (most) corner cases, and the escalating difficulties. I find it to be a very rewarding way to practice data modeling & algorithms, different from the “daily grind” of typical app development.

I use (and mostly enjoy) playgrounds for my AoC efforts. It seems like a pretty good fit: interactive and incremental exploration of the data & code.

However, most problems require moving the main logic into a “Source” inside the Playground Page, since trying to track every single intermediate result quickly runs into performance problems. So watch out!

Also, I simply copy the code from swift-algorithms into the shared Sources folder. Maybe some year there’ll be swift package support for standalone playgrounds inside a workspace, but without a “Target“ I seem to be out of luck.

PS: I wish that the Regex documentation was better! ex: matches(of:) is on BidirectionalCollection, and so it doesn’t show up at all in the documentation for String. :man_facepalming:

3 Likes

Playgrounds aren't good for the later problems, as many of them have performance cliffs between parts 1 and 2 that just won't work in a playground. A simple executable is much faster for those problems and you don't have to deal with the issues you've found.

4 Likes

Sorry to be an idiot (doesn't bode well for the challenges) - but I have a question about the package template.

The readme states:

The challenges assume three files (replace 00 with the day of the challenge).

  • Data/Day00.txt: the input data provided for the challenge
  • Sources/Day00.swift: the code to solve the challenge
  • Tests/Day00.swift: any unit tests that you want to include

To start a new day's challenge, make a copy of these files and update as necessary. The AdventOfCode.swift file controls which day's challenge is run with swift run . By default that runs the most recent challenge in the package.

I've duplicated Day00.swift and renamed the copy Day01.swift and renamed the struct Day01 but upon running the target it runs the Day00 code

Executing Advent of Code challenge 0...

What am I missing? Should I be deleting the Day00 files?

1 Like

Don't apologize! I was confused by this too, but then I noticed at the top of AdventOfCode.swift there's:

// Add each new day implementation to this array:
let allChallenges: [any AdventDay] = [
  Day00()
]

You need to add Day01() and so on as you complete puzzles.

7 Likes

Thanks for this!

I solved today's puzzles, then looped back and did them a second time using the template and wanted to say thanks for the folks that put it together, I've learned a few things just from working with it.

Aside from playing with this in REPL, are there any recommendations for how to easily toggle between 'verbose' and non-verbose output for test cases? I'm new to Xcode debugging, but this has been a good motivator to learn.

Joined the leaderboard. :grinning:

I am not using a specific template: each day I just create a new vanilla SPM package.

3 Likes

Great!
This is my third year coding AoC using Swift.

4 Likes

Hey everyone, just wanted to share how I resolved an error I encountered while working with the template

The error message "Source files for target AdventOfCode should be located under 'Sources/AdventOfCode'" led me to reorganize my project structure. I fixed it by creating a directory named 'AdventOfCode' within 'Sources/' and moved all the source files from 'Sources/' into 'Sources/AdventOfCode'. Similarly, for tests, I created 'AdventOfCodeTests' under 'Tests/' and moved the test files into 'Tests/AdventOfCodeTests'.

This restructuring aligned the project structure with the expected location defined in the Package.swift manifest, resolving the issue. Hope this solution saves someone else from the same headache! Good luck with your Advent of Code challenges! :blush::christmas_tree:

Ugh, it's not a great start when the day one puzzle (part 2) is insufficiently specified such that there's at least two right answers, but they only accept one of them, chosen arbitrarily. I had to dig through a bunch of Reddit threads to find the results of someone taking the time to reverse engineer the accepted code solutions to figure out the missing specification. And I noticed in doing so that I was very far from the only one that was fouled by this.

Is it normally like this? If I want poorly-specified problems I can stick to real-world work. :stuck_out_tongue_closed_eyes:

2 Likes

Quite curious what was missing in the specification for you? I personally had no issues. I made the obvious mistake of not thinking about overlapping numbers but after entering the wrong answer once it is up to us to take a second look and notice that it is hinted at quite well in the provided example input. I found this more an error on my side than in the specification.

2 Likes

The problem expects "twone" to be processed as "21", not "2ne" or "tw1". But it does not provide any indication of this, and none of its example cases cover this.

It thus depends in part on which way you happen to approach the problem. In this particular case an imperative approach is more likely to stumble on the answer variant that they want, than a functional-programming one.

1 Like

Advent of Code is great! It broadens my problem-solving skills and facilitates my exploration of new language features. I recently attempted to solve Day 2 Part 2 utilizing variadic generics, which turned out to be a challenging experience. During this process, I encountered a compiler crash too.

2 Likes

Just a note, consider adding the input data files to your .gitignore if your repo is public. The creator of AoC has stated he prefers people not be able to aggregate all the various inputs together.

Some more info here: https://www.reddit.com/r/adventofcode/wiki/faqs/copyright/inputs/

Hope everyone is enjoying AoC 2023 as much as I am!

6 Likes

Pfff just completed day 3. What a mess that first part. Always trips me up when the full input contains cases that apparently are not in the example and thus swing by my test harnas.

day 1 was tough. day 2 was fun. let's see how day 3 goes lol.

1 Like

I've solved each day so far, and today is the first time I've had any kind of performance issue. Wondering if folks here could help me learn where I've gone "wrong" (produces correct solution, but in ~2.5 minutes on an M1 Mac). My instinct is that I've done something un-Swifty with Strings/Characters, and I'd love to learn how to do this better.

My bet is that it's in one of the two methods below, which I'll blur to hide spoilers. (If I'm reading Instruments correctly, they're taking up nearly all of the runtime)

 @inlinable
    func charAt(_ x: Int, _ y: Int) -> Character {
        if x < 0 || y < 0 || y >= grid.count || x >= grid[y].count {
            return "."
        }
        return grid[y][grid[y].index(grid[y].startIndex, offsetBy: x)]
    }
// Pull all the data out of the grid
    func loadGrid(pointsOfInterest: inout [PointOfInterest], partNumbers: inout [PartNumber]) {
        for y in 0 ..< grid.count {
            var x = 0
            while x < grid[y].count {
                switch charAt(x,y) {
                case ".":
                    x += 1          // If empty space, just skip it
                case "0"..."9":     // If a number, record a part number
                    let location = CGPoint(x: x, y: y)
                    var length = 1
                    var value = Int(String(charAt(x, y)))!
                    x += 1
                    while charAt(x,y).isNumber {
                        value = value * 10 + Int(String(charAt(x, y)))!
                        x += 1
                        length += 1
                    }
                    partNumbers.append( PartNumber(value: value, location: location, length: length))
                default:            // If it's anything else, consider it a symbol
                    pointsOfInterest.append( PointOfInterest(value: charAt(x,y),location: CGPoint(x: x,y: y)))
                    x += 1
                }
            }
        }
    }