読者です 読者をやめる 読者になる 読者になる

Minecraftとタートルと僕

PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。

クラフティタートルプログラミング チュートリアル(4)

さて、少し寄り道したけれど、クラフティタートルのチュートリアルを続けましょう。

前回のプログラムの改善

対応できるレシピを増やそう

前回のプログラムは、サトウキビ→砂糖のような1x1レシピに対応するためのプログラムだけど、
もっと多くのレシピに対応できるようプログラムを改善しましょう。

今回、対応させるのは以下のような3種類のレシピ。
そして強調しておきたいのは、「材料は問わず」「これらの形に並べる」レシピに対応していること。
たとえば、3x3レシピなら、金ナゲットだろうが、鉄ナゲットだろうがなんであろうとこのプログラムで対応できます。

  • 「2x2レシピ」
    • f:id:hevohevo:20131208212005p:plain (画像は焼石。クラフトすると焼石レンガ4個になる)
  • 「3x3レシピ」
    • f:id:hevohevo:20131208212409p:plain (画像は金ナゲット。クラフトすると金インゴット1個になる)
  • 「3x3ドーナツ型レシピ」
    • f:id:hevohevo:20131208212009p:plain (画像はオークの葉。クラフトするとプラントボール1個になる)

どうやってこれを実装するのか

簡潔に言うならば、「上図のような配置に材料を並べる関数」を作るだけ。
あとは前回のプログラム中で、turtle.craft()する直前にこの関数を実行すれば完成です。

それでは材料を並べる関数を作るために、一番単純な、2x2レシピの形に並べる手順を考えてみましょう。

2x2レシピの形に材料を並べる手順

  • まず、真上のチェストから材料を1スタック取ったとき、タートルのインベントリは以下のように、スロット1にアイテム64個が収まっている。
    • f:id:hevohevo:20131208213953p:plain
  • これを以下の図のように振り分ければ良い。
    • f:id:hevohevo:20131208214338p:plain
  • なお、スロット番号を重ねたのが以下の図。
    • f:id:hevohevo:20131208215351p:plain

以上を踏まえて、正確に手順(アルゴリズム)を記述するならば次のようになります。

  1. 材料スロット(スロット1)にあるアイテムの個数を数える →「64個」
  2. 今回は4等分するのだから、64/4=「16」個ずつ振り分ければいい。
  3. 現在材料があるスロット1から「スロット1」「スロット2」「スロット5」「スロット6」に16個ずつ材料を移動する
  4. 材料分配終了。あとはクラフトするだけ!

これを実装した関数が以下のように。

functions distMaterials2x2()
  local nTotal = turtle.getItemCount(MATERIAL_SLOT)
  local n = nTotal/4
 
  turtle.select(MATERIAL_SLOT)
  turtle.transferTo(1,n)    --スロット1からスロット1にn個移動 ←実質的に何もしない
  turtle.transferTo(2,n)
  turtle.transferTo(5,n)
  turtle.transferTo(6,n)
end

なお、使っているTurle APIについて簡単に説明すると次のとおり。

  • turtle.getItemCount(スロット番号): 指定スロットにあるアイテムの個数を返す
  • turtle.transfer(Toスロット,個数): 現在の選択スロット(ここではスロット1)にあるアイテムを、「Toスロット」へ、「個数」分だけ移動する。個数部分に小数が入ったら、その整数部分の個数を移動する(n=2.5ならば2個移動)。

3x3レシピ、3x3ドーナツレシピの配置に並べる関数

同じように考えるならば、3x3レシピ、3x3ドーナツ型レシピの形に分配する関数は次のように。

functions distMaterials3x3()
  local nTotal = turtle.getItemCount(MATERIAL_SLOT)
  local n = nTotal/9
 
  turtle.select(MATERIAL_SLOT)
  turtle.transferTo(1,n)
  turtle.transferTo(2,n)
  turtle.transferTo(3,n)
  turtle.transferTo(5,n)
  turtle.transferTo(6,n)
  turtle.transferTo(7,n)
  turtle.transferTo(9,n)
  turtle.transferTo(10,n)
  turtle.transferTo(11,n)
end


functions distMaterials3x3donut()
  local nTotal = turtle.getItemCount(MATERIAL_SLOT)
  local n = nTotal/8
 
  turtle.select(MATERIAL_SLOT)
  turtle.transferTo(1,n)
  turtle.transferTo(2,n)
  turtle.transferTo(3,n)
  turtle.transferTo(5,n)
  turtle.transferTo(7,n)
  turtle.transferTo(9,n)
  turtle.transferTo(10,n)
  turtle.transferTo(11,n)
end

ここで振り返って考える

これらには、無駄な記述や同じような記述の繰り返しがありませんか?
今後、新しいレシピ追加するときにも、このように同じような記述を繰り返さなくてはならないのでしょうか。

無駄ははぶこう。工夫しよう。DRY!DRY!

汎用型アイテム分配関数を作る

次のような関数を作りましょう。

  • distMaterials(<分配するスロット番号のリスト>)
    • distMaterials({1,2,5,6}) と実行すると、スロット1、2、5、6に均等にアイテムを分配する。

このような関数を作れば、1種類のアイテムを均等分配するタイプのレシピすべてに対応できます。

そして実装したのが、こちら。

-- distMaterials({1,2,5,6}) -- 2x2
-- distMaterials({1,2,3,5,7,9,10,11}) -- 3x3donut
function distMaterials(slots)
  local nTotal = turtle.getItemCount(MATERIAL_SLOT)
  local n = nTotal/(#slots)
 
  turtle.select(MATERIAL_SLOT)
  for i,v in pairs(slots) do
    turtle.transferTo(v,n)
  end
end
  • 「#slots」は、関数の引数であるslotsの要素の個数を返す。つまり「nTotal/(#slots)」によって均等割りしたときの個数を計算している
  • 「for i,v in pairs(slots) do ~ end」は、テーブルの中身を順番に処理するときの常套句。こういうのをイテレータと呼ぶ。
    • do~endの中で、vの値だけを使っている。vにはテーブルslotsの中の値(value)が順に入ってくる。

今回はここまで

かなり長くなってきたので、今回はこれでいったん終了です。
次回は、distMaterials()関数を使って実際に動くプログラムを組んでみましょう。

え? もったいぶらずにさっさと動くプログラムを出せって? しかたないなぁのび太君は。
http://pastebin.com/DgmhfHpM

それではまた次回。