YouTube とニコニコ動画で投稿中の動画シリーズで、動画に収まりきらなかったこぼれ話や詳細解説を掲載しています。
動画はこちらから
作成したプログラム
clock.lua
簡単なデジタル時計のプログラム。
pastebin get qcGCwqXc build.lua
でゲーム内からダウンロードできる。
コメント付き版
-- const
-- コマンドライン引数を取得
local args = {...}
-- 建築サイズを定義
local X_SIZE, Y_SIZE, Z_SIZE = 16, 4 * (args[1] or 1), 16
-- 使用ブロックを指定
local BLOCKS = {
corner = 'minecraft:polished_deepslate',
wall = 'minecraft:stone_bricks',
floorEdge= 'minecraft:smooth_stone',
floor = 'minecraft:smooth_stone_slab',
window = 'minecraft:glass_pane',
}
-- 窓ガラスの位置を指定
local WINDOWS_I = {2, 3, 5, 6, 9, 10, 12, 13}
-- function
-- テーブルがある値を含むか
table.includes = function (t, x)
for k, v in pairs(t) do
if v == x then
return true
end
end
return false
end
-- 壁に窓をつけるか
function isWindow(i)
-- i: 端を 1 として、端から何ブロック目か
return table.includes(WINDOWS_I, i)
end
-- 建築家位置からの相対座標から、設置すべきブロックを返す
function getBlock(x, y, z)
-- X軸・Z軸 に平行な壁面であるか
local isEdgeX, isEdgeZ = x == 0 or x == X_SIZE - 1, z == 0 or z == Z_SIZE - 1
-- X軸・Z軸 両方の壁面 ⇔ 四隅の柱
if isEdgeX and isEdgeZ then
return BLOCKS.corner
-- 四ブロックごとに床を張る
elseif y % 4 == 3 then
if isEdgeX or isEdgeZ then
return BLOCKS.floorEdge
else
return BLOCKS.floor
end
elseif isEdgeX or isEdgeZ then
local i -- 端から何ブロック目か
if isEdgeX then
i = z
else
i = x
end
if y % 4 == 1 and isWindow(i) then
return BLOCKS.window
else
return BLOCKS.wall
end
end
return nil
end
-- タートルが持っているアイテムを数える
function countItems()
local counts = {}
local detail
for slot = 1, 16 do
detail = turtle.getItemDetail(slot)
if detail ~= nil then
counts[detail.name] = (counts[detail.name] or 0) + detail.count
end
end
return counts
end
-- 必要な建材の量を数える
function countMaterials()
local counts = {}
for y = 0, Y_SIZE - 1 do
for z = 0, Z_SIZE - 1 do
for x = 0, X_SIZE - 1 do
block = getBlock(x, y, z)
if block ~= nil then
counts[block] = (counts[block] or 0) + 1
end
end
end
end
return counts
end
-- 総移動回数を求める
function countMove()
return (X_SIZE - 1) * 2 * Z_SIZE * Y_SIZE + (Z_SIZE - 1) * 2 * Y_SIZE + Y_SIZE
end
-- 建築を実行可能か確かめる
function canBuild()
local itemCounts = countItems()
local materials = countMaterials()
-- すべての建材が必要数用意されているかをチェックする
for name, needs in pairs(materials) do
local extra = (itemCounts[name] or 0) - needs
if extra < 0 then
-- 何か一つでも足りなかったら、false を返す
return false, ("%s need more %d."):format(name, -extra)
end
end
local moveCount = countMove()
local fuelLevel = turtle.getFuelLevel()
if fuelLevel ~= 'unlimited' and moveCount > fuelLevel then
-- 燃料が足りなければ、false を返す
return false, ("need more fuel. (+%d)"):format(moveCount - fuelLevel)
end
return true
end
-- あるアイテムを選択する
function selectItem(name)
for slot = 1, 16 do
detail = turtle.getItemDetail(slot)
if detail ~= nil then
if detail.name == name then
turtle.select(slot)
return true
end
end
end
return false
end
-- 建築する
function build()
for y = 0, Y_SIZE - 1 do
turtle.up()
for z = 0, Z_SIZE - 1 do
for x = 0, X_SIZE - 1 do
block = getBlock(x, y, z)
if block ~= nil then
selectItem(block)
turtle.placeDown()
end
if x ~= X_SIZE - 1 then
turtle.forward()
end
end
for i = 1, X_SIZE - 1 do
turtle.back()
end
if z ~= Z_SIZE - 1 then
turtle.turnRight()
turtle.forward()
turtle.turnLeft()
end
end
turtle.turnLeft()
for i = 1, Z_SIZE - 1 do
turtle.forward()
end
turtle.turnRight()
end
end
function main()
local isPossible, reason = canBuild()
-- 建築不可能なら理由を表示
if not isPossible then
print(reason)
return false
end
print("OK")
build()
return true
end
-- main
main()
Lua使い方
build [階数]
で実行する。[階数]
には 2 以下の非負整数、つまり 1 か 2 を入力する。(3 以上はタートルのインベントリが足らず、建築不可能である。getBlock(x, y, z)
を書き換えることで、伸ばせるかもしれない。)省略すると、一階建てになる。フールプルーフ1は実装していない。
詳細解説
動画に登場した要素を詳しく解説します。
用語集
動画内に出てきた専門用語やアイテムなどの解説です。これ本当に専門用語か…?
ずんだもち
宮城県を中心とする、東北地方の郷土料理。四国めたん氏は東北ずん子・ずんだもんプロジェクトのキャラクターであるため作成した。
タートル
移動やブロックの設置が出来るようになったコンピューター。というかロボット。製作者曰く、その名前は LEGO の教育用ロボットに由来するらしい。
安息の地――メッカ――
元ネタは東北3姉妹の準公式4コマ「ずんちゃんといっしょ!」の第121話。
コマンド
動画内で使用した、CC のコンピューターなどで実行可能なコマンドの解説です。
dance コマンド
タートルが踊る。それだけ。
refuel コマンド
タートルに燃料を補給する。かまど燃料1秒分で、1ブロック分移動できる。refuel all
でタートルのインベントリ内すべての、refuel [個数]
で指定個数分のアイテムを消費する。個数に0
と入力することで、チャージ済み燃料の量の確認のみを行える。
プログラム関連
{}
(テーブル)
配列とリストと連想配列とオブジェクトの性質を併せ持った型。添え字を書かずに生成すると配列のような振る舞いをし、明示的に添え字を与えると連想配列のような振る舞いをする。値へのアクセスは、[]
(各括弧)を使った一般的な記法のほかに、JavaScript のように添え字が文字列2のものに関しては.
(ドット)でアクセスできる。
...
(可変長引数)
...
は可変長引数式を表し、今回の場合は{...}
と記載することで、コマンドライン引数を順に要素に持つテーブルを生成している。単にコマンドライン引数にアクセスするには、グローバル変数arg
も使用可能である。
分割代入
細かく説明できないが、カンマで区切ることで一行のうちに複数の代入処理を書ける。
~=
(不等価演算子)
二つのオペランドが等しくないとき、true
を返す演算子。ほかの言語では!=
で表現されることも多い。
for
文
繰り返し変数の値を増減値分ずつ変化させながら、do
~end
を繰り返す。増減値を省略すると、1ずつ変化する。
for 繰り返し変数 = 初期値, 終了値, 増減値 do
繰り返す内容
end
for 繰り返し変数 = 初期値, 終了値 do
繰り返す内容
end
ほかの一般的な言語に翻訳すると、以下のようなイメージになる。
for (繰り返し変数 = 初期値; 繰り返し変数 <= 終了値; 繰り返し変数 += 増減値) {
繰り返す内容
}
for (繰り返し変数 = 初期値; 繰り返し変数 <= 終了値; 繰り返し変数++) {
繰り返す内容
}
また、テーブルの要素を順に処理するには、for ... in
文が有効である。
for 添え字, 値 in pairs(テーブル) do
繰り返す内容
end
turtle.select(slot)
タートルの選択スロットをslot
に変更する。左上が1番、その右が2番で、右下が16番。
turtle.getItemDetail([slot[, detailed]])
指定スロット(slot
、省略時は選択中のスロット)のアイテムに関する情報を取得する。空の際はnil
を、アイテムが取得できた際はtable
を返す。
turtle.getFuelLevel()
現在チャージ済みの燃料の量を返す。
turtle.placeDown(text)
真下に選択中のスロットのブロックを設置する。一部右クリックで使用するアイテムもこれで使用する。正面や真上版のturtle.place()
やturtle.placeUp()
も存在する。看板などを設置する際には、引数text
の文字列が使用されるらしい(未検証)。
turtle.forward()
/turtle.back()
/turtle.up()
/turtle.down()
それぞれの方向にタートルが移動する。障害物があると失敗する。
turtle.turnLeft()
/turtle.turlRight()
それぞれの方向にタートルが回転する。
編集後記
このあたりから制作プログラムの規模が肥大化してきた気がする。あと、今思うとtable.includes()
を用意するより、{[2] = true, [3] = true, ...}
の方が賢い気がする。