Guest User

Untitled

a guest
May 31st, 2013
275
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 29.58 KB | None | 0 0
  1. ----------------------------
  2. --  TURTLE-BASED SOKOBAN  --
  3. ----------------------------
  4. --[[
  5. This file will print a standard sokoban level to
  6. a computer screen, or, if run on a turtle, will
  7. build the level, then leave the turtle in the
  8. starting position, allowing the turtle to be
  9. controlled remotely with buttons and a synced
  10. computer to play and solve the level. A level can
  11. be built horizontally (top-down), or vertically.
  12.  
  13. Optimizations for building are close to maxed,
  14. vertical building is slower than horizontally.
  15. Currently turtles themselves check the level for
  16. valid moves by comparing blocks, but a faster
  17. gameplay will be created by storing logic internally
  18. based on the game board loaded. Using this same
  19. logic will also allow sokoban to be played text-based
  20. on any computer (with color if on an advanced computer).
  21.  
  22. Please report any bugs found.
  23.  
  24. Usage: Place this file in a location of your choosing.
  25. Place the file 'sokoban_levels.sok' in the directory
  26. listed below (default = ...), or change that path to
  27. the directory you placed the file into.
  28. --]]
  29.  
  30. local args = { ... }
  31.  
  32. --path where default levels are located
  33. local default = "rom/programs/sokoban/default.sok"
  34. local isTurtle = (turtle ~= nil)
  35.  
  36. if #args < 1 then
  37.   print("Usage: sokoban <level> [path] [-v]")
  38.   print("Path is for custom levels.")
  39.   print("Use -v to build a level vertically instead of horizontally.")
  40.   print("If level doesn't exist in path, default level is loaded instead, located at:")
  41.   print(default)
  42.   return
  43. end
  44.  
  45. local level = tonumber(args[1]) --level number
  46.  
  47. local path, vert
  48. if #args > 1 then
  49.   if args[2] == "-v" then vert = true
  50.   else path = args[2] end
  51.  
  52.   if args[3] then
  53.     if args[3] == "-v" and not vert then vert = true
  54.     elseif args[3] ~= "-v" and not path then path = args[3]
  55.     else print("args[3] ignored.") end
  56.   end
  57. end
  58.    
  59. lvl = { } --actual level data
  60.  
  61. function isnumber(arg)
  62.   local tot = 0
  63.   if not arg then return false end
  64.   for c in arg:gmatch("%d") do
  65.     tot = tot + 1
  66.   end
  67.   if tot ~= #arg then return false
  68.   else return true end
  69. end
  70.  
  71. -------------------------------
  72. --  LEVEL LOADING / PARSING  --
  73. -------------------------------
  74. function loadsok(lvl)
  75.   if not fs.exists(default) then
  76.     print("Default level pack missing!")
  77.     return
  78.   else
  79.     f = io.open(default)
  80.     local linetot = 0
  81.     local levels = { }
  82.     for line in f:lines() do
  83.       linetot = linetot + 1
  84.       levels[linetot] = line
  85.     end
  86.     f:close()
  87.     if linetot == 0 then
  88.       print("Default level pack is blank!")
  89.       return
  90.     elseif level < 1 or level > linetot then
  91.       print("Level "..level.." doesn't exist!")
  92.       return
  93.     else
  94.       local tmp = splitdata(levels[level])
  95.       tmp = parsesok(tmp)
  96.       return tmp
  97.     end
  98.   end
  99. end
  100.  
  101. function splitdata(data)
  102.   --split the level into multiple lines
  103.   local tot = 0
  104.   local split = { }
  105.   for part in data:gmatch("[^|]+") do
  106.     tot = tot + 1
  107.     split[tot] = part
  108.   end
  109.   return split
  110. end
  111.  
  112. function parsesok(data)
  113.   local extended = { }  --store the level in its full form
  114.   for i = 1, #data do --for each line in puzzle
  115.     local cur = nil --store numbers preceding symbols
  116.     extended[i] = "" --initialize a new line in the extended array
  117.     for c in data[i]:gmatch(".") do --for each character in the line
  118.       if cur == nil and isnumber(c) then cur = c
  119.       elseif isnumber(cur) and isnumber(c) then
  120.         cur = cur..c --combine the two digits
  121.       elseif isnumber(cur) and not isnumber(c) then
  122.         --a number before a symbol, repeat the symbol
  123.         local rep = ""
  124.         if c == "-" then c = " " end
  125.         for j = 1, tonumber(cur) do rep = rep..c end
  126.         extended[i] = extended[i]..rep
  127.         cur = nil
  128.       else
  129.         --a symbol with no number
  130.         if c == "-" then c = " " end
  131.         extended[i] = extended[i]..c
  132.       end
  133.     end
  134.   end
  135.  
  136.   if not isTurtle then
  137.     for i = 1, #extended do
  138.       print(extended[i])
  139.     end
  140.     return
  141.   else return extended end
  142. end
  143.  
  144. if path then
  145.   if not fs.exists(path) then
  146.     print(path.." doesn't exist!")
  147.     print("Loading default level "..level..".")
  148.     loadsok(level)
  149.   else
  150.     f = io.open(path, "r")
  151.     local linetot = 0
  152.     local levels = { }
  153.     for line in f:lines() do
  154.       linetot = linetot + 1
  155.       levels[linetot] = line
  156.     end
  157.     f:close()
  158.     if linetot == 0 then
  159.       print(path.." is blank!")
  160.       print("Loading default level "..level..".")
  161.       return loadsok(level)
  162.     elseif level < 1 or level > linetot then
  163.       print("Level "..level.." doesn't exist in "..args[2].."!")
  164.       print("Loading default level "..level..".")
  165.       loadsok(level)
  166.     else
  167.       lvl = splitdata(levels[level])
  168.       parsesok(lvl)
  169.     end
  170.   end
  171. else
  172.   lvl = loadsok(level)
  173. end
  174.  
  175. if not isTurtle then return end
  176.  
  177. ---------------------------
  178. --  NAVIGATION ROUTINES  --
  179. ---------------------------
  180. posx = 0
  181. posy = 0
  182. posz = 0
  183. dir = 0
  184. path = 1
  185.  
  186. function getInput()
  187.   local input = io.read()
  188.   if input == "exit" or input == "end" or input == "close" or input == "cancel" then
  189.     return false
  190.   end
  191.   return true
  192. end
  193.  
  194. function checkFuel()
  195.   while turtle.getFuelLevel() == 0 do
  196.     print("Out of fuel!")
  197.     print("Please refuel and press enter to continue.")
  198.     getInput()
  199.     for i=1, 12 do
  200.       turtle.select(i)
  201.       if turtle.refuel() then break end
  202.     end
  203.   end
  204. end
  205.  
  206. function turnR()
  207.   turtle.turnRight()
  208.   dir = dir + 1
  209.   if dir >= 4 then dir = 0 end
  210. end
  211.  
  212. function turnL()
  213.   turtle.turnLeft()
  214.   dir = dir - 1
  215.   if dir < 0 then dir = 3 end
  216. end
  217.  
  218. function efficTurn(destDir)
  219.   --print("Current dir: "..dir)
  220.   --print("Dest dir: "..destDir)
  221.   if dir == destDir then return end
  222.   if dir == destDir + 2 or dir == destDir - 2 then
  223.     --print("turn 180")
  224.     turnAround()
  225.   elseif dir == destDir + 1 or dir == destDir - 3 then
  226.     --print("turn left")
  227.     turnL()
  228.   elseif dir == destDir - 1 or dir == destDir + 3 then
  229.     --print("turn right")
  230.     turnR()
  231.   end
  232. end
  233.  
  234. function turnAround()
  235.   turnR()
  236.   turnR()
  237. end
  238.  
  239. function safeForward()
  240.   success = false
  241.   while not success do
  242.     success = turtle.forward()
  243.     if not success then
  244.       if turtle.getFuelLevel() > 0 then
  245.         if path == 0 then
  246.           print("Blocked attempting to move forward.")
  247.           print("Please clear and press enter to continue.")
  248.         elseif path == 1 and not turtle.dig() then
  249.           print("Can't dig block to move forward.")
  250.           print("Please clear and press enter to continue.")
  251.         elseif path == 2 then
  252.           print("Pausing current direction.")
  253.           return false
  254.         elseif path == 3 then
  255.           print("Stopping current direction.")
  256.           return true
  257.         end
  258.         if path ~= 1 then getInput() end
  259.       else checkFuel() end
  260.     end
  261.   end
  262.   return true
  263. end
  264.  
  265. function safeBack()
  266.   success = false
  267.   while not success do
  268.     success = turtle.back()
  269.     if not success then
  270.       if turtle.getFuelLevel() > 0 then
  271.         if path == 0 then
  272.           print("Blocked attempting to move back.")
  273.           print("Please clear and press enter to continue.")
  274.         elseif path == 1 then
  275.           turnR()
  276.           turnR()
  277.           if not turtle.dig() then
  278.             print("Can't dig block behind to move back.")
  279.             print("Please clear and press enter to continue.")
  280.           else return end
  281.         elseif path == 2 then
  282.           print("Pausing current direction.")
  283.           return false
  284.         elseif path == 3 then
  285.           print("Stopping current direction.")
  286.           return true
  287.         end
  288.         if path ~= 1 then getInput() end
  289.       else checkFuel() end
  290.     end
  291.   end
  292.   return true
  293. end
  294.  
  295. function safeUp()
  296.   success = false
  297.   while not success do
  298.     success = turtle.up()
  299.     if not success then
  300.       if turtle.getFuelLevel() > 0 then
  301.         if path == 0 then
  302.           print("Blocked attempting to move up.")
  303.           print("Please clear and press enter to continue.")
  304.         elseif path == 1 and not turtle.digUp() then
  305.           print("Can't dig block to move up.")
  306.           print("Please clear and press enter to continue.")
  307.         elseif path == 2 then
  308.           print("Pausing current direction.")
  309.           return false
  310.         elseif path == 3 then
  311.           print("Stopping current direction.")
  312.           return true
  313.         end
  314.         if path ~= 1 then getInput() end
  315.       else checkFuel() end
  316.     end
  317.   end
  318.   return true
  319. end
  320.  
  321. function safeDown()
  322.   success = false
  323.   while not success do
  324.     success = turtle.down()
  325.     if not success then
  326.       if turtle.getFuelLevel() > 0 then
  327.         if path == 0 then
  328.           print("Blocked attempting to move down.")
  329.           print("Please clear and press enter to continue.")
  330.         elseif path == 1 and not turtle.digDown() then
  331.           print("Can't dig block to move down.")
  332.           print("Please clear and press enter to continue.")
  333.         elseif path == 2 then
  334.           print("Pausing current direction.")
  335.           return false
  336.         elseif path == 3 then
  337.           print("Stopping current direction.")
  338.           return true
  339.         end
  340.         if path ~= 1 then getInput() end
  341.       else checkFuel() end
  342.     end
  343.   end
  344.   return true
  345. end
  346.  
  347. function moveX(targetx)
  348.   if targetx == posx then
  349.     return true
  350.   end
  351.  
  352.   if (dir ~= 1 and dir ~= 3) then -- check axis
  353.     turnR()
  354.   end
  355.  
  356.   while targetx > posx do
  357.     if dir == 1 then
  358.       if not safeForward() then return false end
  359.     else
  360.       if not safeBack() then return false end
  361.     end
  362.     posx = posx + 1
  363.   end
  364.  
  365.   while targetx < posx do
  366.     if dir == 3 then
  367.       if not safeForward() then return false end
  368.     else
  369.       if not safeBack() then return false end
  370.     end
  371.     posx = posx - 1
  372.   end
  373.  
  374.   return true
  375. end
  376.  
  377. function moveY(targety)
  378.   if targety == posy then
  379.     return true
  380.   end
  381.  
  382.   --don't need to change direction
  383.  
  384.   while targety > posy do
  385.     if not safeUp() then return false end
  386.     posy = posy + 1
  387.   end
  388.  
  389.   while targety < posy do
  390.     if not safeDown() then return false end
  391.     posy = posy - 1
  392.   end
  393.  
  394.   return true
  395. end
  396.  
  397. function moveZ(targetz)
  398.   if targetz == posz then
  399.     return true
  400.   end
  401.  
  402.   if (dir ~= 0 and dir ~= 2) then -- check axis
  403.     turnR()
  404.   end
  405.  
  406.   while targetz > posz do
  407.     if dir == 0 then
  408.       if not safeForward() then return false end
  409.     else
  410.       if not safeBack() then return false end
  411.     end
  412.     posz = posz + 1
  413.   end
  414.  
  415.   while targetz < posz do
  416.     if dir == 2 then
  417.       if not safeForward() then return false end
  418.     else
  419.       if not safeBack() then return false end
  420.     end
  421.     posz = posz - 1
  422.   end
  423.  
  424.   return true
  425. end
  426.  
  427. function gototarg(targx, targy, targz)
  428.   local startDir = dir
  429.   local finishX, finishY, finishZ = false, false, false
  430.   local attempts = 0
  431.   while not finishX or not finishY or not finishZ do
  432.     if dir == 0 or dir == 2 then
  433.       if not finishZ then finishZ = moveZ(targz) end
  434.       if not finishX then finishX = moveX(targx) end
  435.     elseif dir == 1 or dir == 3 then
  436.       if not finishX then finishX = moveX(targx) end
  437.       if not finishZ then finishZ = moveZ(targz) end
  438.     end
  439.     if not finishY then finishY = moveY(targy) end
  440.    
  441.     if path == 3 then break end --only run once when in 'dumb' mode
  442.     if path == 2 then attempts = attempts + 1 end
  443.     if (not finishX or not finishY or not finishZ) and path == 2 and attempts > 3 then
  444.       print("Could not find a safe path to destination.")
  445.       return
  446.     end
  447.   end
  448.  
  449.   efficTurn(startDir)
  450.  
  451.   --print("Arrived at destination.")
  452. end
  453.  
  454. ----------------------
  455. --  LEVEL BUILDING  --
  456. ----------------------
  457. local SWALL, SPLAYER, SPLAYERGOAL, SBOX, SBOXGOAL, SGOAL, SFLOOR = "#", "@", "+", "$", "*", ".", "-"
  458. local WALL, BOX, GOAL, BOXGOAL = 1, 2, 3, 4 --if more blocks are needed, add 4
  459. local maxSlots = 16
  460. local startx, starty, startz = 0, 0, 0
  461. local numGoals, numSolved = 0, 0
  462.  
  463. function selnextslot(slot)
  464.   tmp = slot
  465.   while turtle.getItemCount(tmp) == 0 do
  466.     tmp = tmp + 4
  467.     if tmp > maxSlots then
  468.       print("Out of materials! Refill and press enter or type cancel to abort.")
  469.       if not getInput() then return end
  470.       tmp = slot
  471.     end
  472.   end
  473.   turtle.select(tmp)
  474. end
  475.  
  476. function selslot(symb)
  477.   if symb == SWALL then            selnextslot(WALL); return 1
  478.   elseif symb == SBOX then         selnextslot(BOX); return 1
  479.   elseif symb == SGOAL then        selnextslot(GOAL); return 2
  480.   elseif symb == SBOXGOAL then     selnextslot(GOAL); return 3
  481.   elseif symb == SPLAYER then      return 4
  482.   elseif symb == SPLAYERGOAL then  selnextslot(GOAL); return 5
  483.   else return 0 end
  484. end
  485.  
  486. function efficPlaceDown()
  487.   if turtle.detectDown() then
  488.     if turtle.compareDown() then return
  489.     else
  490.       turtle.digDown()
  491.       turtle.placeDown()
  492.     end
  493.   else turtle.placeDown() end
  494. end
  495.  
  496. function build()
  497.   for i = 1, #lvl do  --read each row in the level
  498.     if i % 2 == 0 then
  499.       lvl[i] = lvl[i]:reverse()
  500.       if not vert then moveZ(#lvl[i] - 1)
  501.       else moveZ(#lvl[i] - 2) end
  502.     else
  503.       if not vert then moveZ(0)
  504.       else moveZ(1) end
  505.     end
  506.     for c in lvl[i]:gmatch(".") do  --read each char in the row
  507.       ret = selslot(c)
  508.       if ret == 1 then
  509.         if not vert then efficPlaceDown()
  510.         else turtle.place() end
  511.         if i % 2 == 0 then moveZ(posz - 1)
  512.         else moveZ(posz + 1) end
  513.       elseif ret == 2 then
  514.         numGoals = numGoals + 1
  515.         if not vert then
  516.           moveY(posy - 1)
  517.           efficPlaceDown()
  518.           moveY(posy + 1)
  519.         else
  520.           if i % 2 == 0 then
  521.             moveZ(posz + 1)
  522.             turnL()
  523.             turtle.place()
  524.             turnR()
  525.             moveZ(posz - 1)
  526.           else
  527.             moveZ(posz - 1)
  528.             turnR()
  529.             turtle.place()
  530.             turnL()
  531.             moveZ(posz + 1)
  532.           end
  533.         end
  534.         if i % 2 == 0 then moveZ(posz - 1)
  535.         else moveZ(posz + 1) end
  536.       elseif ret == 3 then
  537.         numGoals = numGoals + 1
  538.         numSolved = numSolved + 1
  539.         if not vert then
  540.           moveY(posy - 1)
  541.           efficPlaceDown()
  542.           moveY(posy + 1)
  543.           turtle.select(BOXGOAL)
  544.           efficPlaceDown()
  545.         else
  546.           if i % 2 == 0 then
  547.             moveZ(posz + 1)
  548.             turnL()
  549.             turtle.place()
  550.             turnR()
  551.             moveZ(posz - 1)
  552.           else
  553.             moveZ(posz - 1)
  554.             turnR()
  555.             turtle.place()
  556.             turnL()
  557.             moveZ(posz + 1)
  558.           end
  559.           turtle.select(BOXGOAL)
  560.           turtle.place()
  561.         end
  562.         if i % 2 == 0 then moveZ(posz - 1)
  563.         else moveZ(posz + 1) end
  564.       elseif ret == 4 or ret == 5 then
  565.         if not vert then
  566.           startx = posx
  567.           startz = posz
  568.         else
  569.           starty = posy
  570.           if i % 2 == 0 then startz = posz + 1
  571.           else startz = posz - 1 end
  572.         end
  573.         if ret == 5 then
  574.           numGoals = numGoals + 1
  575.           if not vert then
  576.             moveY(posy - 1)
  577.             efficPlaceDown()
  578.             moveY(posy + 1)
  579.           else
  580.             if i % 2 == 0 then
  581.               moveZ(posz + 1)
  582.               turnL()
  583.               turtle.place()
  584.               turnR()
  585.               moveZ(posz - 1)
  586.             else
  587.               moveZ(posz - 1)
  588.               turnR()
  589.               turtle.place()
  590.               turnL()
  591.               moveZ(posz + 1)
  592.             end
  593.           end
  594.         end
  595.         if i % 2 == 0 then moveZ(posz - 1)
  596.         else moveZ(posz + 1) end
  597.       else
  598.         if i % 2 == 0 then moveZ(posz - 1)
  599.         else moveZ(posz + 1) end
  600.       end
  601.     end
  602.     if vert then
  603.       moveY(-i)
  604.       turnAround()
  605.       --moveZ(1)
  606.     else
  607.       moveX(i)
  608.       --gototarg(i, 1, 0)
  609.     end
  610.   end
  611. end
  612.  
  613. ------------------------
  614. --  GAMEPLAY CONTROL  --
  615. ------------------------
  616. function sokoUp()
  617.   if not vert then
  618.     --top-down movement up
  619.     efficTurn(3)
  620.     if turtle.detect() then
  621.       --check if the block is a wall
  622.       selnextslot(WALL)
  623.       if turtle.compare() then return true end
  624.      
  625.       --box, go check if there's something behind it
  626.       moveY(posy + 1)
  627.       moveX(posx - 2)
  628.       if turtle.detectDown() then --there is
  629.         moveX(posx + 2)
  630.         moveY(posy - 1)
  631.         return true
  632.       else
  633.         --there's not, check if the space is a goal
  634.         moveY(posy - 1)
  635.         selnextslot(GOAL)
  636.         if turtle.compareDown() then
  637.           selnextslot(BOX)
  638.           turnAround()
  639.           --if the box to push is not solved, it is now being solved,
  640.           --so add one to the count of solved boxes
  641.           if turtle.compare() then numSolved = numSolved + 1 end
  642.           selnextslot(BOXGOAL)
  643.         else
  644.           selnextslot(BOXGOAL)
  645.           turnAround()
  646.           --if the box to push is solved, it is now being unsolved,
  647.           --so subtract one from the count of solved boxes
  648.           if turtle.compare() then numSolved = numSolved - 1 end
  649.           selnextslot(BOX)
  650.         end
  651.         --now move the box and place the new box (selected above)
  652.         turtle.dig()
  653.         moveX(posx + 1)
  654.         turnAround()
  655.         turtle.place()
  656.       end
  657.     else moveX(posx - 1) end
  658.   else
  659.     --vertical movement up
  660.     --check if anything is in path
  661.     if turtle.detectUp() then
  662.       selnextslot(WALL)
  663.       if turtle.compareUp() then return true end
  664.      
  665.       --check if anything is behind the box
  666.       moveX(posx + 1)
  667.       moveY(posy + 2)
  668.       efficTurn(3)
  669.       if turtle.detect() then
  670.         moveY(posy - 2)
  671.         moveX(posx - 1)
  672.         return true
  673.       else
  674.         --there's not, check if there's a goal
  675.         moveX(posx - 1)
  676.         selnextslot(GOAL)
  677.         if turtle.compare() then
  678.           --there's a goal, check if the box being pushed will
  679.           --contribute to an additional goal, or if it's just
  680.           --swapping goal spaces
  681.           selnextslot(BOX)
  682.           if turtle.compareDown() then numSolved = numSolved + 1 end
  683.           selnextslot(BOXGOAL)
  684.         else
  685.           --there's no goal, check if the box being pushed will
  686.           --be pushed off of a goal, subtracting from the number
  687.           --of solved boxes
  688.           selnextslot(BOXGOAL)
  689.           if turtle.compareDown() then numSolved = numSolved - 1 end
  690.           selnextslot(BOX)
  691.         end
  692.         --now move the box (replacing with the box selected above)
  693.         turtle.digDown()
  694.         moveY(posy - 1)
  695.         turtle.placeUp()
  696.       end
  697.     else moveY(posy + 1) end
  698.   end
  699.   return false
  700. end
  701.  
  702. function sokoDown()
  703.   if not vert then
  704.     --top-down movement down
  705.     efficTurn(1)
  706.     if turtle.detect() then
  707.       --check if the block is a wall
  708.       selnextslot(WALL)
  709.       if turtle.compare() then return true end
  710.      
  711.       --box, go check if there's something behind it
  712.       moveY(posy + 1)
  713.       moveX(posx + 2)
  714.       if turtle.detectDown() then --there is
  715.         moveX(posx - 2)
  716.         movY(posy - 1)
  717.         return true
  718.       else
  719.         --there's not, check if the space is a goal
  720.         moveY(posy - 1)
  721.         selnextslot(GOAL)
  722.         if turtle.compareDown() then
  723.           selnextslot(BOX)
  724.           turnAround()
  725.           --if the box to push is not solved, it is now being solved,
  726.           --so add one to the count of solved boxes
  727.           if turtle.compare() then numSolved = numSolved + 1 end
  728.           selnextslot(BOXGOAL)
  729.         else
  730.           selnextslot(BOXGOAL)
  731.           turnAround()
  732.           --if the box to push is solved, it is now being unsolved,
  733.           --so subtract one from the count of solved boxes
  734.           if turtle.compare() then numSolved = numSolved - 1 end
  735.           selnextslot(BOX)
  736.         end
  737.         --now move the box and place the new box (selected above)
  738.         turtle.dig()
  739.         moveX(posx - 1)
  740.         turnAround()
  741.         turtle.place()
  742.       end
  743.     else moveX(posx + 1) end
  744.   else
  745.     --vertical movement down
  746.     --check if anything is in path
  747.     if turtle.detectDown() then
  748.       selnextslot(WALL)
  749.       if turtle.compareDown() then return true end
  750.      
  751.       --check if anything is behind the box
  752.       moveX(posx + 1)
  753.       moveY(posy - 2)
  754.       efficTurn(3)
  755.       if turtle.detect() then
  756.         moveY(posy + 2)
  757.         moveX(posx - 1)
  758.         return true
  759.       else
  760.         --there's not, check if there's a goal
  761.         moveX(posx - 1)
  762.         selnextslot(GOAL)
  763.         if turtle.compare() then
  764.           --there's a goal, check if the box being pushed will
  765.           --contribute to an additional goal, or if it's just
  766.           --swapping goal spaces
  767.           selnextslot(BOX)
  768.           if turtle.compareUp() then numSolved = numSolved + 1 end
  769.           selnextslot(BOXGOAL)
  770.         else
  771.           --there's no goal, check if the box being pushed will
  772.           --be pushed off of a goal, subtracting from the number
  773.           --of solved boxes
  774.           selnextslot(BOXGOAL)
  775.           if turtle.compareUp() then numSolved = numSolved - 1 end
  776.           selnextslot(BOX)
  777.         end
  778.         --now move the box (replacing with the box selected above)
  779.         turtle.digUp()
  780.         moveY(posy + 1)
  781.         turtle.placeDown()
  782.       end
  783.     else moveY(posy - 1) end
  784.   end
  785. end
  786.  
  787. function sokoRight()
  788.   efficTurn(0)
  789.   if turtle.detect() then
  790.     --check if the block is a wall
  791.     selnextslot(WALL)
  792.     if turtle.compare() then return true end
  793.    
  794.     --box, go check if anything is behind it
  795.     if not vert then
  796.       --top-down movement
  797.       moveY(posy + 1)
  798.       moveZ(posz + 2)
  799.       if turtle.detectDown() then --there is
  800.         moveZ(posz - 2)
  801.         movY(posy - 1)
  802.         return true
  803.       else
  804.         --there's not, check if the space is a goal
  805.         moveY(posy - 1)
  806.         selnextslot(GOAL)
  807.         if turtle.compareDown() then
  808.           selnextslot(BOX)
  809.           turnAround()
  810.           --if the box to push is not solved, it is now being solved,
  811.           --so add one to the count of solved boxes
  812.           if turtle.compare() then numSolved = numSolved + 1 end
  813.           selnextslot(BOXGOAL)
  814.         else
  815.           selnextslot(BOXGOAL)
  816.           turnAround()
  817.           --if the box to push is solved, it is now being unsolved,
  818.           --so subtract one from the count of solved boxes
  819.           if turtle.compare() then numSolved = numSolved - 1 end
  820.           selnextslot(BOX)
  821.         end
  822.         --now move the box and place the new box (selected above)
  823.         turtle.dig()
  824.         moveZ(posz - 1)
  825.         turnAround()
  826.         turtle.place()
  827.       end
  828.     else
  829.       --vertical movement
  830.       --check if anything is behind the box
  831.       moveX(posx + 1)
  832.       moveZ(posz + 2)
  833.       efficTurn(3)
  834.       if turtle.detect() then --there is
  835.         moveZ(posz - 2)
  836.         moveX(posx - 1)
  837.         return true
  838.       else
  839.         --there's not, check if there's a goal
  840.         moveX(posx - 1)
  841.         selnextslot(GOAL)
  842.         if turtle.compare() then
  843.           --there's a goal, check if the box being pushed will
  844.           --contribute to an additional goal, or if it's just
  845.           --swapping goal spaces
  846.           turnL()
  847.           selnextslot(BOX)
  848.           if turtle.compare() then numSolved = numSolved + 1 end
  849.           selnextslot(BOXGOAL)
  850.         else
  851.           --there's no goal, check if the box being pushed will
  852.           --be pushed off of a goal, subtracting from the number
  853.           --of solved boxes
  854.           turnL()
  855.           selnextslot(BOXGOAL)
  856.           if turtle.compare() then numSolved = numSolved - 1 end
  857.           selnextslot(BOX)
  858.         end
  859.         --now move the box (replacing with the box selected above)
  860.         turtle.dig()
  861.         moveZ(posz - 1)
  862.         turnAround()
  863.         turtle.place()
  864.       end
  865.     end
  866.   else moveZ(posz + 1) end
  867. end
  868.  
  869. function sokoLeft()
  870.   efficTurn(2)
  871.   if turtle.detect() then
  872.     --check if the block is a wall
  873.     selnextslot(WALL)
  874.     if turtle.compare() then return true end
  875.    
  876.     --box, go check if anything is behind it
  877.     if not vert then
  878.       --top-down movement
  879.       moveY(posy + 1)
  880.       moveZ(posz - 2)
  881.       if turtle.detectDown() then --there is
  882.         moveZ(posz + 2)
  883.         movY(posy - 1)
  884.         return true
  885.       else
  886.         --there's not, check if the space is a goal
  887.         moveY(posy - 1)
  888.         selnextslot(GOAL)
  889.         if turtle.compareDown() then
  890.           selnextslot(BOX)
  891.           turnAround()
  892.           --if the box to push is not solved, it is now being solved,
  893.           --so add one to the count of solved boxes
  894.           if turtle.compare() then numSolved = numSolved + 1 end
  895.           selnextslot(BOXGOAL)
  896.         else
  897.           selnextslot(BOXGOAL)
  898.           turnAround()
  899.           --if the box to push is solved, it is now being unsolved,
  900.           --so subtract one from the count of solved boxes
  901.           if turtle.compare() then numSolved = numSolved - 1 end
  902.           selnextslot(BOX)
  903.         end
  904.         --now move the box and place the new box (selected above)
  905.         turtle.dig()
  906.         moveZ(posz + 1)
  907.         turnAround()
  908.         turtle.place()
  909.       end
  910.     else
  911.       --vertical movement
  912.       --check if anything is behind the box
  913.       moveX(posx + 1)
  914.       moveZ(posz - 2)
  915.       efficTurn(3)
  916.       if turtle.detect() then --there is
  917.         moveZ(posz + 2)
  918.         moveX(posx - 1)
  919.         return true
  920.       else
  921.         --there's not, check if there's a goal
  922.         moveX(posx - 1)
  923.         selnextslot(GOAL)
  924.         if turtle.compare() then
  925.           --there's a goal, check if the box being pushed will
  926.           --contribute to an additional goal, or if it's just
  927.           --swapping goal spaces
  928.           turnR()
  929.           selnextslot(BOX)
  930.           if turtle.compare() then numSolved = numSolved + 1 end
  931.           selnextslot(BOXGOAL)
  932.         else
  933.           --there's no goal, check if the box being pushed will
  934.           --be pushed off of a goal, subtracting from the number
  935.           --of solved boxes
  936.           turnR()
  937.           selnextslot(BOXGOAL)
  938.           if turtle.compare() then numSolved = numSolved - 1 end
  939.           selnextslot(BOX)
  940.         end
  941.         --now move the box (replacing with the box selected above)
  942.         turtle.dig()
  943.         moveZ(posz + 1)
  944.         turnAround()
  945.         turtle.place()
  946.       end
  947.     end
  948.   else moveZ(posz - 1) end
  949. end
  950.  
  951. function sync()
  952.   while true do
  953.     term.clear()
  954.     term.setCursorPos(1, 1)
  955.     shell.run("id")
  956.     print("Send the message 'sync' to this turtle using the ID above.")
  957.     print("Waiting for computer sync...")
  958.     id, message = rednet.receive()
  959.     if message == "sync" then
  960.       print("Received input from @"..id..", sync with this computer?")
  961.       print("    (Enter yes or no)")
  962.       local input = io.read()
  963.       if input == "y" or input == "yes" or input == "ok" then
  964.         print("Sync complete.")
  965.         rednet.send(id, "true")
  966.         return id --return computer ID that sent the message
  967.       elseif input == "cancel" or input == "quit" or input == "exit" then
  968.         rednet.send(id, "false")
  969.         return -1 -- don't sync
  970.       end
  971.     end
  972.   end
  973. end
  974.  
  975. function run()
  976.   rednet.open("right")
  977.   local synced = sync()
  978.   if synced == -1 then return end
  979.   local continue = true
  980.   local lastCmd = ""  --store the last command sent to the turtle
  981.   local lastFailed = false  --store whether the last command succeeded
  982.   local moves = 0
  983.   local firstMove = true
  984.   local delta = 0
  985.   while continue do
  986.     print("Awaiting command...")
  987.     id, message = rednet.receive()
  988.     print("@"..id..": "..message)
  989.     if id == synced and not (lastCmd == message and lastFailed) then
  990.       --received message from synced computer, read the message
  991.       --don't waste time attempting to complete the same move that
  992.       --was just tried and failed
  993.       if message == "up" then
  994.         lastFailed = sokoUp()
  995.       elseif message == "down" then
  996.         lastFailed = sokoDown()
  997.       elseif message == "right" then
  998.         lastFailed = sokoRight()
  999.       elseif message == "left" then
  1000.         lastFailed = sokoLeft()
  1001.       elseif message == "end" then
  1002.         print("User control terminated.")
  1003.         return
  1004.       elseif message == "sync" then
  1005.         local newSync = sync()
  1006.       else
  1007.         print("Unknown command: "..message) --shouldn't happen
  1008.         print("Expected: up, down, right, left, or end")
  1009.         lastFailed = false
  1010.       end
  1011.      
  1012.       if not lastFailed then
  1013.         print("Last move succeeded.")
  1014.       else print("Last move failed.") end
  1015.      
  1016.       if not lastFailed and (message == "up" or
  1017.           message == "down" or message == "right" or
  1018.           message == "left") then
  1019.         moves = moves + 1
  1020.         if firstMove then
  1021.           delta = os.clock()
  1022.           firstMove = false
  1023.         end
  1024.       end
  1025.      
  1026.       if numSolved == numGoals then
  1027.         delta = os.clock() - delta
  1028.         --local days, hours, minutes, seconds
  1029.         --convert to a meaningful time format
  1030.         print("You solved the puzzle!")
  1031.         print("It took "..moves.." moves and "..delta.." seconds!")
  1032.         print("User control terminated.")
  1033.         rednet.send(id, "end")
  1034.         return
  1035.       end
  1036.       lastCmd = message
  1037.     end
  1038.   end
  1039. end
  1040.  
  1041. -------------------------
  1042. --  BEGINNING THE END  --
  1043. -------------------------
  1044.  
  1045. function start()
  1046.   if vert then
  1047.     posy = -#lvl - 1
  1048.     gototarg(0, 0, 1)
  1049.     turnL()
  1050.     turnL()
  1051.   else gototarg(0, 1, 0) end
  1052.   build()
  1053.   if vert then
  1054.     moveX(1)
  1055.     gototarg(1, starty, startz)
  1056.     moveX(0)
  1057.   else gototarg(startx, starty, startz) end
  1058.   run()
  1059. end
  1060.  
  1061. start()
Advertisement
Add Comment
Please, Sign In to add comment