SkillAgentSearch skills...

Factompiler

A compiled language for Factorio Blueprints.

Install / Use

/learn @Snagnar/Factompiler
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <img src="https://github.com/Snagnar/Factompiler/blob/main/doc/img/header_bp.png" alt="Facto compiled blueprint in Factorio" width="800"/> </p> <h1 align="center">Facto - Write code. Get Factorio circuits.</h1> <p align="center"> <a href="https://github.com/Snagnar/Factompiler/actions/workflows/ci.yml"><img src="https://github.com/Snagnar/Factompiler/actions/workflows/ci.yml/badge.svg" alt="CI Pipeline"/></a> <a href="https://codecov.io/gh/Snagnar/Factompiler"><img src="https://codecov.io/gh/Snagnar/Factompiler/branch/main/graph/badge.svg" alt="codecov"/></a> <a href="https://pypi.org/project/factompile/"><img src="https://img.shields.io/pypi/v/factompile.svg" alt="PyPI version"/></a> <a href="https://pypi.org/project/factompile/"><img src="https://img.shields.io/pypi/pyversions/factompile.svg" alt="Python versions"/></a> <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"/></a> </p> <p align="center"> <a href="https://snagnar.github.io/factompile/">Try it out with the online compiler!</a> </p>

Facto is a programming language that compiles to Factorio circuit network blueprints. You write readable code describing the logic you want, and the compiler generates optimized combinators that you paste directly into your game. No manual wiring, no layout headaches, no remembering signal types, no memory frustration, no debugging visual spaghetti.

<div style="width: 100%; overflow-x: auto;"> <table style="width: 100%; table-layout: auto;"> <tr> <td style="padding-right: 20px; vertical-align: top;">
Memory counter: "signal-A";
counter.write((counter.read() + 1) % 20);

Entity lamp = place("small-lamp", 0, 0);
lamp.enable = counter.read() < 30;
</td> <td width="40%"> <img src="https://github.com/Snagnar/Factompiler/blob/main/doc/img/small_example.gif" style="width: 100%; height: auto; min-width: 300px;" alt="RGB color cycling lamp grid in Factorio"/> </td> </tr> </table> </div>

Save this to a blink.facto, run factompile blink.facto, copy the output, import it into Factorio, and watch your lamp blink. You can also visit https://snagnar.github.io/factompile/ to run factompile without having to install anything!


What Facto Does

Facto handles the tedious parts of circuit building so you can focus only on logic. The compiler takes care of placing combinators in a sensible layout, routing wires between them (choosing red vs green to avoid signal conflicts), and inserting relay poles when distances exceed 9 tiles. You can also pin specific signals to red or green wires when interfacing with external circuits. It catches type mismatches at compile time rather than leaving you to debug mysterious in-game behavior.

What Facto doesn't do: it might not produce the most minimal, most compact circuit layouts. The goal is to make circuits that are efficient enough to run well in-game while being easy to read, write, and maintain. If you need absolute minimalism, hand-optimizing the generated blueprint is still an option.


Installation

pip install factompile

Verify it works with factompile --help.

Try It Instantly

Don't want to create a file? Try compiling directly from the command line:

factompile -i 'Signal x = 42; Entity lamp = place("small-lamp", 0, 0); lamp.enable = x > 20;'

Copy the output blueprint into Factorio and see a lit lamp. Now you're circuit building.


The Language

Facto programs describe circuit networks using four main concepts: signals, memory, entities, and bundles. Here's what each one does and how to use it.

Signals

Signals are values that flow through circuit networks. Every signal has a type (like "iron-plate" or "signal-A") and a value (an integer). You can do arithmetic on signals, and the compiler generates the necessary combinators:

Signal iron = ("iron-plate", 100);    # Explicit type and value
Signal count = 42;                     # Compiler picks a type
Signal doubled = count * 2;            # Arithmetic combinator
Signal isHigh = count > 50;            # Comparison combinator
Signal copper = iron | "copper-plate";  # Type cast

Conditional Values

Facto has a concise syntax for conditional values: condition : value. This outputs value when the condition is true, and 0 when false. It compiles to a single decider combinator, making it both readable and efficient:

Signal capped = (count > 100) : 100;   # 100 if over limit, else 0
Signal passed = (count <= 100) : count; # count if within limit, else 0
Signal clamped = capped + passed;       # Combined: clamped to max 100

This compiles more efficiently than the condition * value pattern you might use in raw Factorio circuits, where multiplication adds an extra combinator.

Memory

Memory stores values that persist across game ticks. Without memory, signals exist only for a single tick. Memory cells are implemented as decider combinators feeding back into themselves:

Memory counter: "signal-A";
counter.write(counter.read() + 1);     # Increment every tick

You can make writes conditional, which is how you build latches and state machines:

Memory buffer: "signal-B";
buffer.write(new_value, when=trigger > 0);   # Only write when triggered

Memory latch: "signal-L";
latch.write(1, set=turn_on > 0, reset=turn_off > 0);  # SR latch

Entities

Entities are Factorio objects you place in the world. You control them by assigning to their properties:

Entity lamp = place("small-lamp", 0, 0);
lamp.enable = count > 50;              # Turn on when count exceeds 50

Entity inserter = place("fast-inserter", 2, 0);
inserter.enable = chest_full == 0;     # Run when chest isn't full

Entity station = place("train-stop", 5, 0, {station: "Iron Pickup"});
station.send_to_train = departure_signal;

The compiler automatically wires entities to the combinators that compute their control signals.

Bundles

Bundles let you operate on multiple signals at once, using Factorio's "each" wildcard. This is how you build circuits that handle arbitrary item types without hardcoding each one:

Bundle resources = { ("iron-plate", 100), ("copper-plate", 80), ("coal", 50) };
Bundle doubled = resources * 2;           # Double all values
Signal iron = resources["iron-plate"];    # Access a single signal
Signal anyLow = any(resources) < 20;      # True if any resource is below 20
Bundle positive = (resources > 0) : resources;  # Filter to only positive values

Bundles are powerful for building generic circuits like balanced train loaders that work regardless of what items you're loading.

Functions

Functions let you reuse logic without copy-pasting combinator setups:

func clamp(Signal value, int min_val, int max_val) {
    Signal use_min = (value < min_val) : min_val;
    Signal use_max = (value > max_val) : max_val;
    Signal use_val = (value >= min_val && value <= max_val) : value;
    return use_min + use_max + use_val;
}

Signal safe_speed = clamp(raw_speed, 0, 100);

Functions are inlined at compile time—there's no runtime call overhead, just shared combinator logic.

Loops

For loops generate multiple entities at compile time:

for i in 0..8 {
    Entity lamp = place("small-lamp", i * 2, 0);
    lamp.enable = counter.read() == i;  # Chaser effect
}

This places 8 lamps, each enabled when the counter matches its index.


Automatic optimizations

The compiler applies several optimizations automatically. Common subexpressions are shared—if you compute counter.read() + 1 in three places, it only generates one arithmetic combinator. Compound conditions like (a > 5) && (b < 10) fold into a single multi-condition decider combinator (a Factorio 2.0 feature). Adding signals of the same type uses wire merging instead of extra combinators.

Wire routing is handled intelligently. The compiler assigns red and green wire colors to avoid signal conflicts, and inserts medium electric poles as relays when combinators are too far apart. For complex patterns like balanced loaders, it ensures signals merge correctly.

Type checking catches mistakes early:

ERROR: Type mismatch: Memory 'buffer' expects 'iron-plate' but write provides 'copper-plate'
WARNING: Mixed signal types in binary operation: 'iron-plate' + 'copper-plate'

Documentation

For deeper coverage, see the documentation in the doc/ directory:

The complete language reference is in LANGUAGE_SPEC.md, and all available entities and their properties are documented in doc/ENTITY_REFERENCE.md.


Command Line Usage

# Compile a file
factompile program.facto                    # Output blueprint to stdout
factompile program.facto -o output.txt      # Save to file

# Comp
View on GitHub
GitHub Stars395
CategoryDevelopment
Updated1d ago
Forks7

Languages

Python

Security Score

95/100

Audited on Apr 4, 2026

No findings