Created
February 1, 2025 00:10
-
-
Save Frank-Buss/4c6b8d53c970ab49e7a95aa217714615 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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