local util = require("util") local robot = require("robot") local config_file = "state.cfg" local libnav = { str = "", _navlog = "", retry = true, breaking = false, } function libnav.get_location() return util.config_get(config_file, "location", "")..libnav.str end local function log_flush() local state = util.config(config_file) local place = state:get("location", "") place = place .. libnav._navlog libnav._navlog = "" state:set("location", place) state:close() end local function log_assert(cond) if not cond then log_flush() end assert(cond) end local function record(str) libnav._navlog = libnav._navlog .. str end function retry_loop(fn, swingfn, msg) while true do if fn() then break end if libnav.breaking and swingfn then swingfn() end if fn() then break end if libnav.retry then os.sleep(1) else log_assert(false, msg) end end end local function _up() retry_loop(robot.up, robot.swingUp, "can't move up") record("u") end local function _down() retry_loop(robot.down, robot.swingDown, "can't move down") record("d") end local function _turnLeft() retry_loop(robot.turnLeft, nil, "can't turn left") record("l") end local function _turnRight() retry_loop(robot.turnRight, nil, "can't turn right") record("r") end local function _forward() retry_loop(robot.forward, robot.swing, "can't move forward") record("f") end local function _back() retry_loop(robot.back, nil, "can't move back") record("b") end local function _brk() libnav.breaking = true record("X") end local function _unbrk() libnav.breaking = false record("x") end function libnav.add(str) libnav.str = libnav.str .. str end function libnav.up() libnav.add("u") end function libnav.down() libnav.add("d") end function libnav.left() libnav.add("l") end function libnav.right() libnav.add("r") end function libnav.fwd() libnav.add("f") end function libnav.back() libnav.add("b") end function libnav.brk() libnav.add("X") end function libnav.unbrk() libnav.add("x") end function libnav.opt(str) -- todo optimize local bak = str while true do local prev = str str = str :gsub("ud", ""):gsub("du", "") :gsub("fb", ""):gsub("bf", "") :gsub("rl", ""):gsub("lr", "") :gsub("llll", ""):gsub("rrrr", "") :gsub("fllf", "ll"):gsub("frrf", "rr") :gsub("bllb", "ll"):gsub("brrb", "rr") :gsub("dllu", "ll"):gsub("drru", "rr") :gsub("ulld", "ll"):gsub("urrd", "rr") :gsub("dlu", "l"):gsub("dru", "r") :gsub("uld", "l"):gsub("urd", "r") :gsub("dfu", "f"):gsub("dbu", "b") :gsub("ufd", "f"):gsub("ubd", "b") :gsub("lll", "r"):gsub("rrr", "l") -- move all rotations down so they can cancel :gsub("ul", "lu"):gsub("ur", "ru") :gsub("ld", "dl"):gsub("rd", "dr") :gsub("frfrf", "rfr"):gsub("flflf", "lfl") :gsub("frflb", "rfl"):gsub("flfrb", "lfr") if prev == str then break end end -- print("opt "..bak.." => "..str) return str; end function libnav.flush() local actions = { u = _up, d = _down, l = _turnLeft, r = _turnRight, f = _forward, b = _back, X = _brk, x = _unbrk, } local str = libnav.opt(libnav.str) libnav.str = "" for i = 1, str:len() do local ch = str:sub(i, i) local action = actions[ch] assert(action) action() end log_flush() -- optimize location local state = util.config(config_file) local location = state:get("location", "") location = libnav.opt(location) state:set("location", location) state:close() end function libnav.estimate_and_discard() local str = libnav.opt(libnav.str) libnav.str = "" return str:len() end function libnav.invert(movestr) local res = "" local flips = { u = "d", d = "u", l = "r", r = "l", f = "f", b = "b", x = "X", X = "x", } for i = movestr:len(), 1, -1 do local ch = movestr:sub(i, i) local flip = flips[ch] assert(flip) res = res .. flip end return "rr" .. res .. "rr" end -- go to where 'libnav.get_location()' was 'loc' function libnav.go_to(loc) libnav.add(libnav.invert(libnav.get_location())) libnav.add(loc) end return libnav