ICFP Contest 2026

Publication date: 2026-07-27

As almost every year, codingteam was participating in the annual ICFP Contest.

I will document the task first, and then provide an overview of our team's work this year.

The Task

This year's task was quite an interesting one: we were programming 2D computation schemas, controlling groups of so-called "little men". Here I will very succinctly summarize the basics of this programming, but you are welcome to read the interactive textbook on the programming in this system.

Here's a correct program representing a little man @ that will go in a loop:

+---+
|>@v|
|   |
|^ <|
+---+

(you can see that arrow-like characters control the movement).

And here's a program in which the little man will grab a number 4 into its register and then Halt:

+----+
|    |
|@4 H|
|    |
+----+

Each room (usually) contains one little man that follows the instructions on the room floor. The instructions operate on two registers of the little man, and can contain basic arithmetic and branching. Rooms can be connected with pipes and support asynchronous data passing. There are special room kinds — Input and Output, that allow the system to talk with the outside world.

There are many more features: there are programmable displays, little man parallelism, advanced arithmetic operations — but please read the textbook if you want to know more.

The Chronicles

This year, we were planning to participate using Haskell, though as usual, our team members were using a plethora of different tools (and, yes, Fortran, there's always some Fortran).

Preparation

This time, we started preparation for the contest really late: for example, I've created the repository several hours before the start of the contest.

While setting up a repository (by copying the CI/project stuff from our contest entry from 2024), I have found an Easter egg in our 2024's contest! In the license file, there's an ominous phrase "Join Us".

Some members of the team were using Jabber, and some others used Telegram, so we once again relied on our Telegram–XMPP bridge solution, Emulsion. And guess what? Of course, one of its features, the message archive, has been broken: due to a misconfiguration in the TypeScript frontend build pipeline, we ignored the compilation errors (imagine that — it's the default behavior when using Parcel), so a breaking update of a frontend library was happily merged and passed the CI, and we've deployed a broken version of Emulsion. After figuring it out, I fixed Emulsion.

A member of our team, @portnov, was experiencing difficulties with accessing the contest website, so I decided to build a quick proxy for him. Unfortunately, he didn't succeed in accessing the content via proxy anyway, and was unable to participate in the contest. Thus, our team this year consisted of:

All times and dates below are in my local time zone (CEST).

Day 0, 2026-07-24

At 12:00 UTC, the contest has officially started. While I was still struggling with setting up nginx for content proxy, folks have started investigating the details of this year's task. Overall, it's really similar to the 3D task from 2024.

Foxtran immediately proposed an idea to use the organizers' code from the website as a basis for our scoring/emulation solution. And we actually used it for a bit!

Folks have found an annoying bug in the web editor's behavior, which was reported to Discord and resolved a bit later: it was losing the editor contents on switching tabs.

We started struggling with the Triangle task (which asks to calculate a triangular number). We quickly produced some solutions, and were investigating which one was optimal. Eventually we found that we can calculate the optimal size and time complexity of the solution by looking at the other teams' scores: if the best visible score is 832, this means that people's solutions fit into an 8×8 grid and take 13 ticks to solve. At some point, @foxtran invented a smart trick using the U command (that allows turning and sending a piece of data into a pipe in one tick), and we achieved the best solution of 832 points, sharing 1st place with everybody else.

Minoru started thinking about the Memory task, and implemented one memory cell:

+--------+
| v     <|
|@>rXWMs^|
|   >rM ^|
+--------+
^      v
^      v
+-+    +-+
|I|    |O|
+-+    +-+

At some point, all the folks were solving different tasks, and @foxtran was using some sort of "compiler" of simple programs into sequences of commands, developed in Fortran (of course!) with LLM assistance.

@Akon32 laid out 4 memory cells (out of the required 100) for the Memory task and got tired.

@Akon32 also proposed a solution for the Reverse a List task: instead of using convoluted memory cell rooms, we can just implement delay lines using pipes, so that the signals are temporarily stored in pipes of varying size, and are delayed accordingly to the task requirements. This is a technique that we'll be using during the whole contest.

@gsomix mentioned that this year's task reminds him of Factorio, which I agree with.

Day 1, 2026-07-25

We noted that the online editor got the first convenience feature: now there's a download button near each solution. This improved our collaboration dramatically — now we could download and improve each other's solutions without need to share them via the repo or the chat.

Our solution for Memory has arrived, and used the same technique of storing the data in pipes.

@Akon32 has solved the Sudoku Auditor.

I have started working on manual optimizations of some tasks in the places where I see any room for it. Move the blocks, choose a more compact layout, shorten the pipes — this kind of thing. While working with that, I found that generally we have two kinds of pipes: one kind where we don't care about their length, and can reduce them as we see fit, and another kind where the pipes are used as delay lines, and shortening them will break the program (because this will potentially cause two signals to arrive to their destination in a different order).

@Akon32 started to solve the Plotter task, one of the few that used the LM-75 programmable display.

I started reading about various tools to lay out the integrated circuits, with hope that there are some basic algorithms we'd be able to implement and use to lay out rooms and pipes. A fun fact I found: early computers were made of some kind of cosmic hypercubes. Eventually I came to the conclusion that all the algorithms in the field are too complex, and we'll need something much simpler, some basic heuristics, and an optimizer that will try to randomly move rooms and see if it improves the result.

After the first 24 hours of the contest have passed, a new Split instruction (Y) was introduced that allowed to split the little men in the same room. I don't think we used it in any of our further solutions, unfortunately.

At some point, @foxtran started threatening us with a WASM engine that he wanted to use for some purpose. (Turns out it was regarding his previous suggestion to use the emulator/interpreter developed by the organizers, instead of relying on our own.)

I have found a problem with the following pipe layout:

+-+ +-+
|I|>|O|
+-+|+-+
   |

Here, the pipe clearly comes from below and leads into the O room — while the engine was refusing to count this (because it thought that the pipe may also lead from the I room into the O room). This was a bug in the contest engine, which I wasn't aware of, and thought that it's an error almost till the very end of the contest. This peculiarity also created some difficulties for the automatic layout engine I was working on, and for the program parser @Minoru was working on.

@gsomix has dramatically optimized the list sorter solution.

Day 2, 2026-07-26

@foxtran proceeds to work on his Fortran compiler.

While I work on the optimizer, @Minoru has implemented a parser for the programs, to load them into our Haskell data model. I have also found that in a lot of cases, my optimization strategy won't be efficient: it doesn't consider that the pipe connection points to the rooms should be static (in cases when r command placement relies on these, and in cases when pipe lengths are significant for the program).

When I finally finished the optimizer and joined it with the parser from @Minoru, it turned out that it's a… pessimizer — it was unable to optimize anything, and in addition to that, it was actively breaking the working programs (e.g. overwriting rooms with pipe ends, and such). So, I started working on the bug fixes.

@Akon32 was struggling with the Plotter task.

Day 3, 2026-07-27

We all pretty much exhausted our strength and imagination, so just started to micro-optimize some tasks manually. @Minoru tried to help with fixing the optimization core in the optimizer, but didn't succeed much.

I have found that the web editor got another improvement, and now it's easy to directly open in the editor any solution that we sent. Which was convenient for the manual optimization!

During the last minute of the contest, @gsomix sent a new solution for Memory that saved us 5M of score (26.5M21.2M). Nice!

And thus the contest ended. We finished in 48th place (out of 267 teams with non-zero score).

The Conclusion

I enjoyed this year's task, it was quite interesting!

Unfortunately, a lot of our team's work was manual, we were optimizing and implementing tasks ourselves. My plan to write an automated optimizer didn't succeed :(

The compiler in Fortran did somewhat succeed, though, so that's good!

Nevertheless: as a result, I have several bug reports in Haskeletor (IDE tooling for Haskell maintained by myself) and CyclopsChat (an XMPP client, also maintained by myself, that I occasionally use for checking how our XMPP bridge works). More work to do for the good of all of us!

You can read our code here, and I'll hopefully publish our official standings after it's made available (sometime during September 2026?).