123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- local modname = minetest.get_current_modname()
- local S = minetest.get_translator(modname)
- local MACHINE_TICK_INTERVAL = 0.5
- local MAX_MEMORY_RANGE = 512
- local tokenize = _G[modname].tokenize
- local parse = _G[modname].parse
- local compute_step = _G[modname].compute_step
- local node_formspec =
- {
- "size[8,7.5]",
- "label[2,0.5;" .. S("Program tape") .. "]",
- "list[context;main;2,1;1,1;]",
- nil,
- nil,
- "list[current_player;main;0,3.5;8,4;]",
- "listring[]",
- }
- local function get_node_formspec(running)
- node_formspec[4] = "label[4,0.5;" .. S("Current status:@n@1", running and S("running") or S("halted")) .. "]"
- node_formspec[5] = "button[4,1.3;1.7,1;" .. (running and "halt;" or "run;") ..
- (running and S("Halt") or S("Run")) .. "]"
- return table.concat(node_formspec)
- end
- local function halt(pos, meta)
- mesecon.receptor_off(pos)
- meta:set_string("formspec", get_node_formspec(false))
- end
- minetest.register_node(modname .. ":machine",
- {
- description = S("Turing machine"),
- drawtype = "mesh",
- mesh = modname .. "_machine.obj",
- tiles =
- {
- modname .. "_machine_base_horizontals.png",
- modname .. "_machine_base.png",
- modname .. "_machine_taperolls.png",
- modname .. "_machine_tapes.png"
- },
- groups = {deconstructable = 2},
-
- on_construct = function(pos)
- local meta = minetest.get_meta(pos)
- meta:set_string("formspec", get_node_formspec(false))
- local inv = meta:get_inventory()
- inv:set_size("main", 1)
- end,
-
- can_dig = function(pos)
- local inv = minetest.get_inventory({type = "node", pos = pos})
- return inv:is_empty("main")
- end,
-
- on_timer = function(pos, elapsed)
- local oc = os.clock()
- --TODO: do program caching stuff
- local meta = minetest.get_meta(pos)
-
- --fetch data from meta
- local ast = minetest.deserialize(meta:get_string("program"))
- local env = minetest.deserialize(meta:get_string("env"))
- local pc = minetest.deserialize(meta:get_string("pcs"))
- local instr = ast
-
- --process one step of the program
- --advance program counter
- pc[#pc] = pc[#pc] + 1
- --go to the loop we're in and find the current instruction
- for i, v in ipairs(pc)
- do
- instr = instr[v]
- end
- local powered = meta:get_int("mesecon_powered")
- local restart, output = compute_step(env, instr, pc, powered)
- if output == "wait"
- then
- if powered == 0
- then
- --save pc so we can continue after receiving a signal
- meta:set_string("pcs",minetest.serialize(pc))
-
- meta:set_int("wait_for_input", 1)
- return false
- end
- elseif output
- then
- if output > 0
- then
- mesecon.receptor_on(pos)
- else
- mesecon.receptor_off(pos)
- end
- end
- if not restart
- then
- halt(pos, meta)
- return false
- end
- --keep memory consumption in check
- if math.abs(env.headpos) > MAX_MEMORY_RANGE
- then
- halt(pos, meta)
- return false --TODO: show some sign of error
- end
-
- restart = restart * MACHINE_TICK_INTERVAL
-
- --writeback
- meta:set_string("pcs",minetest.serialize(pc))
- meta:set_string("env", minetest.serialize(env))
- --minetest.chat_send_all(os.clock() - oc)
-
- --we don't use auto restart by returning true so we can implement
- --the | (wait) instruction
- local timer = minetest.get_node_timer(pos)
- timer:start(restart)
- return false
- end,
-
- on_receive_fields = function(pos, formname, fields, sender)
- local meta = minetest.get_meta(pos)
- local timer = minetest.get_node_timer(pos)
- local timer_started = timer:is_started()
-
- if fields.run and not timer_started
- then
- local inv = meta:get_inventory()
- local itemstack = inv:get_stack("main", 1)
- if not itemstack:is_empty() and itemstack:get_name() == modname .. ":tape"
- then
- do
- --parse program
- local imeta = itemstack:get_meta()
- local program = imeta:get_string("program")
- local tkns = tokenize(program)
- local ast = parse(tkns)
- if ast
- then
- meta:set_string("pcs",minetest.serialize({0})) --initialize program counter
- meta:set_string("program", minetest.serialize(ast)) --write program into meta
- meta:set_string("env", minetest.serialize({headpos = 1})) --write tape into meta
- end
- end
- collectgarbage()
- --start node timer
- local timer = minetest.get_node_timer(pos)
- timer:start(MACHINE_TICK_INTERVAL)
- meta:set_string("formspec", get_node_formspec(true))
- end
- elseif fields.halt and timer_started
- then
- --stop node timer
- timer:stop()
- halt(pos, meta)
- end
- end,
-
- on_metadata_inventory_take = function(pos, listname, index, stack, player)
- local timer = minetest.get_node_timer(pos)
- timer:stop()
- local meta = minetest.get_meta(pos)
- halt(pos, meta)
- end,
-
- mesecons =
- {
- receptor =
- {
- state = mesecon.state.off
- },
- effector =
- {
- action_on = function(pos, node)
- local meta = minetest.get_meta(pos)
- meta:set_int("mesecon_powered", 1)
-
- if meta:get_int("wait_for_input") == 1
- then
- meta:set_int("wait_for_input", 0)
- local def = minetest.registered_nodes[node.name]
- def.on_timer(pos, 0)
- end
- end,
- action_off = function(pos, node)
- local meta = minetest.get_meta(pos)
- meta:set_int("mesecon_powered", 0)
- end,
- },
- },
- })
|