Skip to content

Instantly share code, notes, and snippets.

@Frank-Buss
Created February 1, 2025 00:10
Show Gist options
  • Save Frank-Buss/4c6b8d53c970ab49e7a95aa217714615 to your computer and use it in GitHub Desktop.
Save Frank-Buss/4c6b8d53c970ab49e7a95aa217714615 to your computer and use it in GitHub Desktop.
# run like this:
# julia -O3 --check-bounds=no tm.jl busy-beaver-5.json --fast
using ArgParse
using JSON
# ANSI codes for inverse video and reset
const INVERSE = "\u001B[7m"
const RESET = "\u001B[0m"
function parse_commandline()
s = ArgParseSettings(
prog = "tm",
version = "0.1.0",
description = "A Turing Machine simulator that loads a machine from JSON"
)
@add_arg_table! s begin
"--fast"
help = "if present, only print every 100k steps (faster)"
action = "store_true"
"filename"
help = "the JSON file describing the Turing Machine"
required = true
end
return parse_args(s)
end
# Converts a JSON value to string, removing surrounding quotes
function to_str(val)
if val isa String
return val
else
return string(val)
end
end
function main()
# Parse command line arguments
args = parse_commandline()
# Read and parse JSON file
text = try
read(args["filename"], String)
catch e
println(stderr, "Error reading file $(args["filename"]): $e")
exit(1)
end
tm = try
JSON.parse(text)
catch e
println(stderr, "Error parsing JSON in $(args["filename"]): $e")
exit(1)
end
# Mandatory fields: "blank" & "initial"
blank = get(tm, "blank", nothing)
initial = get(tm, "initial", nothing)
if isnothing(blank) || isnothing(initial)
println(stderr, "Error: JSON missing mandatory fields 'blank' or 'initial'.")
exit(1)
end
# Optional fields
name = get(tm, "name", nothing)
desc = get(tm, "description", nothing)
author = get(tm, "author", nothing)
date = get(tm, "date", nothing)
link = get(tm, "link", nothing)
paper = get(tm, "paper", nothing)
tape_str = get(tm, "tape", "")
position = get(tm, "position", 0)
# Build transitions dictionary
transitions = Dict{Tuple{String,String}, Tuple{String,String,String}}()
if haskey(tm, "transitions") && tm["transitions"] isa Vector
for rule in tm["transitions"]
if rule isa Vector && length(rule) == 5
old_state = to_str(rule[1])
old_symbol = to_str(rule[2])
new_symbol = to_str(rule[3])
direction = uppercase(to_str(rule[4]))
new_state = to_str(rule[5])
transitions[(old_state, old_symbol)] = (new_symbol, direction, new_state)
end
end
end
# Create initial tape
tape = isempty(tape_str) ? [string(blank)] : [string(c) for c in tape_str]
# Position fix
while position < 0
pushfirst!(tape, string(blank))
position += 1
end
while position >= length(tape)
push!(tape, string(blank))
end
# Print summary
println("\nStarting Turing machine")
!isnothing(name) && println("Name: $name")
!isnothing(desc) && println("Description: $desc")
!isnothing(author) && println("Author: $author")
!isnothing(date) && println("Date: $date")
!isnothing(link) && println("Link: $link")
!isnothing(paper) && println("Paper: $paper")
println("(blank='$blank', initial='$initial', position=$position)\n")
# Run the Turing machine
fast_mode = args["fast"]
step = 0
state = string(initial)
while true
do_print = !fast_mode || step % 100_000 == 0
if do_print
joined_tape = join([i == position + 1 ? INVERSE * sym * RESET : sym for (i, sym) in enumerate(tape)])
println("Step $(lpad(step, 6)) State=$(rpad(state, 8)) Pos=$(lpad(position, 5)) Tape=$joined_tape")
end
# Current symbol
current_symbol = tape[position + 1] # Julia is 1-based
# Find rule
key = (state, current_symbol)
rule = get(transitions, key, nothing)
if isnothing(rule)
rule = get(transitions, (state, "*"), nothing)
if !isnothing(rule)
new_symbol, direction, new_state = rule
new_symbol = new_symbol == "*" ? current_symbol : new_symbol
else
println("\nHalted: no rule for state=$state and symbol='$current_symbol'")
break
end
else
new_symbol, direction, new_state = rule
new_symbol = new_symbol == "*" ? current_symbol : new_symbol
end
tape[position + 1] = new_symbol
state = new_state
# Handle movement
if direction == "L"
position -= 1
if position < 0
pushfirst!(tape, string(blank))
position = 0
end
elseif direction == "R"
position += 1
while position >= length(tape)
push!(tape, string(blank))
end
end
step += 1
end
# Final tape
final_tape = join(tape)
while startswith(final_tape, blank)
final_tape = final_tape[length(blank)+1:end]
end
while endswith(final_tape, blank)
final_tape = final_tape[1:end-length(blank)]
end
println("\nFinal tape: $final_tape")
println("Steps: $step")
# Print histogram
histogram = Dict{String,Int}()
for sym in tape
histogram[sym] = get(histogram, sym, 0) + 1
end
println("Histogram:")
for (sym, count) in histogram
println(" symbol: $sym, count: $count")
end
end
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment