採掘タートルで整地する: (6)松明設置、チェストへのアイテム格納機能の追加
前回までのお話
一度に3つの山を整地(切土)する関数を使うことで、これまでの1.5倍効率がよくなったよ!(当社比)
すばらしいプログラムですね!(独自調査による評価)
今回のお話
広い範囲を整地するとなると敵MOBが湧くのが心配ですね。松明を等間隔で設置する機能を付け加えましょう。
また、整地するうちに掘ったブロックを持ちきれなくなります。
持ちきれなくなったら、チェストを設置して、アイテムを格納する機能も付け加えましょう。
どのタイミングで松明やチェストを設置するのか
まずは松明です。
松明を設置したブロックの明るさは14になり、そこから1ブロック離れるごとに明るさは1ずつ減っていきます。
普通のMOBは明るさ7以下で湧きはじめるので、安全のために地上を明るさ8以上に維持しなくてはなりません。
どのような配置で松明を置けば効率的かという議論はすでにいろいろなところでなされていますが、ここでは設置のしやすさを考えて、松明を縦横6ブロック間隔で置く方法を採用します。
具体的に6ブロック間隔で置いたときの明るさの度合いを示したのが以下の図です。参考までに。
とはいえ厳密に6ブロック間隔というのは現在使っているアルゴリズム(一度に3つの山を切土する)上で実現するのは面倒なので、5ブロック間隔で置くことにします。
間隔が1狭い程度ならば効率もさほど問題ないですよね?
次にチェストを置いてアイテムを収納するタイミングについてです。
タイミングは大きく分けて、
- タートルのインベントリを定期的に監視してアイテムが一定以上貯まったらチェストを設置する方法
- インベントリは気にせず、等間隔でチェストを設置する方法
の2種類が考えられますが、今回は、実装の簡易さを重視して前者のアイテムが貯まった時を採用します。
具体的な設置タイミング
ここで注意しなくてはならないのが、松明を置くタイミングとチェストを置くタイミングが重なってしまい、どちらか一方が置かれているために、もう片方を置くのに失敗するというトラブルです。
そうならないよう、厳密にそれぞれを置く方向と場所を決めておきましょう。
以下の図は、この整地プログラムの中心となるseichiThreeStep()
関数、その移動の軌跡を示しています。
今回は、この関数実行直後に松明とチェストを取り扱うことにしましょう。
- 松明設置の条件が満たされていたら真後ろを振り向いて(地上に)松明設置
- チェスト設置条件が満たされていたらその場で頭上に(空中に)チェスト設置
のように決めておけば、松明とチェストが互いに干渉することもないですね。
松明は5ブロック間隔で置くことにしましたが、これは以下の2つの条件を同時に満たすときです。
- 3つの山を切土する
seichiThreeStep()
の実行1回おきに(つまり2回に1回)松明を設置することで、奥行き方向(縦方向)に5ブロック間隔となる - 指定した幅(WIDTH)を整地するために折り返し実行する
seichiOneLine()
の実行5回おきに(つまり6回に1回)松明を設置することで、幅方向(横方向)に5ブロック間隔となる
そしてチェストを置くかどうかは、インベントリ内を確認することで判断しましょう。
ここまで具体的に説明すると、プログラミングがある程度できる人ならば、「あぁ、あのあたりに関数を埋め込んであげればいいのだな、そして条件式は・・・」とだいたい予想がつくのではないでしょうか。
おおよそ予想通りだと思います。具体的なコードは以下をご確認ください。
ソースコード
version0.2からの変更点
前回の記事で紹介したversion0.2からは、以下の点を修正しています。
- 指定したブロックを任意の方向に設置する
placeItem()
関数の定義 - 松明設置に関する
placeToarch()
関数の定義 - チェスト設置に関する
putItemsInChest()
関数の定義 - チェスト設置後、インベントリ内のアイテムを格納する関数
dropAllItems()
も定義 seichiOneLine()
関数の中に、placeToarch()
関数と、putItemsInChest()
関数を埋め込み。- 入手したブロックをチェストに格納するので、version0.2のときにあった、入手ブロックの個数を数え上げる機能は使えなくなりました。実現するにはまた別の方法を使わなくてはならないので、今回はそのまま削っています。
ソースコード読解のポイント
全てを細かく見ていくとキリがないので大まかにポイントだけ抑えていきましょう。
placeItem(slot, place_func)
の定義- 引数としてスロット番号を指定し、設置時に使う関数もオプションとして指定できます。
- 特に関数を引数として渡している点にご注意。Luaは関数がファーストクラスオブジェクトとなっているので、関数それ自体を関数の引数として渡すことができます。
- 関数を特に指定しないときには、デフォルトで正面にブロックを設置します。
- たとえば設置する方向を上にするならば、
turtle.placeUp
を指定してあげましょう。なお、ここで関数名末尾の()はつけてはいけません。つけるとその関数を実行した結果を引数として渡すことになってしまいます。
placeToarch(i, current_w)
の定義- 縦横5ブロックおきに設置するために、
seichiThreeStep()
の実行が何回目(i)か、そして現在の横方向の位置(current_w)情報を引数として渡しています。
- 縦横5ブロックおきに設置するために、
putItemsInChest()
- 12番目のスロットを見て、何かアイテムが入っていたらチェストにそれらを全て格納しようと試みます。
- どのスロットを見るべきかは調整の余地があるかもしれません。
dropAllItems()
の定義- スロット1から14までのアイテムをチェストに格納します。
- dropなのに、上方向に落とすとはこれいかにw
seichiOneLine(current_w)
関数には前バージョンからかなり手を加えています。- 位置情報を内部で取り扱うために、引数として
current_w
を必要とする - 現在の奥行きを処理するために、
current_d
という変数を内部で取り扱っている - 前回は手抜きをしましたが、できるだけわかりやすくするために、割り算の商と余りを明示的に変数で取り扱っている
seichiThreeStep()
の実行直後に、松明・チェスト関係の処理を挿入
- 位置情報を内部で取り扱うために、引数として
おわりに
さすがに200行超えのプログラムを解説するのは大変なので、概略の説明となりました。
説明不足な箇所も多いので、質問等あれば気軽にどうぞ。
さてこれで、奥行き/幅/高さを指定して、その範囲を整地(切土)するプログラムは完成しました。
次回は、このプログラムの発展形として、高さを指定しなくても自動的に高さを判別して切土するプログラムを検討します。
お楽しみに