Skip to content

aproposmath/stationeers-pytrapic

Repository files navigation

Stationeers PyTrapIC - Transpiler from Python to IC10

PyTrapIC is a Python tool to convert Python code into Stationeers IC10. See below for features and limitations.

Simple Example

More examples are below

Python  Open in online editor
from stationeers_pytrapic.symbols import *

panels = SolarPanels  # port facing north
sensor = DaylightSensor(d0)  # port facing east

while True:
    panels.Horizontal = sensor.Horizontal
    panels.Vertical = 90 - sensor.Vertical
IC10
l r0 d0 Horizontal # Generated by PyTrapIC v0.2.3
sb HASH("StructureSolarPanel") Horizontal r0
l r0 d0 Vertical
sub r1 90 r0
sb HASH("StructureSolarPanel") Vertical r1
j 0

How to use

pip install git+https://github.com/aproposmath/stationeers-pytrapic.git
python -m stationeers-pytrapic some_simple_code.py

Features

Python Features

  • Common Python features:
    • +, -, *, /, %, +=, -=, *=, /=, and, or, not
    • if, else, while, for, break, continue
    • list (only compile-time constant lists)
    • functions
    • global
  • Type-safe access to structures (WallHeater(d0).Activate = True)
  • Batch operations (use plural name) (AutoLathes.Activate = True)
  • Named batch operations with [] operator (my_value = WallLights["Some Name"].On.Maximum)
  • Slot access, either by name (furnace.Export.Occupied) or by index (furnace.slot0.Occupied)
  • Enums for all IC10 types like LogicType.On, DisplayMode.Celsius, SortingClass, etc.
  • Auto-completion for logic types / slot types etc.
  • Intrinsics - all IC10 instructions can be called as functions, e.g. a = pop() or x = lb(HASH("StructureWallHeater", "On", "Maximum")) (example below)
  • Optimizations (evaluate constant expressions, function inlining, remove unused code)
  • @constexpr function decorator to force evaluation at compile time (example below)
  • Compile flags (example below)

Planned Features

  • Access slot by variable index (furnace.slots[i].Occupied)
  • better register allocation (check scope in ic10 code instead of python code for more granularity)
  • pass function arguments/return values in registers (currently only via stack)
  • allow structures as function arguments/return values (currently only simple values like float are allowed)
  • you tell me!

NOT planned

  • lambda functions
  • anything that is not supported by IC10 (e.g. classes, exceptions, etc.)
  • anything provided by the Python standard library (e.g. math, random, etc.)
  • list, dict, set, tuple, comprehensions (maybe later, but only for constant expressions)

Examples

Logic Types
Python  Open in online editor
from stationeers_pytrapic.symbols import *

# Simple access to logic types

# access devices by their symbolic names (d0-d5, db)
db.Setting = d0.RatioCarbonDioxide

# simple calculations will get optimized away
d1.Setting = 273.15 + 22

# use structure names to get proper autocompletion
sensor = DaylightSensor(d5)
db.Setting = sensor.Horizontal
IC10
l r0 d0 RatioCarbonDioxide # Generated by PyTrapIC v0.2.3
s db Setting r0
s d1 Setting 295.15
l r0 d5 Horizontal
s db Setting r0
Batch Operations
Python  Open in online editor
from stationeers_pytrapic.symbols import *

# Batch operations

# Use the plural name (append "s") to do batch operations
GrowLights.On = True

# Use bracket operator for named batch instructions
GrowLights["Potatos"].On = True

# Access batch properties, either first by batch mode, or by property name
charge0 = Batteries.Average.Charge
charge1 = Batteries.Charge.Average

# having batch mode first is useful if you access the same type multiple times
batteries_bank1 = Batteries["Bank1"].Average

if batteries_bank1.Charge < 0.2:
    SolidFuelGenerators.On = True
    # wait until charge is above 30%
    while batteries_bank1.Charge < 0.3:
        pass
    SolidFuelGenerators.On = False
IC10
sb HASH("StructureGrowLight") On 1 # Generated by PyTrapIC v0.2.3
sbn HASH("StructureGrowLight") HASH("Potatos") On 1
lb r0 HASH("StructureBattery") Charge Average
lb r1 HASH("StructureBattery") Charge Average
lbn r2 HASH("StructureBattery") HASH("Bank1") Charge Average
bge r2 0.2 11
sb HASH("StructureSolidFuelGenerator") On 1
lbn r2 HASH("StructureBattery") HASH("Bank1") Charge Average
bge r2 0.3 10
j 7
sb HASH("StructureSolidFuelGenerator") On 0
Loops
Python  Open in online editor
from stationeers_pytrapic.symbols import *

def while_condition():
    # blocking wait until pressure is 0
    while GasSensors["Airlock"].Average.Pressure > 0:
        pass
        
def for_range():
    sum = 0
    for i in range(10):
        sum += stack[i]
    return sum
        
def for_list():
    for name_hash in [HASH("O2"), HASH("N2"), HASH("VOL"), HASH("CO2")]:
        pressure = PipeAnalysizers[name_hash].Average.Pressure
        ConsoleLED1x2s[name_hash].Setting = pressure

# typical endless loop as main entry point
while True:
    while_condition()
    db.Setting = for_range()
    for_list()
    for_range()
IC10
lbn r1 HASH("StructureGasSensor") HASH("Airlock") Pressure Average
ble r1 0 3 # Generated by PyTrapIC v0.2.3
j 0
jal 22
get r0 db 511
s db Setting r0
move r1 7
move r1 HASH("O2")
jal 16
move r1 HASH("N2")
jal 16
move r1 HASH("VOL")
jal 16
move r1 HASH("CO2")
jal 16
j 19
lbn r2 HASH("StructurePipeAnalysizer") r1 Pressure Average
sbn HASH("StructureConsoleLED1x2") r1 Setting r2
j ra
jal 22
get r0 db 511
j 0
move r1 0
move r2 0
bge r2 10 29
get r3 db r2
add r1 r1 r3
add r2 r2 1
j 24
put db 511 r1
j ra
Misc
Python  Open in online editor
from stationeers_pytrapic.symbols import *

# assign boolean value directly from comparison
GrowLights.On = DaylightSensor(d0).Vertical > 90

# if-else expression will use "select" IC10 instruction
db.Setting = STR("Day") if DaylightSensor(d0).Vertical < 90 else STR("Night")
IC10
l r1 d0 Vertical # Generated by PyTrapIC v0.2.3
sgt r0 r1 90
sb HASH("StructureGrowLight") On r0
l r0 d0 Vertical
slt r1 r0 90
select r2 r1 STR("Day") STR("Night")
s db Setting r2
Stack
Python  Open in online editor
from stationeers_pytrapic.symbols import *

# "stack" variable gives access to the IC10 stack directly
stack[0] = 123
db.Settings = stack[2] + stack[3]

# create stack variables for other devices (dX or by RefId)
stack_d3 = Stack(d3)
stack_d3[0] = 456

stack_dev = Stack(ref_id=0x2355)
stack_dev[0] = 789
IC10
poke 0 123 # Generated by PyTrapIC v0.2.3
get r0 db 2
get r1 db 3
add r2 r0 r1
s db Settings r2
put d3 0 456
putd 9045 0 789
Constexpr
Python  Open in online editor
from stationeers_pytrapic.symbols import *

@constexpr
def filter_by_sorting_class(cls, negate=False):
    condop = ConditionOperation.Equals
    if negate:
        condop = ConditionOperation.NotEquals
    condop = condop << 8
    cls = cls << 16
    return cls + condop + SorterInstruction.FilterSortingClassCompare

@constexpr
def filter_by_name(name, negate=False):
    hash = HASH(name) << 8
    instruction = SorterInstruction.FilterPrefabHashEquals
    if negate:
        instruction = SorterInstruction.FilterPrefabHashNotEquals
    return hash + instruction

# access the stack of a sorter
ores_sorter_stack = Stack(d0)
ores_sorter_stack[0] = filter_by_sorting_class(SortingClass.Ores)

no_ices_sorter_stack = Stack(d1)
no_ices_sorter_stack[0] = filter_by_sorting_class(SortingClass.Ices, negate=True)

# you can also get the stack by the reference id
only_steel = Stack(ref_id=0x3455)
only_steel[0] = filter_by_name("ItemSteelIngot")
IC10
put d0 0 $90003 # Generated by PyTrapIC v0.2.3
put d1 0 $A0303
putd $3455 0 -167626437375
Intrinsics
Python  Open in online editor
from stationeers_pytrapic.symbols import *

# Builtin functions STR and HASH
# Depending on compiler settings, they will be evaluated at compile time
db.Setting = STR("Hello")
db.Setting = HASH("StructureAutholathe")

# all ic10 instructions are available as python functions
push(HASH("Some name"))

# instructions with an output argument will return a value and have one operand less
db.Setting = pop()

# any string argument of an intrinsic will be emited as-is
# but variables (bat_name) are also supported
but_name = HASH("Main Battery")
charge = lbn("HASH('StructureBattery')", bat_name, "Average", LogicType.Ratio)
Diodes["Low Power"].On = charge < 0.1
IC10
s db Setting STR("Hello") # Generated by PyTrapIC v0.2.3
s db Setting HASH("StructureAutholathe")
push HASH("Some name")
pop r1
s db Setting r1
lbn r0 HASH('StructureBattery')  Average Ratio
slt r1 r0 0.1
sbn HASH("StructureDiode") HASH("Low Power") On r1
Compiler Options
Python  Open in online editor
from stationeers_pytrapic.symbols import *

# Available compiler options
# name              Default    Description
# compact           False      evaluates HASH/STR (if shorter), use numbers instead of enums
# inline-functions  True       Inline function calls if possible
# remove-labels     False      removes jump labels
# append-version    True       add comment with PyTrapIC version

# Usage is demonstated below. Prepend "no-" for negating options.
# The options can appear anywhere in the code and will be applied globally.

# pytrapic: compact
# pytrapic: no-append-version
# pytrapic: inline-functions
# pytrapic: remove-labels

def some_function():
    display = ConsoleLED1x2(d0)
    display.Mode = DisplayMode.String
    display.Setting = STR("Hi")

while True:
    some_function()
IC10
s d0 3 10
s d0 12 26952
j 0

How does it work

The transpiler itself is written in Python and uses astroid to parse python code.

The web application uses Pyodide to run the transpiler in the browser. The editor is based on monaco and uses monaco-pyright-lsp for code completion. No data is sent to the server, everything is running in the browser.

Developer / Contributor Setup

Python Environment

The project requires Python 3.10+. A .venv is the recommended way to work locally.

Create and activate the venv (first time only):

python3 -m venv .venv
source .venv/bin/activate

Install the package in editable mode with its dependencies:

pip install -e .

For building wheels, generating type stubs, and running the webapp data pipeline, also install:

pip install build wheel pyright setuptools setuptools_scm

macOS note: The system Python (e.g. Homebrew Python 3.14) is PEP 668 "externally managed" and will refuse pip install without --break-system-packages. Always use the project .venv.

Running Tests

pip install pytest
pytest

Tests live in test/ and compare transpiler output against .ref reference files.

Project Structure

src/stationeers_pytrapic/   # transpiler core (Python)
test/                        # pytest test cases and reference outputs
mod/                         # in-game C# mod (PyTrapIC.cs)
webapp/                      # browser IDE (TypeScript + Vite)
scripts/                     # code generation and build helpers
pyproject.toml               # package metadata and build config

Version is managed by setuptools_scm from git tags, written to src/stationeers_pytrapic/_version.py at build time.

Webapp

The browser IDE has its own setup steps (Node.js, data file generation). See webapp/README.md.

C# Mod

The in-game BepInEx mod has its own setup steps (.NET SDK, game DLLs, mod dependencies). See the Developer Setup section in mod/README.md.

About

No description, website, or topics provided.

Resources

License

Stars

15 stars

Watchers

3 watching

Forks

Packages

 
 
 

Contributors

Languages