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

Minecraftとタートルと僕

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

採掘タートルで整地する: (5)整地スピードを約1.5倍にする工夫

はじめに

前回の記事で、奥行き(Depth)/幅(Width)/高さ(Height)を指定して、その範囲内を整地するプログラムを紹介しました。

以下のように引数を与えてプログラムを実行すると、奥行き16、幅4(タートル設置地点より右側へ)、高さ10(設置地点より上へ)の範囲を綺麗に整地してくれます。

> seichi 16 4 10

今回の改造・・・効率化

一通り問題なく動くのですが、整地スピードはそれほど速くありません。

基本的に、正面掘って正面移動・真上を掘って真上移動・真下を掘って真下移動のように、1回移動するたびに1回掘っているだけなので仕方ありません。

今回はこの点に手を加えてみましょう。

基本的なアイデア

タートルのdig系の関数は、turtle.dig()turtle.digUp()turtle.digDown()の3種類あるので、タートルが移動するときに移動方向だけでなく、他の方向も同時に掘れば、効率が上がりますよね。

たとえば、前回採用したアルゴリズムを再考してみましょう。1セットで2山のブロックを採掘する関数です。

  • seichiTwoStep()
    • 真上を掘って、真上移動
    • 指定された高さまで来たら、正面掘って正面移動
    • 真下掘って、真下移動
    • 最初の高さまで来たら1セット終了

この流れの中で1回移動するたびに二方向を採掘しましょう。

たとえば真上移動のときに、真上と同時に正面も掘ることで2つの山を掘ることができます。

また真下移動のときにも真下と同時に正面も掘ることでさらに2つの山を掘ることができます。

真上→真下という往復で計4つの山を掘ることができるなんてすばらしい。

それでは早速、1セットで4つの山を掘ることができるseichiFourStep()関数を作りましょう。seichiTwoStep()に比べれば、効率2倍! みなぎってきたー!

・・・といいたいところですが、ちょっと待ちましょう。

基本的なアイデアとしてはこれで問題ないのですが、場合によっては、おかしなことになる可能性があります。

真上移動時に正面と真上を掘ると・・・

整地範囲内に砂や砂利が混じっているとき、真上移動時に正面を掘ると問題が生じる可能性があります。

まず、下の図のような状況を考えて見ましょう。ここからタートルは、正面掘って、真上掘って、1歩上に移動します。

f:id:hevohevo:20140720215236j:plain:w300

つまり、次の図のようになります。それではさらに、正面掘って、真上掘って、1歩上に移動したらどうなるでしょうか。

f:id:hevohevo:20140720215239j:plain:w300

あらら・・・。下の図のようになってしまうのですね。

f:id:hevohevo:20140720215242j:plain:w300

正面の土を掘ると、その上にあった砂が落下してしまうので、正面を綺麗に整地するためにはもう一度降りてきて、下に落ちた砂を採掘しないといけないのです。これは二度手間ですね・・・。

つまり、真上方向に移動するときに正面と真上の2つの山を同時に掘るのは問題ありなのです。

逆に一番上まで上昇したあと、真下に下りてくるときに2つの山を掘るのは問題ありません。

つまり、上下の往復で3つの山を掘る!

上下の往復で3つの山を掘ることができる次のような関数を作ります。

  • seichiThreeStep()
    • 真上を掘って真上移動
    • 指定された高さまで来たら、正面掘って正面移動
    • 真下掘って、さらに正面も掘って、真下移動
    • 最初の高さまで来たら、1歩前進して、1セット終了

f:id:hevohevo:20140720221639j:plain:w400

前回のプログラムでは、seichiOneLine()が指定された奥行きまでの一列を整地する関数でした。

seichiTwoStep()関数をできるだけ使っていき、指定された奥行きが奇数のとき、seichiOneStep()関数を最後に1回だけ使うことで、ちょうど一列を整地していました。

今回のseichiThreeStep()関数の導入により、seichiOneLine()の挙動は以下のようになります。

  • seichiOneLine()  【New!】
    • できるだけseichiThreeStep()を使って整地していく
      • depth/3 をしたときの商を求め、その回数だけ繰り返し。
    • depth / 3を計算したときの余りが2のときには最後に、seichiTwoStep()実行
    • depth / 3を計算したときの余りが1のときには最後に、seichiOneStep()実行

ソースコード

http://pastebin.com/0398fapc

簡単な解説

前回のversion0.1から変更した箇所は、以下の2点だけです。

  • 関数seichiThreeStep()追加
  • 関数seichiOneLine()を、seichiThreeStep()を使用するように変更

関数seichiThreeStep()追加

function seichiThreeStep()
  for i=1,HEIGHT-1 do
    surelyUp()
  end
  surelyFwd()  
  for i=1,HEIGHT-1 do
    surelyDig()
    turtle.digDown()
    turtle.down()
  end
  surelyFwd()
end

指定された高さまで上ったあと、降りてくるときに真下と同時に正面を掘っています。

正面を掘るために、正面が砂山であることを考慮してsurelyDig()を使っています。

また、最初の高さまで降りたら、1歩前進surelyFwd()することを忘れずに。

関数seichiOneLine()を修正

function seichiOneLine(d)
  local d = d or DEPTH
  for i=1, d/3 do
    seichiThreeStep()
    if i<(d/3) then surelyFwd() end
  end
  if d%3==1 then
    seichiOneStep()
  elseif d%3==2 then
    seichiTwoStep()
  end
end
  • local d = d or DEPTHは、変数の初期値(default値)を指定する慣用表現です。もしこの関数seichiOneLine()が引数(d)無しで実行されたら、変数dは定数DEPTH(プログラム実行時に指定された奥行き)を使います。
  • d/3によって奥行きを3で割った値を求めることができます(例:奥行き10なら、10/3=3.33333・・・)。forループ文では、カウンターiの値は1ずつ増えるのでそのままこの値を使ってseichiThreeStep()を繰り返しています。
  • if i<(d/3) then surelyFwd() endは、1セットのseichiThreeStep()終了後の地点から次回のseichiThreeStep()実行開始地点までの移動です。当然ながらforループ文の最後の繰り返しのときには必要ないので行わないようにしています。
  • 奥行きを3で割ったときの余りd%3によって、seichiOneStep()seichiTwoStep()を使い分けています。
  • なお、厳密に商(整数)と余りが欲しいときには、次のように求めます。
    • syou = math.floor(d/3)
    • amari = d%3
  • だから正確に商と余りをつかって関数を作るならば以下のようになります(手抜きって言わないで><)
function seichiOneLine(d)
  local d = d or DEPTH
  local syou = math.floor(d/3)
  local amari = d%3
  for i=1, syou do
    seichiThreeStep()
    if i<syou then surelyFwd() end
  end
  if amari==1 then
    seichiOneStep()
  elseif amari==2 then
    seichiTwoStep()
  end
end

おわりに

これまでseichiTwoStep()関数を使っていたところをseichiThreeStep()関数を使うようになったので、原理的にはほぼ、1.5倍のスピードアップになります。

次回は、さらなる機能追加に努めましょう。

たとえば、広い範囲を整地していると、採掘したブロックがもてなくなり溢れてしまいますよね。その対策のために、チェストを設置してそこに入れるようにしましょう。

また、広い範囲の整地をするとどうしてもMOBが湧いてしまいます。湧き防止のために松明を設置する機能も付け加えましょう。

それでは次回をお楽しみに。