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

Minecraftとタートルと僕

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

採掘タートルで整地する: (3)確実に1歩前進/上昇する関数

ComputerCraft Lua Minecraft チュートリアル

はじめに

前回までに、砂/砂利を全て掘るのになぜ正面と真上で待機時間が違うのかというお話をし、確実に採掘プログラム(1行または、3行のコード)を紹介しました。

今回は、このコードを使って、以下の関数を検討しましょう。

  • 前にブロックがあろうとなかろうと確実に1歩前進する関数
  • 真上にブロックがあろうとなかろうと確実に1歩上昇する関数

この2つの関数を駆使することで整地プログラムを組み立てていきます。言わばこの2つの関数は整地プログラムの要(かなめ)となります。

確実に1歩上昇する関数 surelyUp()

まずは簡単な方から、以下にコードを示します。

function surelyDigUp()
  while turtle.digUp() do
    os.sleep(0.4)
  end
end

function surelyUp()
  surelyDigUp()
  return turtle.up()
end

たとえ真上のブロックがあろうとなかろうと、そして真上が石ブロックだろうが砂ブロックだろうが確実に掘る関数surelyDigUp()を定義し、その後にturtle.up()を実行することで確実に1歩上昇する関数を実現しています。

ポイントは、turtle.up()の返値をreturnしているところです。 例えば燃料切れ、たとえば真上が岩盤など、どうしても上昇できないときにはfalseと同時にエラーメッセージが返ってきます。

確実に1歩前進する関数surelyFwd()

前回の検討の中で、砂の下のブロックが1m未満であったら(1+α)メートル砂が落下することになるので待機時間が必要であると結論しましたが、

整地するにあたって、砂/砂利の真下が雪であったりチェストであったりすることはそうあることではありません。

ということで、待機時間は無しで!

・・・と言いたいところなのですが、次のような状況があり得るのですよね*1

目の前の石ブロックを掘ったところ、その真下は深い空洞で、しばらく砂が落ち続けるという状況です。

つまり、1+αメートルどころか、数メートル場合によっては数十メートルの深さの穴へと複数の砂ブロックが通り過ぎていくわけですね。

これはまずいです。穴の深さも状況次第ですし、砂や砂利がいくつ積み上がっているかも状況次第、そのため待機時間も何秒に設定すればよいのかわかりません。

またあるいは、目の前をブロック以外のエンティティが塞いでいる可能性もあります。

広い平坦な地形で目の前になんらかのMOB(ゾンビなど)いたとしても、タートルはそれを押しのけて進むことができます。

しかし、目の前が行き止まりの袋小路でMOBにぶつかると、それを押しのけて前進することはできません。

広い範囲を整地するには時間がかかるので、できるだけいろいろな状況に対応した柔軟で堅牢なプログラムを組みたいのでこのあたりも考慮しておきたいものです。

というわけで、これらを考慮に入れたのが以下の関数になります。

function surelyDig()
  while turtle.dig() do end
end

function surelyFwd()
  for i=1,4 do
    local status, err = turtle.forward()
    if status then
      return true  -- success!
    elseif err=="Out of fuel" then
      return status, err
    end

    surelyDig() -- face to a normal block or a sand(gravel) hill

    if turtle.detect() and not turtle.dig() then
      return false, "bedrock!" 
    end

    if turtle.forward() then return true end -- success!
 
    if turtle.attack() then
      -- face to monster-mob
      while turtle.attack() do end
    else
      -- face to sand-blocks which is dropping long time
      os.sleep(5) -- probably, adjustment is required
    end
  end
  return turtle.forward()
end

まずは、前方が石ブロックであろうと砂ブロックであろうと確実に掘るsurelyDig()関数を定義してから本題のsurelyFwd()関数を定義しています。

surelyFwd()関数の処理の流れは少しだけ複雑です。

なぜなら、前進に失敗したときにその理由がなぜなのかを確実に判断する方法がないからです。そのため、処理が行き当たりばったりのその場しのぎ(adhoc)になっています。だれかもっと良い方法教えてくれないかなー(チラッチラッ

以下、surelyFwd()の簡単な解説です。

まずは、turtle.forward()を試して前に進めるかどうかを試します。進むことができたらそれはこの関数の実行に成功したということなのでtrueを返しこの関数を終了します。また、燃料切れが理由で前進できないのならすぐにそのエラーを返して関数を終了しましょう。

それ以外の前進失敗ならば、前方になんらかの障害物があるということなので、まずはブロックだと仮定してsurelyDig()を実行します。

さらに、まだ目の前にブロックが存在しており(turtle.detect())、turtle.dig()を行っても失敗するということは、それは岩盤などの採掘不可のブロックということなのでその旨を返して関数を終了します。

そしてturtle.forward()を再チャレンジ。ここで前進に成功しするなら問題ないのですが、失敗したときには次の処理を続けます。

失敗した理由として、まず袋小路でMOBに出会った可能性があります。そして先ほどのsurelyDig()により真下の深い空洞に落下していく複数の砂ブロックという可能性もあります。

この判別は、turtle.attack()が成功するかどうかで行います。成功したらそれはMOBということなので、続けてattackし続けることでMOBを倒してしまいましょう。

失敗したらそれは落下し続けている複数の砂ブロックということになります。この対策は難しいので、とりあえず5秒ほど待機しましょう。特殊なワールドでない限り5秒も待てば落下はひと段落しているはずです。

surelyFwd()は、この一連の処理を1セットとして、4セット繰り返すことにしています。4セットも繰り返してもダメなときには、それはよっぽどのことでしょうから*2、関数の実行に失敗ということでfalseを返すようにしています。

最悪の場合は、20秒(5秒×4セット)待つことになるので、せっかちな人は繰り返し回数を2回くらいにしても問題ありません。

こうして、確実に1歩前進する関数surelyFwd()が完成しました。

ほとんどのケースでは問題なく成功しtrueを返してくれるはずですが、どうしても進めない時にはfalseを返します。

(上記の映像では、Vine動画の6秒という時間の制約のため、待機時間を3.5秒に変更しています)

おわりに

次回以降は、これら関数を使ってプログラムを組んでいきます。

とりあえず次回は、奥行き/幅/高さを入力してその直方体領域内を整地するプログラムを紹介します。

お楽しみに。

*1:元ネタはkssrさんこちらのツイートから

*2:たとえば袋小路で自動回復するMOBが邪魔をしているとか、タートルが攻撃できないプレイヤーがわざと邪魔をしているとか、あるいは200個以上!?の縦に積み上がった砂ブロックが延々と奈落に落ち続けているとか