akaneutils.lua

akaneutils.lua

ユーティリティ関数集です。

-----------------------------------------------------------
-- @file  akaneutils(lua)
-- @brief 茜ちゃんによるユーティリティ集や
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------

-----------------------------------------------------------
-- 引数なしの関数を作るよ<br>
-- CCのparallel APIに渡したりするのに便利やで
--
-- @param func 元の関数
-- @param ... 関数に渡す引数
-- @return 作った関数
-----------------------------------------------------------
function getNoArgFunc(func, ...)
  local args = { ... } -- ...のままではクロージャで参照できんからテーブルに入れるよ
  if #args < 1 then
    return func -- 引数がなければそのまま返す
  end
  return function()
    return func(unpack(args)) -- argsを参照できるのはクロージャだからやで
  end
end


-----------------------------------------------------------
-- テーブルを上書きするよ<br>
-- baseをoverで上書きするよ
-- luaのテーブルは参照渡しだから、関数呼び出し元の方のテーブルも上書きされてるよ
--
-- @param base 上書きされるテーブル
-- @param over 上書きするテーブル
-----------------------------------------------------------
function overwrite(base, over)
  for k, v in pairs(over) do
    base[k] = v -- テーブル上書きや! 以上!
  end
end

-----------------------------------------------------------
-- テーブルにイベントハンドラを追加するよ<br>
-- 追加前に登録されてたハンドラもちゃんと呼ぶよ
-- いまんとこ追加されたハンドラを削除する方法は無いよ
-- CCのイベントシステム使わん場合の簡易な方法やな
-- @param table [in/out]ハンドラを登録するテーブル
-- @param key [in]登録対象のキー
-- @param handler [in]登録するイベントハンドラ
-----------------------------------------------------------
function addHandler(table, key, handler)
  local org_handler = table[key]
  local new_handler = handler
  if type(org_handler) == 'function' then -- すでにハンドラが登録されてたら
    new_handler = function(...) -- 元のハンドラと追加するハンドラ両方呼ぶ関数を作るよ
      org_handler(...)
      handler(...)
    end
  end
  table[key] = new_handler -- ハンドラ登録や!
end

-----------------------------------------------------------
-- ファイルを書き込む準備をするよ
-- いまんとこ具体的には必要なディレクトリがなければ作る、ってだけやけどな
-- @param path [in]書き込む予定のファイルのパス
-----------------------------------------------------------
function prepareToWrite(path)
  local dir = fs.getDir(path)
  if not fs.exists(dir) then -- ディレクトリが存在するか見るで
    fs.makeDir(dir) -- 無いから作るよ
  end
end

-----------------------------------------------------------
-- 文字列をセパレータで分割する、イテレータ関数を作って返すよ
-- セパレータが省略されると','を使うよ
-- @param str [in]分割する文字列
-- @param separator [in]セパレータ・区切り文字。省略時は','
-- @return イテレータ関数
-----------------------------------------------------------
function stringSplitter(str, separator)
  separator = separator or ',' -- 省略時はカンマを使うで
  local init = 1
  return function()
    if not init then return nil end -- 終わりや
    local sep_head, sep_tail, col_head, col_tail

    sep_head, sep_tail = string.find(str, separator, init, true) -- セパレータを探すよ
    if sep_head then -- 見つかったか?
      col_head, col_tail = init, sep_head - 1 -- 見つかったよ!
      init = sep_tail + 1 -- 次はセパレータの次の文字から探すよ
    else
      col_head, col_tail = init, -1 -- 見つからんかった
      init = nil -- 次はもうないよ
    end
    return string.sub(str, col_head, col_tail) -- 分割や!
  end
end

-----------------------------------------------------------
-- セパレータで区切られた文字列を分割するよ
-- セパレータが省略されると','を使うよ
-- strがnilの場合はnilを返すよ
-- @param str [in]分割する文字列
-- @param separator [in]セパレータ・区切り文字。省略時は','
-- @return 分割された文字列を格納した配列
-----------------------------------------------------------
function splitString(str, separator)
  if not str then return nil end
  local result = {} -- 結果を入れる配列や

  for column in stringSplitter(str) do -- 分割するよ
    result[#result + 1] = column
  end
  return result
end

-----------------------------------------------------------
-- テキストファイルを読んで1行ずつ返す、イテレータ関数を作って返すよ
-- 第二戻り値でopen中のファイルハンドルも返すで
-- ファイルの最後まで読み込まないとファイルクローズしないから
-- forの途中で抜け出す場合は、そっちでファイルクローズしないとあかんよ
-- @param path [in]csvファイルのパス
-- @return イテレータ
-- @return ファイルハンドル
-----------------------------------------------------------
function fileEachLine(path)
  -- ファイルがなければnilを返すだけの関数を返すよ
  if not fs.exists(path) then return (function() return nil end) end

  local h = fs.open(path, 'r')
  assert(h, 'fileEachLine cant open : '.. path)

  -- イテレータ関数
  local function iterator()
    local line = h.readLine() -- 次の行を読むよ
    if not line then -- ファイルはもう終わりか?
      h.close()
      h = nil
    end
    return line, h
  end

  return iterator, h
end

-----------------------------------------------------------
-- csvファイルを読んでカラムの配列を返す、イテレータ関数を作って返すよ
-- ファイルのクローズに関してはfileEachLine()と同じやで
-- セパレータが省略されると','を使うよ
-- @param path [in]csvファイルのパス
-- @param separator [in]セパレータ・区切り文字。省略時は','
-- @return イテレータ
-- @return ファイルハンドル
-- @see fileEachLine
-----------------------------------------------------------
function csvEachLine(path, separator)
  separator = separator or ','

  local each_line, h = fileEachLine(path)

  -- イテレータ関数
  local function parser()
    return splitString(each_line(), separator), h
  end

  return parser, h
end

turi_startup.lua

turi_startup.lua

turiのスタートアッププログラムです。 startupの中で、

shell.run('/start/turi_startup')

のように呼び出すことを想定しています。
ものすごく眠い時に書いたやつをそのまま使っていて酷いですが、直す予定もないです。

-----------------------------------------------------------
-- turi_startup.lua
-- 自動釣りプログラムのスタートアップ
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
os.loadAPI('/lib/apis/application')
os.loadAPI('/lib/apis/const')
os.loadAPI('/lib/apis/identify')
os.loadAPI('/lib/apis/turtlefuel')

application.turtleInit('turi_startup')

-----------------------------------------------------------
-- プログラム引数処理
-----------------------------------------------------------
local args = { ... }
local config_path = args[1] or 'turi_config' -- 第一引数はコンフィグファイル名や

-----------------------------------------------------------
-- コンフィグファイル読み込み
-----------------------------------------------------------
local config = application.loadConfig(config_path)

-----------------------------------------------------------
-- 釣りをする方向に水ブロックがあるか
-----------------------------------------------------------
local function isWater(direction)
  local success, data = turtleapis.INSPECT[direction]()
  if identify.isWaterBlock(data) then return true end
  return false
end

local search_water = {
  [const.FORWARD] = function()
    for i=1, 4 do
      if isWater(const.FORWARD) then return true end
      turtle.turnRight()
    end
    return false
  end,

  [const.UP] = function() return isWater(const.UP) end,
  [const.DOWN] = function() return isWater(const.DOWN) end,
}

if not search_water[config.turi.dir_fishing]() then
  -- 水ブロックは見つからんかった…
  return false
end

shell.run('turi')

jidoturi.lua

jidoturi.lua

自動釣りクラス

-----------------------------------------------------------
-- jidoturi.lua
-- 自動釣りAPI
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
os.loadAPI('/lib/apis/akaneutils')
os.loadAPI('/lib/apis/turtleapis')
os.loadAPI('/lib/apis/turtlefuel')
os.loadAPI('/lib/apis/identify')
os.loadAPI('/lib/apis/simattyau')

-----------------------------------------------------------
-- 自動釣りオブジェクトを作って返すよ<br>
-- つり関数としまっちゃおうね関数はここに入るよ
--
-- オブジェクトは釣りをした回数、釣れた回数を覚えてるよ
--
-- @param setting 自動釣りオブジェクトの設定テーブル
-- @return 自動釣りオブジェクト
-----------------------------------------------------------
function new(setting)
  -- テーブル作成。デフォルト値も設定するよ
  -----------------------------------------------------------
  -- @class table
  -- @name turi
  -- @description 自動釣りオブジェクト
  -- 自動でひたすら釣りするよ。釣ったものはチェストにしまうよ
  -- 焼ける魚とソレ以外でしまうチェストを分けられるよ
  -- 燃料が減ったら補給するよ
  -- @field hokyu           自動補給管理オブジェクト
  -- @field simattyau       アイテム収納管理オブジェクト
  -- @field dir_fishing     釣りする方向
  -- @field dir_store_fish  焼ける魚をしまうチェストの方向
  -- @field dir_store_other 焼ける魚以外をしまうチェストの方向
  -- @field wait_fishing    釣りの待機時間
  -- @field wait_check_slot インベントリスロット毎の確認待機時間
  -- @field end_fishing      釣り終了時に呼ばれるイベントハンドラ
  -- @field stats_auto_load 生成時に統計情報を自動で読み込むかどうか
  -- @field stats_save_add  統計情報保存時に追加モードで書き込むかどうか
  -- @field stats_result_path 統計情報ファイルのパス
  -- @field stats_items_path  釣れたアイテム情報ファイルのパス
  -----------------------------------------------------------
  local turi = {
    hokyu           = turtlefuel.new(),
    simattyau       = simattyau.new(),
    dir_fishing     = const.FORWARD,-- 釣りをする方向や
    dir_store_fish  = const.UP,     -- 魚をしまう方向や
    dir_store_other = const.DOWN,   -- 魚以外をしまう方向や
    wait_fishing    = 30, -- 釣りの待ち時間(秒数)や
    wait_check_slot = 1, -- しまっちゃおうねチェックの待ち時間(秒数)や
    stats_auto_load = false,
    stats_save_add  = false,
    stats_result_path = 'turi.csv',
    stats_items_path  = 'turi_items.csv',
  }
  -- クロージャOOP
  local self = turi

  -- private
  local tried   = 0 -- 釣りした回数やで
  local success = 0 -- 釣れた回数やで

  local start_time -- 釣りを始めた時刻(os.clock())

  -----------------------------------------------------------
  -- 釣りを始める関数だよ<br>
  -- 釣り回数(self.tried)はまだカウントアップしないで
  -----------------------------------------------------------
  function turi.startFishing()
    return turtleapis.ATTACK[self.dir_fishing]() -- 釣るよ
  end

  -----------------------------------------------------------
  -- 釣り上げる関数だよ<br>
  -- ここで釣れた回数(self.success)をカウントアップするよ
  -- 釣り回数(self.tried)も釣り上げた時点でカウントアップするで
  -----------------------------------------------------------
  function turi.endFishing()
    local result = turtleapis.DIG[self.dir_fishing]() -- 釣り上げるよ
    tried = tried + 1 -- 釣り上げたらまず、釣り回数をカウントアップするで
    if result then
      success = success + 1 -- 釣れたら釣れた回数をカウントアップや!
    end
    self.onEndFishing(result) -- 「釣り上げたで!」イベント発生
    return result
  end

  -----------------------------------------------------------
  -- 「釣り上げたで!」イベントを起こすよ<br>
  -- イベントと言ってもturiではCCのイベントシステムは使わんで
  -- 登録されたイベントハンドラを直接呼ぶよ
  -- @param result [in]釣りの結果
  -----------------------------------------------------------
  function turi.onEndFishing(result)
    local handler = self.end_fishing
    if not handler then return end
    handler(self, result)
  end

  -----------------------------------------------------------
  -- タートルが釣りするよ<br>
  --
  -- @param wait [in]釣りの待ち時間(秒数)や。省略時はself.waitが使われるよ
  -----------------------------------------------------------
  function turi.turi(wait)
    wait = wait or self.wait_fishing

    while true do -- メインループや
      print('turude')
      self.startFishing() -- 釣り開始やで
      sleep(wait) -- ちょっと待つよ
      if self.endFishing() then -- 釣り上げるで
        print('tureta')
      else
        print('turen katta')
      end
    end
  end

  -----------------------------------------------------------
  -- しまっちゃおうねAPIに渡す分類コールバック関数だよ<br>
  --
  -- スロットを調べて、魚じゃなければチェストにしまうよ
  -- 焼ける魚とそれ以外でしまうチェストを分けるで
  -- 魚の場合はスタックがいっぱいな場合だけしまうよ
  -- @param slot_no [in]対象スロット番号
  -- @param force [in]trueの場合はスタックいっぱいじゃなくてもしまうよ
  -- @return しまったらture / しまわなかったらfalse
  -----------------------------------------------------------
  function turi.bunrui(slot_no, force)
    local item_detail = turtle.getItemDetail(slot_no) -- 対象のアイテム情報やで
    if not item_detail then return false end -- スロットが空ならなにもしないよ

    local direction = self.dir_store_other -- アイテムをしまう先
    if identify.isFish(item_detail) then -- 魚やろか?
      -- 魚の場合、スタックいっぱいやなかったら何もしないで
      if not force and turtle.getItemSpace(slot_no) ~= 0 then return false end

      if identify.isRoastableFish(item_detail) then -- この魚、焼けるか?
        direction = self.dir_store_fish -- 焼ける魚ならこっちのチェストや
      end
    end
    print('slot '.. slot_no ..' wo simau yo!') -- しまうで~
    return direction
  end

  -----------------------------------------------------------
  -- 持っとるアイテムをチェストにしまうよ<br>
  --
  -- インベントリスロットを順番に見ていって、
  -- アイテムがあったらチェストにしまうよ
  -- 焼ける魚とそれ以外でしまうチェストを分けるで
  -- 魚の場合はスタックがいっぱいな場合だけしまうよ
  -- @param wait [in]1スロット処理するごとの待機時間やで
  -----------------------------------------------------------
  function turi.simattyaoune(wait)
    wait = wait or self.wait_check_slot

    while true do -- メインループや
      for i = 1, 16 do -- タートルのインベントリスロットを順番に見てくよ
        self.simattyau.simau(self.bunrui, i)
        sleep(wait)
      end
    end
  end

  -----------------------------------------------------------
  -- 持っとるアイテム全部をチェストにしまうよ<br>
  --
  -- インベントリスロットを順番に見ていって、
  -- アイテムがあったらチェストにしまうよ
  -- 焼ける魚とそれ以外でしまうチェストを分けるで
  -- 魚の場合はスタックがいっぱいな場合だけしまうよ
  -----------------------------------------------------------
  function turi.simattyaouneAll()
    local bunrui = self.bunrui
    for i = 1, 16 do -- タートルのインベントリスロットを順番に見てくよ
      self.simattyau.simau(
        function(slot_no) return bunrui(slot_no, true) end,
        i
      )
    end
  end

  -----------------------------------------------------------
  -- 自動で釣りするよ<br>
  -- parallel.waitForAnyを使って自動で釣りを繰り返すよ
  -- 追加したいコルーチンを引数で渡せるよ
  -- @param ... 追加実行したいコルーチン(可変数)
  -----------------------------------------------------------
  function turi.doFishing(...)
    -- 釣り実行
    parallel.waitForAny(
      self.turi,
      self.simattyaoune,
      self.hokyu.hokyu,
      ...
    )
  end

  -----------------------------------------------------------
  -- 釣りを始めてからの経過時間を返すよ<br>
  -- まだ釣りを始めてなければ0を返すよ
  -----------------------------------------------------------
  function turi.getTimeElapsed()
    if not start_time then return 0 end
    return os.clock() - start_time
  end

  -----------------------------------------------------------
  -- 釣った回数を返すよ
  -----------------------------------------------------------
  function turi.getTriedCount()
    return tried
  end

  -----------------------------------------------------------
  -- 釣れた回数を返すよ
  -----------------------------------------------------------
  function turi.getSuccessCount()
    return success
  end

  -----------------------------------------------------------
  -- 統計情報をリセットするよ
  -- 経過時間・釣った回数・釣れた回数をリセットするよ
  -----------------------------------------------------------
  function turi.resetStats()
    tried, success = 0, 0
    start_time = os.clock()
  end

  -----------------------------------------------------------
  -- 統計情報を(csv)ファイルに書き込むよ
  -- @param result_path [in]統計ファイルパス
  -- @param items_path [in]アイテムファイルパス
  -----------------------------------------------------------
  function turi.saveStats(result_path, items_path)
    result_path = result_path or self.stats_result_path
    items_path  = items_path  or self.stats_items_path

    akaneutils.prepareToWrite(result_path) -- 書き込み準備や

    local mode = 'w'
    if self.stats_save_add then mode = 'a' end
    local h = fs.open(result_path, mode) -- ファイルオープン
    -- 釣り結果をcsv出力するよ
    h.writeLine(string.format('%d,%d,%d,%d,%d',
      self.wait_fishing,          -- 釣り待機時間の設定値
      self.getTriedCount(),       -- 釣り回数
      self.getSuccessCount(),     -- 釣れた回数
      self.getTimeElapsed(),      -- 経過時間(秒)
      self.hokyu.getTotalUsage()  -- 消費燃料(FL)
    ))
    h.close() -- ファイルクローズ

    -- 釣れたアイテムをcsv出力するよ
    self.simattyaouneAll() -- まず釣れたアイテムを全部しまっちゃうよ
    self.simattyau.saveStats(items_path) -- しまったアイテムをcsv出力や
  end

  -----------------------------------------------------------
  -- 統計情報(csv)ファイルを読み込むよ
  -- @param result_path [in]統計ファイルパス
  -- @param items_path [in]アイテムファイルパス
  -----------------------------------------------------------
  function turi.loadStats(result_path, items_path)
    result_path = result_path or self.stats_result_path
    items_path  = items_path  or self.stats_items_path
    self.resetStats()
    -- 統計ファイルを読み込むよ
    -- 複数行あった場合は最後の行を読み込むねん
    local result
    for columns in akaneutils.csvEachLine(result_path) do
      if #columns == 5 then result = columns end
    end
    if result then
      -- wait_fishingは読み込まないよ
      tried       = tonumber(result[2])
      success     = tonumber(result[3])
      start_time  = os.clock() - tonumber(result[4])
      self.hokyu.addUsage(tonumber(result[5]))
    end

    -- アイテムcsvを読み込むよ
    self.simattyau.loadStats(items_path)
  end


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

  -- 統計情報初期化
  self.resetStats()
  if self.stats_auto_load then
    self.loadStats()
  end

  return self
end

turi_config.lua

turi_config.lua

コンフィグファイルですが実態はLuaソースコードそのものです。
ここでオーバーライドする関数を記述してしまうのは暴挙なのかアリなのか。

-----------------------------------------------------------
-- turi_config
-- 自動釣りプログラムの設定ファイル
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
local config = {}
local turi = {}
local hokyu = {}
local save = {}
-------------------------------------------------------------------------------
-- 動作設定 ここから
--   方向は、[forward / up / down / right / left / back]のどれかを使ってね
-------------------------------------------------------------------------------
-----------------------------------------------------------
-- 釣り設定
-----------------------------------------------------------
-------------------------------------
-- 釣りをする方向(水ブロックの方向)
turi.dir_fishing     = 'forward'

-------------------------------------
-- 焼ける魚をしまうチェストの方向
-- (bunruiをオーバーライドする場合はこれは使われないよ)
turi.dir_store_fish  = 'down'

-------------------------------------
-- 焼ける魚以外をしまうチェストの方向
-- (bunruiをオーバーライドする場合はこれは使われないよ)
turi.dir_store_other = 'down'

-------------------------------------
-- 釣りの待ち時間
turi.wait_fishing    = 25

-------------------------------------
-- 統計情報を自動読込するかどうか
turi.stats_auto_load = true

-------------------------------------
-- 統計情報保存時に追加モードで書き込むかどうか
turi.stats_save_add  = false

-------------------------------------
-- 釣り結果出力ファイルのパス
turi.stats_result_path  = '/stats/turi.csv'

-------------------------------------
-- 釣り結果出力ファイルのパス
turi.stats_items_path   = '/stats/turi_items.csv'

-----------------------------------------------------------
-- 燃料補給設定
-----------------------------------------------------------
-------------------------------------
-- 燃料チェストの方向
hokyu.direction   = 'right'

-------------------------------------
-- 燃料確認間隔(秒)
hokyu.wait        = 60

-------------------------------------
-- 燃料補給閾値
-- Fuel levelがこの数値未満になったら補給するよ
hokyu.threashold  = 100

-------------------------------------
-- 燃料十分量
-- 補給時、Fuel levelがこの数値以上になったら補給を完了するよ
hokyu.enough      = 100

-----------------------------------------------------------

-------------------------------------
-- 釣りの最大回数
-- 指定回数釣りしたら止まるで
-- nilまたはfalseを設定すると無制限に釣りを繰り返すよ
config.turi_limit = 1000

-----------------------------------------------------------
-- 釣り結果を保存するファイル
-----------------------------------------------------------
-------------------------------------
-- 出力頻度。釣りを何回したら出力するか
save.frequency     = 1

-------------------------------------
-- 釣りオブジェクトの走査間隔(秒数)
save.interval      = 10

-----------------------------------------------------------
-- gomi_bunrui チェスト設定
-----------------------------------------------------------
-------------------------------------
-- ゴミ箱の方向
-- 皮ブーツとか、チェストが山盛りになるやつはゴミ箱行きやで
local dir_store_gomi = 'up'

-------------------------------------
-- 釣果チェストの方向
-- ゴミ以外は全部こっちに入れるよ
local dir_store_other = 'down'

-------------------------------------------------------------------------------
-- 動作設定 ここまで
-------------------------------------------------------------------------------

-----------------------------------------------------------
-- 自動釣りアイテム分類関数オーバーライド
-----------------------------------------------------------
-----------------------------------------------------------
-- アイテムがゴミかどうか調べるよ
-- 長時間放置が前提だから
-- 皮ブーツとか釣り竿はチェストが溢れんようにゴミ扱いやで
-----------------------------------------------------------
local function kore_gomika(item_detail)
  -- 革ブーツ・釣り竿、水入り瓶、サドルならゴミや
  for _i, v in ipairs {
     'isLeatherBoots', 'isFishingRod', 'isWaterPotion', 'isSaddle',
  } do
    if identify[v](item_detail) then return true end
  end
  return false
end

-----------------------------------------------------------
-- しまっちゃおうねAPIに渡す分類コールバック関数やで
-- この関数で自動釣りAPIのアイテム分類関数をオーバーライドするよ
--
-- スロットを調べて、ゴミアイテムならゴミ用チェスト行きや
-- ゴミでも魚でもなければ釣果用チェストにしまうで
-- 魚の場合はスタックがいっぱいなら釣果用チェストにしまうよ
--
-- @param slot_no [in]対象スロット番号
-- @param force [in]trueの場合はスタックいっぱいじゃなくてもしまうよ
-- @return しまったらture / しまわなかったらfalse
-----------------------------------------------------------
local function gomi_bunrui(slot_no, force)
  local item_detail = turtle.getItemDetail(slot_no) -- 対象のアイテム情報やで
  if not item_detail then return false end -- スロットが空ならなにもしないよ

  -- ゴミチェック
  if kore_gomika(item_detail) then
    print('gomibako ikiya')
    return dir_store_gomi -- コイツはゴミ箱行きや
  end

  if identify.isFish(item_detail) then -- 魚やろか?
    -- 魚の場合、スタックいっぱいやなかったら何もしないで
    if not force and turtle.getItemSpace(slot_no) ~= 0 then return false end
  end
  print('slot '.. slot_no ..' wo simau yo!') -- しまうで~
  return dir_store_other
end

turi.bunrui = gomi_bunrui

-----------------------------------------------------------
turi.hokyu      = turtlefuel.new(hokyu)
config.turi     = turi
config.save     = save

return config

turi.lua (part √2)

turi.lua

自動釣りプログラムturiのメイン部分です。
自動釣り機能自体はjidoturi.luaなどの他ファイルに記述されています。
本ファイルでは自動釣りクラスの準備と実行、そして自動釣りクラスで追加実行される、定期的な統計ファイル保存コルーチンと終了判定コルーチンの定義を行っています。

-----------------------------------------------------------
-- turi.lua
-- 自動釣りプログラム
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------
dofile('/lib/turi_lib')

application.turtleInit('turi')

-----------------------------------------------------------
-- プログラム引数処理
-----------------------------------------------------------
local args = { ... }
local config_path = args[1] or 'turi_config' -- 第一引数はコンフィグファイル名や

-----------------------------------------------------------
-- コンフィグファイル読み込み
-----------------------------------------------------------
local config = application.loadConfig(config_path)

-----------------------------------------------------------
-- result_file定義ブロック
-----------------------------------------------------------
local result_file = config.save
do
  -- クロージャOOPや
  local self = result_file

  -- 出力済みの釣り回数
  self.last = 0

  -----------------------------------------------------------
  -- 釣り結果を出力するで
  -- @param turi [in]自動釣りオブジェクト
  -- @param force [in]trueなら回数に関係なく書き込むよ
  -----------------------------------------------------------
  function result_file.write(turi, force)
    -- 書き込むのは釣り何回かに1回だけやで
    local tried = turi.getTriedCount()
    if not force and (tried % self.frequency ~= 0 or tried <= self.last) then
      return
    end

    print('tureta item csv wo save suruyo!')
    turi.saveStats() -- 書き込むよ

    self.last = tried -- 最後に書き込んだ釣り回数を覚えとくよ
  end

  -----------------------------------------------------------
  -- 自動釣りの結果保存用コルーチン
  -- @param turi [in]自動釣りオブジェクト
  -----------------------------------------------------------
  function result_file.saveResult(turi)
    while true do
      sleep(self.interval)
      self.write(turi)
    end
  end

end

-----------------------------------------------------------
-- 釣り終了判定コルーチン
-- @param turi [in]自動釣りオブジェクト
-----------------------------------------------------------
function checkExit(turi)
  repeat
    sleep(turi.wait_fishing - 1) -- 釣り1回につき1回動いて欲しいねん
  -- 釣り終了判定や
  until config.turi_limit and turi.getTriedCount() >= config.turi_limit
end

-----------------------------------------------------------
-- main
-----------------------------------------------------------
-- 自動釣りオブジェクト生成
local turi = jidoturi.new(config.turi)

-- 自動釣りで一緒に動かすコルーチンのリストを作るよ
local coroutines = {
  akaneutils.getNoArgFunc(result_file.saveResult, turi),
}
if config.turi_limit then -- 釣り回数制限があるときだけ追加するよ
  coroutines[#coroutines + 1] = akaneutils.getNoArgFunc(checkExit, turi)
end

-- 釣り実行
print('turi suruyo!! ('.. turtlefuel.getFuelLevelString() ..')')
turi.doFishing(unpack(coroutines))

-- 終わり
print(string.format('%d kai turi sitasi tomaru yo!', turi.getTriedCount()))
print(string.format('%d kai tureta de!', turi.getSuccessCount()))
result_file.write(turi, true) -- 最後に統計ファイル書き出すよ