goexmars

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2026 License: MIT Imports: 14 Imported by: 1

README

goexmars

exMars binding for go. This uses the purego package to call the shared library, without any cgo. It also includes other goodies like go structs for the parsed warriors and similarity helpers.

Features
  • Fight/FightNamed support 1 to 6 warriors for fights.
  • Assemble returns normalized assembled Redcode (labels/macros/comments are not preserved) as string.
  • AssembleParsed parses commands from normalized Redcode and reads ;name, ;author, and numeric END from the original source.
  • Similarity helper to compute similarity between two warriors [0,1].

Usage

Fight
package main

import (
	"fmt"
	"log"

	"github.com/BigJk/goexmars"
)

func main() {
	imp := `
        ;redcode-94
        ;name Imp
        MOV 0, 1
        END
`

	dwarf := `
        ;redcode-94
        ;name Dwarf
        ADD #4, 3
        MOV 2, @2
        JMP -2, 0
        DAT #0, #0
        END
`

	cfg := goexmars.DefaultConfig.SetRounds(50)

	result, err := goexmars.Fight([]string{imp, dwarf}, cfg)
	if err != nil {
		log.Printf("fight failed: %v", err)
	}

	fmt.Printf("wins=%v ties=%d\n", result.Wins, result.Ties)
	if result.Diagnostics != "" {
		fmt.Printf("diagnostics:\n%s\n", result.Diagnostics)
	}
}
Validate
package main

import (
	"fmt"

	"github.com/BigJk/goexmars"
)

func main() {
	warrior := `
;redcode-94
;name Broken
MOV.Z 0, 1
END
`

	if err := goexmars.Validate(warrior, goexmars.DefaultConfig); err != nil {
		fmt.Printf("invalid warrior:\n%s\n", err)
		return
	}

	fmt.Println("warrior is valid")
}
FightNamed
package main

import (
	"fmt"

	"github.com/BigJk/goexmars"
)

func main() {
	imp := `
;redcode-94
;name Imp
MOV 0, 1
END
`

	result, err := goexmars.FightNamed(map[string]string{
		"alpha": imp,
		"beta":  imp,
		"gamma": imp,
	}, goexmars.DefaultConfig.SetRounds(10))
	if err != nil {
		fmt.Println("fight error:", err)
	}

	alphaWins, ties := result.Get("alpha")
	fmt.Printf("alpha wins=%d ties=%d\n", alphaWins, ties)
	fmt.Printf("all wins=%v\n", result.Wins)
}
Assemble
package main

import (
	"fmt"

	"github.com/BigJk/goexmars"
)

func main() {
	warrior := `
;redcode-94
;name Example
step  DAT #0, #0
start MOV step, >step
      JMP start
END 0
`

	assembled, err := goexmars.Assemble(warrior, goexmars.DefaultConfig)
	if err != nil {
		fmt.Printf("assemble failed:\n%s\n", err)
		return
	}

	fmt.Println("normalized redcode:")
fmt.Println(assembled)
}
AssembleParsed
package main

import (
	"fmt"

	"github.com/BigJk/goexmars"
)

func main() {
	warrior := `
;redcode-94
;name Example
;author You
start MOV 0, 1
END 0
`

	parsed, err := goexmars.AssembleParsed(warrior, goexmars.DefaultConfig)
	if err != nil {
		fmt.Println("assemble/parse error:", err)
		return
	}

	fmt.Printf("name=%q author=%q end=%d\n", parsed.Name, parsed.Author, parsed.End)
	for _, cmd := range parsed.Commands {
		fmt.Println(cmd.String())
	}

	// Re-render back to normalized redcode (+ parsed metadata)
	fmt.Println(parsed.String())
}
Shared Library

You need the shared library to run the code. You can build it yourself or download it from the releases page. It needs to be placed in the ./lib directory relative to the executable, or define the GOEXMARS_LIB_PATH environment variable to point to the shared library.

Build shared library

  • The C sources are under ./exmars
  • Build the shared library before building Go code:
cd exmars
./build.sh

This produces libexmars.dylib on macOS or libexmars.so on Linux.

Credits

Documentation

Overview

Package goexmars provides a Go wrapper around the exmars corewar mars.

The package loads a platform-specific shared library at runtime and exposes a single Fight API for running fights with 1 to 6 warriors.

Index

Constants

View Source
const AddressingModeCount = int(AddressingBIndirectPost) + 1

AddressingModeCount is the number of supported addressing modes.

View Source
const ModifierCount = int(ModifierI) + 1

ModifierCount is the number of supported modifiers.

View Source
const OpCodeCount = int(OpCodeNOP) + 1

OpCodeCount is the number of supported opcodes.

Variables

View Source
var DefaultConfig = FightConfig{
	Rounds:        250,
	CoreSize:      8000,
	Cycles:        80000,
	MaxWarriorLen: 100,
	MaxProcess:    8000,
	MinSep:        100,
	PSpaceSize:    500,
	FixPos:        0,
}

DefaultConfig is the standard corewar configuration preset.

View Source
var FortressConfig = FightConfig{
	Rounds:        1000,
	CoreSize:      8000,
	Cycles:        80000,
	MaxWarriorLen: 400,
	MaxProcess:    80,
	MinSep:        4000,
	PSpaceSize:    500,
	FixPos:        0,
}

FortressConfig is the fortress configuration preset. Not fully supported: source preset uses Write limit = 4000 (CoreSize = 8000).

View Source
var LimitedProcessConfig = FightConfig{
	Rounds:        250,
	CoreSize:      8000,
	Cycles:        80000,
	MaxWarriorLen: 200,
	MaxProcess:    8,
	MinSep:        200,
	PSpaceSize:    500,
	FixPos:        0,
}

LimitedProcessConfig is the limited-process configuration preset.

View Source
var MediumProcessConfig = FightConfig{
	Rounds:        250,
	CoreSize:      8000,
	Cycles:        80000,
	MaxWarriorLen: 100,
	MaxProcess:    64,
	MinSep:        100,
	PSpaceSize:    1,
	FixPos:        0,
}

MediumProcessConfig is the medium-process configuration preset.

View Source
var MetaswitchConfig = FightConfig{
	Rounds:        100,
	CoreSize:      8000,
	Cycles:        40000,
	MaxWarriorLen: 100,
	MaxProcess:    8000,
	MinSep:        100,
	PSpaceSize:    500,
	FixPos:        0,
}

MetaswitchConfig is the metaswitch configuration preset.

View Source
var NanoConfig = FightConfig{
	Rounds:        250,
	CoreSize:      80,
	Cycles:        800,
	MaxWarriorLen: 5,
	MaxProcess:    80,
	MinSep:        5,
	PSpaceSize:    5,
	FixPos:        0,
}

NanoConfig is the nano hill configuration preset.

View Source
var TinyConfig = FightConfig{
	Rounds:        250,
	CoreSize:      800,
	Cycles:        8000,
	MaxWarriorLen: 20,
	MaxProcess:    800,
	MinSep:        20,
	PSpaceSize:    50,
	FixPos:        0,
}

TinyConfig is the tiny hill configuration preset.

View Source
var TinyLimitedProcessConfig = FightConfig{
	Rounds:        250,
	CoreSize:      800,
	Cycles:        8000,
	MaxWarriorLen: 50,
	MaxProcess:    8,
	MinSep:        50,
	PSpaceSize:    50,
	FixPos:        0,
}

TinyLimitedProcessConfig is the tiny limited-process configuration preset.

View Source
var TourneyConfig = FightConfig{
	Rounds:        250,
	CoreSize:      8192,
	Cycles:        100000,
	MaxWarriorLen: 300,
	MaxProcess:    8000,
	MinSep:        300,
	PSpaceSize:    512,
	FixPos:        0,
}

TourneyConfig is the tourney configuration preset.

Functions

func Assemble

func Assemble(warrior string, cfg FightConfig) (string, error)

Assemble assembles a single warrior and returns its normalized instruction listing.

The returned source is a disassembly of the assembled instructions (not the original source with labels/macros/comments). If exmars reports an assembly failure, Assemble returns an error containing diagnostics when available.

func AssembleNormalized

func AssembleNormalized(warrior string, cfg FightConfig, opts RedcodeFormatOptions) (string, error)

AssembleNormalized assembles a warrior and returns normalized Redcode text formatted according to opts.

Unlike Assemble, this output can omit metadata and/or the END line and is rendered via the ParsedWarrior formatter.

func CommandDistance

func CommandDistance(a, b []Command) float64

CommandDistance computes a weighted Levenshtein distance between two command sequences.

func CommandDistanceWithCoreSize

func CommandDistanceWithCoreSize(a, b []Command, coreSize int) float64

CommandDistanceWithCoreSize computes a weighted Levenshtein distance between two command sequences.

func CommandDistanceWithWeights

func CommandDistanceWithWeights(a, b []Command, w CommandDistanceWeights) float64

CommandDistanceWithWeights computes a weighted Levenshtein distance between two command sequences.

func CommandSubstitutionCost

func CommandSubstitutionCost(a, b Command, w CommandDistanceWeights) float64

CommandSubstitutionCost returns the weighted substitution cost between two commands.

func Distance

func Distance(a, b ParsedWarrior) float64

Distance computes a weighted distance between two parsed warriors.

func DistanceWithCoreSize

func DistanceWithCoreSize(a, b ParsedWarrior, coreSize int) float64

DistanceWithCoreSize computes a weighted distance between two parsed warriors with a custom core size.

func DistanceWithWeights

func DistanceWithWeights(a, b ParsedWarrior, w CommandDistanceWeights) float64

DistanceWithWeights computes a weighted distance between two parsed warriors.

func Similarity

func Similarity(a, b ParsedWarrior) float64

Similarity computes a normalized similarity score in [0,1].

1 means identical command sequences. 0 means maximally different under the normalization denominator (worst-case insert/delete for sequence lengths).

func SimilarityWithCoreSize

func SimilarityWithCoreSize(a, b ParsedWarrior, coreSize int) float64

SimilarityWithCoreSize computes a normalized similarity score in [0,1].

func SimilarityWithWeights

func SimilarityWithWeights(a, b ParsedWarrior, w CommandDistanceWeights) float64

SimilarityWithWeights computes a normalized similarity score in [0,1].

func Validate

func Validate(warrior string, cfg FightConfig) error

Validate performs a quick validity check for a single warrior.

It runs a single-round self-fight and returns a non-nil error when exmars reports an assembly/setup failure. The returned error message is the captured diagnostics string when available.

Types

type AddressingMode

type AddressingMode byte

AddressingMode is a Redcode operand addressing mode.

const (
	AddressingImmediate AddressingMode = iota
	AddressingDirect
	AddressingAIndirect
	AddressingBIndirect
	AddressingAIndirectPre
	AddressingBIndirectPre
	AddressingAIndirectPost
	AddressingBIndirectPost
)

Supported addressing modes.

func (AddressingMode) String

func (m AddressingMode) String() string

String returns the Redcode operand prefix for the addressing mode.

type Benchmark

type Benchmark struct {
	// Warriors contains the parsed benchmark warriors.
	Warriors []ParsedWarrior
	// Config is used for benchmark fights and for ScoreString assembly/parsing.
	Config FightConfig
}

Benchmark is a set of parsed warriors used for fight-based scoring.

func BenchmarkFromFolder

func BenchmarkFromFolder(folder string, cfg FightConfig) (Benchmark, error)

BenchmarkFromFolder loads all .red files in folder into a benchmark.

Files are loaded in lexicographic filename order for deterministic results.

func (Benchmark) Score

func (b Benchmark) Score(warrior ParsedWarrior) (BenchmarkScore, error)

Score fights warrior against all benchmark warriors and aggregates wins/losses/ties.

func (Benchmark) ScoreString

func (b Benchmark) ScoreString(warrior string) (BenchmarkScore, error)

ScoreString assembles and parses warrior, then fights it against the benchmark set.

type BenchmarkScore

type BenchmarkScore struct {
	Wins   int
	Losses int
	Ties   int
}

BenchmarkScore is the aggregated result of fighting against a benchmark set.

Points are ICWS-style: 3 per win, 1 per tie, 0 per loss.

func (BenchmarkScore) Performance

func (s BenchmarkScore) Performance() float64

Performance returns the normalized ICWS-style performance in [0,1].

func (BenchmarkScore) Points

func (s BenchmarkScore) Points() int

Points returns the ICWS-style point total (3*Wins + Ties).

func (BenchmarkScore) Rounds

func (s BenchmarkScore) Rounds() int

Rounds returns the total number of benchmark rounds represented by the score.

type Command

type Command struct {
	OpCode          OpCode
	Modifier        Modifier
	AddressingModeA AddressingMode
	A               int
	AddressingModeB AddressingMode
	B               int
}

Command is a single normalized Redcode instruction.

func ParseAssembledCommands

func ParseAssembledCommands(assembled string) ([]Command, error)

ParseAssembledCommands parses normalized assembled Redcode instructions into commands.

func (Command) String

func (c Command) String() string

String renders the command as normalized Redcode.

type CommandDistanceWeights

type CommandDistanceWeights struct {
	Insert          float64
	Delete          float64
	Opcode          float64
	Modifier        float64
	AddressingModeA float64
	AddressingModeB float64
	OperandA        float64
	OperandB        float64
	CoreSize        int
}

CommandDistanceWeights controls weighted edit-distance costs for commands.

func DefaultCommandDistanceWeights

func DefaultCommandDistanceWeights() CommandDistanceWeights

DefaultCommandDistanceWeights returns the default weighted edit-distance costs.

type EditKind

type EditKind byte

EditKind identifies the type of command edit operation.

const (
	EditKeep EditKind = iota
	EditSubstitute
	EditInsert
	EditDelete
)

Supported edit operation kinds.

func (EditKind) String

func (k EditKind) String() string

String returns the symbolic name of the edit kind.

type EditOp

type EditOp struct {
	Kind   EditKind
	AIndex int
	BIndex int
	From   Command
	To     Command
	Cost   float64
}

EditOp describes a single weighted edit operation between command sequences.

func CommandEditScript

func CommandEditScript(a, b []Command) []EditOp

CommandEditScript computes a weighted edit script between two command sequences.

func CommandEditScriptWithWeights

func CommandEditScriptWithWeights(a, b []Command, w CommandDistanceWeights) []EditOp

CommandEditScriptWithWeights computes a weighted edit script between two command sequences.

func EditScript

func EditScript(a, b ParsedWarrior) []EditOp

EditScript computes a weighted edit script between two parsed warriors.

func EditScriptWithCoreSize

func EditScriptWithCoreSize(a, b ParsedWarrior, coreSize int) []EditOp

EditScriptWithCoreSize computes a weighted edit script between two parsed warriors.

func EditScriptWithWeights

func EditScriptWithWeights(a, b ParsedWarrior, w CommandDistanceWeights) []EditOp

EditScriptWithWeights computes a weighted edit script between two parsed warriors.

type FightConfig

type FightConfig struct {
	CoreSize      int `json:"core_size"`
	Cycles        int `json:"cycles"`
	MaxProcess    int `json:"max_process"`
	Rounds        int `json:"rounds"`
	MaxWarriorLen int `json:"max_warrior_len"`
	MinSep        int `json:"min_sep"`
	PSpaceSize    int `json:"p_space_size"`
	FixPos        int `json:"fix_pos"`
}

FightConfig holds the simulation parameters for a fight. It supports fluent configuration via SetXxx chainable methods.

func NewFightConfig

func NewFightConfig() FightConfig

NewFightConfig returns an empty config that can be configured fluently.

func (FightConfig) SetCoreSize

func (c FightConfig) SetCoreSize(v int) FightConfig

SetCoreSize returns a copy of c with CoreSize set to v.

func (FightConfig) SetCycles

func (c FightConfig) SetCycles(v int) FightConfig

SetCycles returns a copy of c with Cycles set to v.

func (FightConfig) SetFixPos

func (c FightConfig) SetFixPos(v int) FightConfig

SetFixPos returns a copy of c with FixPos set to v.

func (FightConfig) SetMaxProcess

func (c FightConfig) SetMaxProcess(v int) FightConfig

SetMaxProcess returns a copy of c with MaxProcess set to v.

func (FightConfig) SetMaxWarriorLen

func (c FightConfig) SetMaxWarriorLen(v int) FightConfig

SetMaxWarriorLen returns a copy of c with MaxWarriorLen set to v.

func (FightConfig) SetMinSep

func (c FightConfig) SetMinSep(v int) FightConfig

SetMinSep returns a copy of c with MinSep set to v.

func (FightConfig) SetPSpaceSize

func (c FightConfig) SetPSpaceSize(v int) FightConfig

SetPSpaceSize returns a copy of c with PSpaceSize set to v.

func (FightConfig) SetRounds

func (c FightConfig) SetRounds(v int) FightConfig

SetRounds returns a copy of c with Rounds set to v.

func (FightConfig) Validate

func (c FightConfig) Validate() error

Validate checks whether the config contains a sane set of values.

PSpaceSize may be zero to use exmars' default behavior. FixPos may be zero to use exmars' default placement behavior.

type FightNamedResult

type FightNamedResult struct {
	FightResult
	// contains filtered or unexported fields
}

FightNamedResult is a FightResult with name-based lookup helpers.

func FightNamed

func FightNamed(warriors map[string]string, cfg FightConfig) (FightNamedResult, error)

FightNamed runs a fight for 1 to 6 named warriors.

Warrior map keys are used as stable identifiers for result lookup. Internally names are sorted to make evaluation order deterministic. Use result.Get(name) to map a warrior name back to its sole-win count and the shared tie count.

func (FightNamedResult) Get

func (r FightNamedResult) Get(name string) (int, int)

Get returns the sole-win count for name and the shared tie count.

If name is unknown, Get returns 0 and the tie count.

type FightResult

type FightResult struct {
	// Wins contains the sole-win count for each warrior in the input order.
	Wins []int
	// Ties contains rounds without a sole winner.
	Ties int
	// Diagnostics contains exmars warnings/errors captured during assembly/fight setup.
	Diagnostics string
}

FightResult contains the outcome of a fight.

func Fight

func Fight(warriors []string, cfg FightConfig) (FightResult, error)

Fight runs a fight for 1 to 6 warriors and returns the fight result.

On parser/setup failure, the returned FightResult contains sentinel values (negative wins/ties) and error is set to the diagnostics string when available.

func (FightResult) Failed

func (r FightResult) Failed() bool

Failed reports whether the result encodes a sentinel failure from the C layer.

type FingerprintOptions

type FingerprintOptions struct {
	IncludeName   bool
	IncludeAuthor bool
	IncludeEnd    bool
}

FingerprintOptions controls which normalized warrior parts are included in a fingerprint.

func DefaultFingerprintOptions

func DefaultFingerprintOptions() FingerprintOptions

DefaultFingerprintOptions returns the default fingerprinting options.

By default metadata is ignored so the fingerprint tracks normalized code and END position only.

type Modifier

type Modifier byte

Modifier is a Redcode instruction modifier.

const (
	ModifierF Modifier = iota
	ModifierA
	ModifierB
	ModifierAB
	ModifierBA
	ModifierX
	ModifierI
)

Supported modifiers.

func (Modifier) String

func (m Modifier) String() string

String returns the Redcode mnemonic for the modifier.

type OpCode

type OpCode byte

OpCode is a normalized Redcode opcode.

const (
	OpCodeDAT OpCode = iota
	OpCodeMOV
	OpCodeADD
	OpCodeSUB
	OpCodeMUL
	OpCodeDIV
	OpCodeMOD
	OpCodeJMP
	OpCodeJMZ
	OpCodeJMN
	OpCodeDJN
	OpCodeSPL
	OpCodeCMP
	OpCodeSEQ
	OpCodeSNE
	OpCodeSLT
	OpCodeLDP
	OpCodeSTP
	OpCodeNOP
)

Supported opcodes.

func (OpCode) String

func (o OpCode) String() string

String returns the Redcode mnemonic for the opcode.

type ParsedWarrior

type ParsedWarrior struct {
	Name      string
	Author    string
	End       int
	Commands  []Command
	Assembled string
}

ParsedWarrior is a structured representation of an assembled warrior.

func AssembleParsed

func AssembleParsed(warrior string, cfg FightConfig) (ParsedWarrior, error)

AssembleParsed assembles warrior and parses the normalized result into a Go struct.

Name and Author are parsed from the original source if present. End and Commands are parsed from the normalized assembled Redcode returned by Assemble.

func (ParsedWarrior) Fingerprint

func (w ParsedWarrior) Fingerprint() string

Fingerprint returns a SHA-256 fingerprint of the parsed warrior.

func (ParsedWarrior) FingerprintWithOptions

func (w ParsedWarrior) FingerprintWithOptions(opts FingerprintOptions) string

FingerprintWithOptions returns a SHA-256 fingerprint of the parsed warrior using opts.

func (ParsedWarrior) Format

func (w ParsedWarrior) Format(opts RedcodeFormatOptions) string

Format renders the parsed warrior to Redcode text using the provided options.

func (ParsedWarrior) String

func (w ParsedWarrior) String() string

String renders the parsed warrior back to Redcode text.

type RedcodeFormatOptions

type RedcodeFormatOptions struct {
	IncludeName   bool
	IncludeAuthor bool
	IncludeEnd    bool
}

RedcodeFormatOptions controls how a ParsedWarrior is rendered back to Redcode text.

func DefaultRedcodeFormatOptions

func DefaultRedcodeFormatOptions() RedcodeFormatOptions

DefaultRedcodeFormatOptions returns the default Redcode rendering options.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL