turtleutils.lua

turtleutils.lua

タートル便利関数集ですが、今のところ大したものではないです。

-----------------------------------------------------------
-- turtleutils.lua
-- タートルのちょっと便利?な関数周
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------

-----------------------------------------------------------
-- turtleのスロットを初期化するよ
-- つうてもいまんとこ最初のスロットを選択するだけやで
-- なんかふさわしい処理とか思いついたら追加するよ
-----------------------------------------------------------
function resetSlot()
  turtle.select(1)
end

-----------------------------------------------------------
-- 形式化されたタートル(とコンピューター)の識別文字列を返すよ
-- ID表示を統一しといたらわかりやすいかもしれんからな
-----------------------------------------------------------
function getCompName()
  local label = os.getComputerLabel()
  if not label then
    label = 'no_label' -- labelはついてないこともあるねん
  end

  return string.format('[%03d]%s',
    os.getComputerID(),
    label
  )
end

turtlefuel.lua

turtlefuel.lua

タートル補給クラスです。

-----------------------------------------------------------
-- @name  turtlefuel(lua)
-- @description タートルの燃料を扱う関数群
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
os.loadAPI('/lib/apis/akaneutils')
os.loadAPI('/lib/apis/turtleapis')

-----------------------------------------------------------
-- タートルの燃料レベルを表す文字列を返すよ
-----------------------------------------------------------
function getFuelLevelString()
  return 'FL:'.. tostring(turtle.getFuelLevel())
end

-----------------------------------------------------------
-- 燃料補給を管理するオブジェクトを作って返すよ
-- 前に作った補給関数なんかも含まれてるよ
--
-- オブジェクトは燃料の最初の量と、補給量の合計を覚えてるよ
-- その合計から、現在の燃料の量を引いて
-- 燃料をどれだけ使ったか計算できるよ
--
-- @param setting 燃料補給管理オブジェクトの設定テーブル
-- @return 補給管理オブジェクト
-----------------------------------------------------------
function new(setting)
  -- テーブル作成。デフォルト値も設定するよ
  -----------------------------------------------------------
  -- @class table
  -- @name hokyu
  -- @description 燃料補給管理オブジェクト
  -- タートルの燃料補給と、燃料使用量を計算する機能があるよ
  -- @field direction   補給チェストがどっちにあるか
  -- @field wait        燃料確認間隔
  -- @field threashold  燃料補給閾値 これより少なかったら補給するよ
  -- @field enough      燃料十分量 補給時はコレより多くなったら補給をやめるよ
  -- @field sleep       待機に使用する関数
  -----------------------------------------------------------
  local hokyu = {
    direction = const.FORWARD,
    wait = 60,
    threashold = 1000,
    enough = threashold,
    sleep = sleep,
  }
  -- クロージャOOP
  local self = hokyu

  -- private
  -- 燃料の最初の量と、補給量の合計
  local total_fuel = turtle.getFuelLevel()

  -- 引数で渡された設定テーブルで上書き
  if setting then
    akaneutils.overwrite(hokyu, setting)
  end


  -----------------------------------------------------------
  -- 燃料の総使用量を計算して返すよ
  --
  -- 計算方法は、最初にあった量 + 総補給料 - 現在の燃料 やで
  -- どっかおかしかったら教えてな
  -- @return 燃料の総使用量
  -----------------------------------------------------------
  function hokyu.getTotalUsage()
    return total_fuel - turtle.getFuelLevel()
  end

  -----------------------------------------------------------
  -- 燃料の総使用量に加算するよ
  -- @param fuel_level [in]加算する量
  -----------------------------------------------------------
  function hokyu.addUsage(fuel_level)
    total_fuel = total_fuel + fuel_level
  end

  -----------------------------------------------------------
  -- 選択スロットから燃料を補給して、補給量を記憶するよ
  -- 補給する時はかならずこっちを使うようにしないとあかんで
  -- turtle.refuel()を直接使ってしまうと、補給量を記憶できんねん
  -- @param quantity 補給するアイテムの数
  -- @return turtle.refuel()の戻り値
  -----------------------------------------------------------
  function hokyu.refuel(quantity)
    quantity = quantity or math.huge
    local pre_level = turtle.getFuelLevel() -- 補給前の燃料レベルを覚えとくよ
    local result =  turtle.refuel()
    total_fuel = total_fuel + turtle.getFuelLevel() - pre_level -- 補給量を加算するで
    return result
  end

  -----------------------------------------------------------
  -- hokyu()のループ内容やで
  --
  -- @param take_fuel [in]燃料を補給する関数やで
  -- @param threashold [in]燃料補給する閾値やで
  -- @param enough [in]補給後の必要量や
  -----------------------------------------------------------
  function hokyu.hokyu_(take_fuel, threashold, enough)
    if turtle.getFuelLevel() >= threashold then return end -- 燃料があったら何もしないよ

    -- 補給するよ
    print('hokyu suruyo ('.. getFuelLevelString() ..')')

    while true do -- 必要量を超えるまで補給するで
      local selected_slot = turtle.getSelectedSlot() -- 選択スロットを覚えとくよ
      turtle.select(16) -- インベントリの最後のスロットを使うよ
      if take_fuel() and self.refuel() then -- 燃料補給するよ
        print('hokyu sitayo ('.. getFuelLevelString() ..')')
      else
        print('hara hetta') -- 燃料ないよー
      end
      turtle.select(selected_slot) -- 乙女のたしなみや
      if turtle.getFuelLevel() >= enough then break end -- 必要量以上なら補給終わりや
      self.sleep(self.wait)
    end
  end

  -----------------------------------------------------------
  -- 燃料がなかったら補給するよ<br>
  --
  -- 燃料がthreasholdより低かったら補給を開始するよ。
  -- enoughを超えるまで補給するよ。
  -- 引数省略時はselfの値を使うよ
  --
  -- @param threashold [in]燃料補給する閾値やで
  -- @param enough [in]補給後の必要量や
  -----------------------------------------------------------
  function hokyu.hokyu(threashold, enough)
    -- 引数の初期化
    threashold = threashold or self.threashold
    enough = enough or self.enough

    local takeFuel = turtleapis.SUCK[self.direction]
    while true do -- メインループや
      self.hokyu_(takeFuel, threashold, enough)
      self.sleep(self.wait) -- しばらく待機するで
    end
  end

  return self
end

turtleapis.lua

turtleapis.lua

方向別のタートルAPIをまとめて管理するためのAPIです。
わりと誰でも作るやつだと思いますが、これには right / left / back なんかも追加してあって思ったより便利です。
ぼちぼち良く出来た気がします。
前進/後退のみで一歩移動する STEPF/STEPB を追加予定です。

-----------------------------------------------------------
-- @file  turtleapis(lua)
-- @brief turtle API で方向別のものを簡易に取得できるテーブル集
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
os.loadAPI('/lib/apis/const')

-----------------------------------------------------------
-- 回転
-----------------------------------------------------------
TURN = {
  [const.RIGHT] = turtle.turnRight,
  [const.LEFT]  = turtle.turnLeft,
  [const.BACK]  = function() -- 真後ろに回転するやつや
      local turn = turtle.turnRight -- 右回りやで
      if not turn() then return false end -- 1回目の回転に失敗したらそこまでや
      return turn() -- 2回めの回転や
    end
}

-----------------------------------------------------------
-- タートルが右か左を向いて、関数を実行して、元の向きに戻る
-- っていう関数を作って返すよ
-- 後ろまで向くのもついでに作るけど、TURN[BACK]の途中で失敗するとどうなるかわからん
-- そもそもタートルが回転に失敗することあるかどうか知らんけど
--
-- @param func [in]実行する関数
-- @param direction [in]タートルが向く向き
-- @return 作った関数
-----------------------------------------------------------
local function createSideFunc(func, direction)
  -- タートルが横向く関数や。とりあえず右向くもんとして設定しとくで
  local turn1st, turn2nd = TURN[const.RIGHT], TURN[const.LEFT]

  if direction == const.LEFT then -- 左に向く時は入れ替えればええで
    turn1st, turn2nd = turn2nd, turn1st
  elseif direction == BACK then -- 後ろ向く場合や
    turn1st = TURN[const.BACK] -- 後ろ向くで
    turn2nd = turn1st -- 同じ方向でまた後ろ向くで
  end

  -- 関数を作って返すよ
  return function(not_return)
    if not turn1st() then return false end -- 最初の横向くの失敗したらどうにもならん
    local result = func() -- 戻り値は関数の戻り値をそのまま返すんや
    if not not_return then turn2nd() end -- 引数が真でなければ元の向きに戻るよ
    return result, not_return and turn2nd
  end
end

-----------------------------------------------------------
-- 方向別のタートルAPIを格納したテーブルを作るよ
-- 例えばDROP[方向]で必要な関数がもらえる感じのやつや
--
-- 横向くやつは使い途が限られるかもなあ
--
-- @param methods [in]FORWARD UP DOWNの順にAPIメソッドを格納した配列
-- @return 作ったテーブル
-----------------------------------------------------------
local function createFuncsTable(methods)
  local methods_table = {}

  -- 前・上・下やで
  for i, v in ipairs { const.FORWARD, const.UP, const.DOWN } do
    methods_table[v] = methods[i]
  end

  -- 横向くやつや
  for _i, v in ipairs { const.RIGHT, const.LEFT, const.BACK } do
    methods_table[v] = createSideFunc(methods[1], v)
  end

  return methods_table
end

-----------------------------------------------------------
-- 方向別のタートルAPIメソッドを取得するためのテーブルやで
-- DROP[方向]でメソッドがもらえるで
DROP = createFuncsTable {
  turtle.drop,
  turtle.dropUp,
  turtle.dropDown,
}

-- 以下、似たようなのが続くで

ATTACK = createFuncsTable {
  turtle.attack,
  turtle.attackUp,
  turtle.attackDown,
}

DIG = createFuncsTable {
  turtle.dig,
  turtle.digUp,
  turtle.digDown,
}

PLACE = createFuncsTable {
  turtle.place,
  turtle.placeUp,
  turtle.placeDown,
}

DETECT = createFuncsTable {
  turtle.detect,
  turtle.detectUp,
  turtle.detectDown,
}

INSPECT = createFuncsTable {
  turtle.inspect,
  turtle.inspectUp,
  turtle.inspectDown,
}

COMPARE = createFuncsTable {
  turtle.compare,
  turtle.compareUp,
  turtle.compareDown,
}

SUCK = createFuncsTable {
  turtle.suck,
  turtle.suckUp,
  turtle.suckDown,
}

-----------------------------------------------------------
-- 一歩移動
-----------------------------------------------------------
STEP = createFuncsTable {
  turtle.forward,
  turtle.up,
  turtle.down,
}
STEP[const.BACK] = turtle.back -- 一歩移動の[BACK]だけは他と違って回らずそのまま下がるよ

simattyau.lua

simattyau.lua

しまっちゃうクラスを記述したAPIです。
クラス名とAPI名(= ファイル名)が同一なのは、simattyau.new()と書けてそれっぽいからです。

-----------------------------------------------------------
-- simattyau.lua
-- アイテム収納 API
-- 収納したアイテムと数の記憶機能付き
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
os.loadAPI('/lib/apis/akaneutils')
os.loadAPI('/lib/apis/turtleapis')
os.loadAPI('/lib/apis/identify')

-----------------------------------------------------------
-- アイテムをしまって、しまった種類と数を記憶するオブジェクトを作るよ
-- @param setting [in]アイテム収納オブジェクトの設定テーブル
-- @return アイテム収納オブジェクト
-----------------------------------------------------------
function new(setting)
  -- テーブル作成。デフォルト値も設定するよ
  -----------------------------------------------------------
  -- @class table
  -- @name simau
  -- @description アイテム収納オブジェクト
  -- @field counts 収納したアイテムの数を記憶するテーブル
  -----------------------------------------------------------
  local simau = {
    counts = {},
  }
  -- クロージャOOP
  local self = simau

  -----------------------------------------------------------
  -- アイテム数記憶テーブルのkeyを生成する
  -----------------------------------------------------------
  function simau.createKey(itemID, itemDV)
    if not itemDV then print('nil') end
    return string.format('%s,%d', itemID, tonumber(itemDV)) -- どうせcsvにするんやしなあ
  end

  -----------------------------------------------------------
  -- 指定スロットのアイテムを分類するよ
  -- 分類には引数で渡されたコールバック関数を使うよ
  -- コールバック関数を実行して、戻り値の方向のチェストにしまうよ
  -- 戻り値がfalseならなにもしないよ
  --
  -- @param callback [in]分類に使うコールバック関数
  -- @param slot_no [in]対象スロット番号
  -- @return しまったらture / しまわなかったらfalse
  -----------------------------------------------------------
  function simau.simau(callback, slot_no)
    local direction = callback(slot_no)
    if not direction then return false end
    return self.simauAny(slot_no, direction)
  end

  -----------------------------------------------------------
  -- 指定スロットのアイテムをチェストにしまうよ
  --
  -- @param slot_no [in]対象スロット番号
  -- @param direction [in]アイテムをしまう方向
  -- @return しまったらture / しまわなかったらfalse
  -----------------------------------------------------------
  function simau.simauAny(slot_no, direction)
    if not turtle.getItemDetail(slot_no) then return false end
    local selected_slot = turtle.getSelectedSlot() -- 選択スロットを覚えとくよ
    turtle.select(slot_no)
    local result = self.insert(direction) -- チェストにしまうよ
    turtle.select(selected_slot) -- 選択スロットを戻しとくんのが乙女のたしなみやで
    return result
  end

  -----------------------------------------------------------
  -- 選択中のスロットのアイテムをチェストにしまうよ
  -- しまったアイテムの種類と数を記憶するよ
  --
  -- @param direction [in]アイテムをしまう方向
  -- @return しまったらture / しまわなかったらfalse
  -----------------------------------------------------------
  function simau.insert(direction)
    local item = turtle.getItemDetail() -- アイテムは何やろ
    if not turtleapis.DROP[direction]() then -- アイテムをしまうよ
      return false
    end

    -- しまったアイテムを記憶するよ
    self.addCount(
      identify.getItemID(item),
      identify.getItemDV(item),
      item.count - turtle.getItemCount()
    )
    return true
  end

  -----------------------------------------------------------
  -- アイテムの種類と数を記憶するよ
  --
  -- @param itemID [in]しまったアイテムのID
  -- @param itemDV [in]しまったアイテムのDV
  -- @param count [in]しまったアイテムの数
  -----------------------------------------------------------
  function simau.addCount(itemID, itemDV, count)
    local key = self.createKey(itemID, itemDV)
    local old_count = self.counts[key] or 0
    self.counts[key] = old_count + count
  end

  -----------------------------------------------------------
  -- しまったアイテムの種類と数を(csv)ファイルに書き込むよ
  -- @param path [in]ファイルパス
  -----------------------------------------------------------
  function simau.saveStats(path)
    akaneutils.prepareToWrite(path) -- 書き込み準備や
    local h = fs.open(path, 'w')
    for k, v in pairs(self.counts) do
      h.writeLine(string.format('%s,%d', k, v)) -- 書くよ~
    end
    h.close()
  end

  -----------------------------------------------------------
  -- しまったアイテムの種類と数を書き込んだファイル(csv)を読み込むよ
  -- @param path [in]ファイルパス
  -----------------------------------------------------------
  function simau.loadStats(path)
    self.counts = {} -- 初期化するで
    if not fs.exists(path) then return end -- ファイルが無かったら何もしないよ

    -- csvを読み込んで行ごとに処理するよ
    for columns in akaneutils.csvEachLine(path) do
      if #columns == 3 then
        -- 読み込んだデータを格納するよ
        self.counts[self.createKey(columns[1], columns[2])] = columns[3]
      end
    end
  end

  return self
end

identify.lua

identify.lua

アイテム識別のためのAPIです。
処理効率に無視できないレベルの問題があり、またModに対する拡張性も今ひとつなので、改修予定です。

-----------------------------------------------------------
-- identify(lua)
-- アイテム識別モジュール
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------

-- アイテムID
local ID_FISH             = 'minecraft:fish'
local ID_LEATHER_BOOTS    = 'minecraft:leather_boots'
local ID_TURIZAO          = 'minecraft:fishing_rod'
local ID_POTION           = 'minecraft:potion'
local ID_SADDLE           = 'minecraft:saddle'


local ID_WATER            = 'minecraft:water'
local ID_FLOWING_WATER    = 'minecraft:flowing_water'

-----------------------------------------------------------
-- turtle.getItemDetailまたはinspectの戻り値から
-- アイテムIDを取得するよ<br>
-- これ要るんやろか
-- @param item_detail [in]アイテム詳細テーブル
-- @return アイテムID
-----------------------------------------------------------
function getItemID(item_detail)
  return item_detail.name
end

-----------------------------------------------------------
-- turtle.getItemDetailの戻り値からアイテムのDVを取得する<br>
--
-- CCのアイテム詳細テーブルではDVは'damage'に格納されるぽいな
-- minecraft wikiによるとDVはデータ値(Data values)てことになっとるけど
-- なんでdamageなんやろ
--
-- @param item_detail [in]アイテム詳細テーブル
-- @return アイテムのDV
-----------------------------------------------------------
function getItemDV(item_detail)
  return item_detail.damage
end

-----------------------------------------------------------
-- turtle.inspectの戻り値からメタデータを取得するよ
-- @param data [in]ブロック詳細テーブル
-- @return アイテムID
-----------------------------------------------------------
function getMetadata(data)
  return data.metadata
end

-----------------------------------------------------------
-- turtle.getItemDetailの戻り値からアイテムのDVを取得する<br>
--
-- CCのアイテム詳細テーブルではDVは'damage'に格納されるぽいな
-- minecraft wikiによるとDVはデータ値(Data values)てことになっとるけど
-- なんでdamageなんやろ
--
-- @param item_detail [in]アイテム詳細テーブル
-- @return アイテムのDV
-----------------------------------------------------------
function getItemDV(item_detail)
  return item_detail.damage
end

-----------------------------------------------------------
-- 指定アイテムを識別するよ<br>
--
-- 引数で与えられた識別用テーブルを使ってアイテムを識別するよ
-- 識別用テーブルから、アイテムIDをkeyにしてvalueを取得し、
-- valueが関数ならば、item_detailを引数に実行し、
-- その戻り値を戻り値として返すよ。
--
-- valueが関数以外ならば(booleanまたはnilを想定)、
-- そのまま戻り値として返すよ。
--
-- @param identifier [in]アイテム識別用テーブル
-- @param item_detail [in]アイテム詳細テーブル
-- @return 識別結果
-----------------------------------------------------------
local function identify(identifier, item_detail)
  if not item_detail then return false end

  local value = identifier[getItemID(item_detail)]
  if type(value) == 'function' then
    return value(item_detail)
  end
  return value
end

-----------------------------------------------------------
-- 簡単に識別関数を作るよ<br>
-- 下のisFish()みたいなのをいちいち作ってたらメンドくて失踪あるのみやから
-- 単純な場合はサクッと作れる方法を用意するよ
-- (isFish()はサンプルとしてそのままのこすよ)
-- @param IDs [in]アイテムIDの配列
-- 使用例は下のisLeatherBootsとかを見てな
-----------------------------------------------------------
local function makeSimply(IDs)
  local t = {}
  for i, v in ipairs(IDs) do
    t[v] = true
  end
  return function(item_detail) return identify(t, item_detail) end
end

-----------------------------------------------------------
-- 指定アイテムが生魚かどうか調べるよ<br>
-- @param item_detail [in]アイテム詳細テーブル
-- @return true 魚や! / false 魚やないで!
-----------------------------------------------------------
function isFish(item_detail)
  local t = {}
  t[ID_FISH] = true

  return identify(t, item_detail)
end

-----------------------------------------------------------
-- 指定アイテムが焼き魚にできる生魚かどうか調べるよ<br>
-- アイテムIDが'minecraft:fish'で、DVが0または1なら焼ける魚や
--
-- @param item_detail [in]アイテム詳細テーブル
-- @return true 焼ける魚や! / false 焼ける魚やないで!
-----------------------------------------------------------
function isRoastableFish(item_detail)
  local t = {}
  t[ID_FISH] = function(item) return getItemDV(item) < 2 end

  return identify(t, item_detail)
end

-----------------------------------------------------------
-- 指定アイテムがポーションという名のただの水かどうか調べるよ<br>
-- DVが0のポーション、つまりアイテムIDが'minecraft:potion'が
-- 水入り瓶や
--
-- @param item_detail [in]アイテム詳細テーブル
-- @return true 水入り瓶や / false 水入り瓶やないで!
-----------------------------------------------------------
function isWaterPotion(item_detail)
  local t = {}
  t[ID_POTION] = function(item) return getItemDV(item) == 0 end

  return identify(t, item_detail)
end

-----------------------------------------------------------
-- こっから下は単純なやつ
-----------------------------------------------------------
-- 革ブーツ
isLeatherBoots = makeSimply {
  ID_LEATHER_BOOTS,
}

-- 釣り竿
isFishingRod = makeSimply {
  ID_TURIZAO,
}

-- サドル
isSaddle = makeSimply {
  ID_SADDLE,
}

-- 水ブロック
isWaterBlock = makeSimply {
  ID_WATER,
  ID_FLOWING_WATER,
}

const.lua

const.lua

定数(ぽいもの)の定義です。
ぽいもの、というのは、Luaには定数がないからです。
これはAPIにするより単にグローバル変数にしたほうが良いかもしれません。 現状、グローバル名前空間の汚染を考慮してこのようなAPIにしてあります。

-----------------------------------------------------------
-- const.lua
-- 定数(ぽいもの)定義
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------

-----------------------------------------------------------
-- 方向
-----------------------------------------------------------
FORWARD = 'forward'
UP      = 'up'
DOWN    = 'down'
RIGHT   = 'right'
LEFT    = 'left'
BACK    = 'back'

-----------------------------------------------------------
-- 方角
-----------------------------------------------------------
NORTH   = 'north'
SOUTH   = 'south'
EAST    = 'east'
WEST    = 'west'

application.lua

application.lua

アプリケーション実行に関わる関数集です。
まだ大したプログラムを作ってないので簡素なものです。

-----------------------------------------------------------
-- application.lua
-- プログラム実行に関わるAPI
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
os.loadAPI('/lib/apis/turtleutils')

-----------------------------------------------------------
-- turtleで実行されるプログラムの一般的な初期化処理をするよ
-- @param program_name [in]実行中のプログラム名
-----------------------------------------------------------
function turtleInit(program_name)
  -- 起動メッセージを表示しとくよ
  print(string.format('%s(%dDay) %s: <%s> ikude!!',
    textutils.formatTime(os.time(), true),
    os.day(),
    turtleutils.getCompName(),
    program_name
  ))

  turtleutils.resetSlot()  -- タートルの選択スロットをリセットしとくよ

  -- 他になんか思いついたら追加するよ
end

-----------------------------------------------------------
-- コンフィグファイルを読み込んで返すよ
-- メッセージ表示もするよ
-- @param path [in]コンフィグファイルのpath
-- @return 読み込んだコンフィグテーブル
-----------------------------------------------------------
function loadConfig(path)
  print('config<'.. path ..'> wo load suru yo!')
  local config = dofile(path)
  assert(config, 'config load SIPPAI SITADE nande yanenn...')

  print('config load seikou!!')
  return config
end