hevohevo

TurtleAI API (rule-based programming)

Aug 22nd, 2014
366
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.55 KB | None | 0 0
  1. -- ###################################################################
  2. -- Turtle AI API
  3. --   (You can enjoy rule-based programming)
  4. -- (c) 2014 hevohevo. MIT-Style license
  5. -- http://hevohevo.hatenablog.com/
  6. -- Twitter: @hevohevo
  7.  
  8.  
  9. -- ##################################################################
  10. -- 使い方
  11. --[[
  12. os.loadAPI("turtleAI")
  13.  
  14. -- turtleAI APIを使って新しいAIを作る
  15. local ai = turtleAI.newAI()
  16.  
  17. -- turtleAI APIを使って新しいタスクを作る。「前へ進む」
  18. -- 第1引数はタスクの名前、第2引数は実行優先度。低いほうが優先度高い
  19. -- タスクはcan_execute()とexecute()メソッドが必要
  20. -- can_execute()は、現在の状況においてそのタスクを実行するかどうかの判断。true/falseを返すこと
  21. -- execute()は、実際に実行する行動。trueを返すとそのままターンが進み、falseを返すとターンは進まない。
  22. local fwd_task = turtleAI.newTask('fwd', 0)
  23. fwd_task.can_execute = function(task)
  24.   return true -- 常に実行可能とする
  25. end
  26. fwd_task.execute = function(task)
  27.   -- 現在座標の把握のために、task.xxxx() のように移動系関数を使うこと
  28.   task.forward()
  29.   return true
  30. end
  31.  
  32. -- 同じくturtleAI APIを使って新しいタスクを作る。「z座標が3と0のときに180度回転」
  33. local turn_around_task = turtleAI.newTask('turn_around', 0)
  34. turn_around_task.can_execute = function(task)
  35.   -- z座標が3または0のときだけ実行可能。なお、プログラムスタート地点を基準に、x:横、y:高さ、z:縦(奥行き)
  36.   if task.coord.z == 3 or task.coord.z == 0 then
  37.     return true
  38.   else
  39.     return false
  40.   end
  41. end
  42. turn_around_task.execute = function(task)
  43.   task.turnRight()
  44.   task.turnRight()
  45.   return true
  46. end
  47.  
  48. -- 同じくturtleAI APIを使って新しいタスクを作る。「燃料を15使ったら終了」
  49. local quit_task = turtleAI.newTask('quit', 0)
  50. quit_task.can_execute = function(task)
  51.   if task.initial_fuel - turtle.getFuelLevel() == 15 then
  52.     return true
  53.   else
  54.     return false
  55.   end
  56. end
  57. quit_task.execute = function(task)
  58.   return "quit" -- 文字列"quit"を返すと終了とする
  59. end
  60.  
  61. -- 作成した2つのタスクをAIに追加する。 ai:addTask(fwd_task)でも良い
  62. ai.addTask(ai, fwd_task)
  63. ai.addTask(ai, turn_around_task)
  64. ai.addTask(ai, quit_task)
  65.  
  66. -- 実行は ai:iterate()というイテレータ関数を使用。
  67. --
  68. for task_executed, turn, ai in ai.iterate(ai) do
  69.   print("Turn ",turn)
  70.   print(" executed: ",task_executed.name)
  71.   task_executed.ppPos()
  72. end
  73.  
  74. print("quit")
  75.  
  76. --]]
  77.  
  78. -- ##################################################################
  79. -- Turtle Move クラス
  80.  
  81. TurtleMove = {
  82.   -- Class変数、メソッド
  83.   initial_fuel = turtle.getFuelLevel(),
  84.   turn = 1,
  85.   coord = {x=0,y=0,z=0}, -- y:depth, x:width, z:height
  86.   direction = 0, -- 0,1,2,3, 時計周り
  87.   ppPos = function() -- プログラムスタート時を基準として、現在の位置と方角を表示する
  88.     print(string.format(" x/y/z=%d/%d/%d, dir=%d, fuel=%d/%d", TurtleMove.coord.x, TurtleMove.coord.y, TurtleMove.coord.z, TurtleMove.direction, turtle.getFuelLevel(), TurtleMove.initial_fuel))
  89.   end
  90. }
  91.  
  92. local FwHelper = {{ x = 0, z = 1 }, { x = 1, z = 0 }, { x = 0, z = -1 }, { x = -1, z = 0 }}
  93.  
  94. TurtleMove.forward = function(n)
  95.   n = n or 1
  96.   for i=1,n do
  97.     if turtle.forward() then
  98.       TurtleMove.coord.x = TurtleMove.coord.x + FwHelper[TurtleMove.direction+1].x
  99.       TurtleMove.coord.z = TurtleMove.coord.z + FwHelper[TurtleMove.direction+1].z
  100.     end
  101.   end
  102. end
  103. TurtleMove.turnLeft = function(n)
  104.   n = n or 1
  105.   for i=1,n do
  106.     TurtleMove.direction = (TurtleMove.direction - 1) % 4
  107.     turtle.turnLeft()
  108.   end
  109. end
  110. TurtleMove.turnRight = function(n)
  111.   n = n or 1
  112.   for i=1,n do
  113.     TurtleMove.direction = (TurtleMove.direction + 1) % 4
  114.     turtle.turnRight()
  115.   end
  116. end
  117. TurtleMove.back = function(n)
  118.   n = n or 1
  119.   for i=1,n do
  120.     if turtle.back() then
  121.       TurtleMove.coord.x = TurtleMove.coord.x - FwHelper[TurtleMove.direction+1].x
  122.       TurtleMove.coord.z = TurtleMove.coord.z - FwHelper[TurtleMove.direction+1].z
  123.     end
  124.   end
  125. end
  126. TurtleMove.up = function(n)
  127.   n = n or 1
  128.   for i=1,n do
  129.     if turtle.up() then
  130.       TurtleMove.coord.y = TurtleMove.coord.y + 1
  131.     end
  132.   end
  133. end
  134. TurtleMove.down = function(n)
  135.   n = n or 1
  136.   for i=1,n do
  137.     if turtle.down() then
  138.       TurtleMove.coord.y = TurtleMove.coord.y - 1
  139.     end
  140.   end
  141. end
  142.  
  143.  
  144. -- ################################################################
  145. -- Taskクラス
  146. Task = {}
  147. setmetatable(Task, {__index = TurtleMove})
  148. Task.new = function(name, priority)
  149.   local obj={}
  150.   obj.priority = priority or 0 -- 小さいほど優先度高い
  151.   obj.turn_last_fired = 0  -- AIエンジンにより自動更新
  152.   obj.name = name or tostring(obj)
  153.  
  154.   -- 必ず、trueかfalseを返すこと
  155.   obj.can_execute = function(this)
  156.     return true
  157.   end
  158.  
  159.   obj.execute = function(this)
  160.     print(' execute: ',this.name, ' priority=',this.priority)
  161.     -- trueを返すと今ターンはこれで終了、falseを返すともう一度タスクリストから実行
  162.     return true
  163.   end
  164.   return setmetatable(obj, {__index = Task})
  165. end
  166.  
  167. -- ###############################################################
  168. -- Turtle AIクラス
  169.  
  170. local within = function(arg, array)
  171.   for i,v in ipairs(array) do
  172.     if arg == v then return true end
  173.   end
  174.   return false
  175. end
  176.  
  177. TurtleAI = {
  178.   tasks = {}, -- 全タスクのリスト
  179.   executable_tasks = {} -- 現在の状況で実行可能なタスクリスト
  180. }
  181.  
  182. TurtleAI.new = function()
  183.   local obj = {}
  184.   obj.addTask = function(this, task, priority)
  185.     if type(priority)=="number" then task.priority = priority end
  186.     table.insert(this.tasks, task)
  187.   end
  188.  
  189.   obj.pp_tasks = function(this)
  190.     for i,v in ipairs(this.tasks) do
  191.       print(i,': ',v.name)
  192.     end
  193.   end
  194.  
  195.   obj.get_executable_tasks = function(this)
  196.     local tmp = {}
  197.     for i,task in ipairs(this.tasks) do
  198.       if task:can_execute() then
  199.         table.insert(tmp, task)
  200.       end
  201.     end
  202.     return tmp
  203.   end
  204.  
  205.   obj.resolve_conflict = function(this)
  206.     local best_task
  207.     for i,task in ipairs(this.executable_tasks) do
  208.       if not best_task then
  209.         best_task = task
  210.       else
  211.         -- タスクのpriority値で優先度を決める
  212.         -- priority値が同じならば、最近実行したものほど優先度を下げる
  213.         local p = task.priority
  214.         if best_task.priority > p then
  215.           best_task = task
  216.         elseif best_task.priority == p then
  217.           if best_task.turn_last_fired > task.turn_last_fired then
  218.             best_task = task
  219.           end
  220.         end
  221.  
  222.       end
  223.     end
  224.     return best_task
  225.   end
  226.  
  227.   obj.iterate = function(this,max_turn)
  228.     max_turn = max_turn or 500
  229.     return function()
  230.       this.executable_tasks = this:get_executable_tasks()
  231.       local best_task = this:resolve_conflict()
  232.       local status = best_task:execute()
  233.       if status then
  234.         if status == "quit" then return nil end
  235.         best_task.turn_last_fired = TurtleMove.turn
  236.         TurtleMove.turn = TurtleMove.turn + 1
  237.       else
  238.         best_task.turn_last_fired = TurtleMove.turn
  239.       end
  240.       if TurtleMove.turn>max_turn then
  241.         return nil
  242.       else
  243.         return best_task, TurtleMove.turn, this
  244.       end
  245.     end
  246.   end
  247.  
  248.   return setmetatable(obj, {__index = TurtleAI})
  249. end
  250.  
  251.  
  252. function newAI()
  253.   return TurtleAI.new()
  254. end
  255.  
  256. function newTask(...)
  257.   return Task.new(...)
  258. end
Advertisement
Add Comment
Please, Sign In to add comment