Pythonで遊ぶminecraft -構造物をcsv化-

#はじめに
　最近pythonからminecraftを動かせるライブラリmcpiを使って遊んでいます。
　今回はmcpiを使って海底神殿や村などの自然生成される構造物の設計図を作ります。具体的には構造物が構成するブロックの位置と種類をマインクラフト内から読み取って、csvに保存します。このcsvを使って、好きな場所に構造物を複製できるようになります。
![2022-02-18_17.02.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/3f7a3145-b2f3-89f6-00d0-a15f8aec9cc7.png)

#1. 実行環境
・Windows 11
・minecraft java Edition 1.12.2
・minecraft Forge 14.23.5.2855
・Raspberry Jam Mod　0.92
・Python 3.9.10
・mcpi 1.12.1

#2. 建造物探し
　まずはcsv化したい構造物をマインクラフト内で探します。今回は海底神殿にします。マインクラフト内で海底神殿を探すのは、ゲームモードをクリエイティブにしてコマンド```\locate Monument```を打てば座標が分かります。
![2022-02-18_17.16.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/7a4a14d9-e975-71ae-21de-360c06c0381d.png)
このワールドではx=696,z=152にあるようです。コマンド```\tp 696 100 152```で海底神殿までワープしましょう。
![2022-02-18_17.20.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/2b39f624-ebed-dbd8-441c-e75621bfc9e2.png)
立派な海底神殿がありました。

#3. mcpiの導入
mcpiはpipコマンドでインストールできます。

```Python:
pip install mcpi
```

これをプログラム内でインポートして初期化します。

```Python:
from mcpi.minecraft import minecraft

mc = minecraft.Minecraft.create()
```

#4. ブロックデータの取得　getBlockWithData
　構造物を構成するブロックのデータ取得します。ブロックデータの取得には```getBlockWithData(x, y, z)```を使います。これで座標(x,y,z)のブロックidとブロックDataが取得できます。例えば座標(0,0,0)のブロックが石だった場合Pythonで実行すると以下のようになります。

```Python:test.py
block = mc.getBlockWithData(0, 0, 0)
print(f"ID = {block.id}")
print(f"Data = {block.data}")

# ID = 1
# Data = 0
```

ブロックid,dataは[こちら](https://www.minecraftinfo.com/idlist.htm)にまとまっています。

#5. 構造物全体のブロックデータを取得
　指定の座標のブロックデータを取得できるようになったので、構造物のブロックデータを取得していきます。構造物がある範囲のブロックデータを順番に取得していき、座標とブロックid,dataを記録します。構造物がある範囲は、構造物の角の座標とx,y,z方向それぞれの大きさで指定します。この直方体の範囲の座標を一か所ずつ```getBlockWithData```でブロックデータを取得します。
![2022-02-18_18.39.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/277d6e33-1f38-8789-e569-e3415914cd6b.png)
![2022-02-18_18.39.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552590/ea6f0613-fbe0-4bff-1718-9212bcf2a40e.png)


プログラムは以下のようになります。

```Python:minecraft_get_building.py
def get_building_data(x, y, z, x_range, y_range, z_range):
    """
    建造物をcsvデータとして出力する
    :param x: 建造物の角 x座標の最小値
    :param y: 建造物の角 y座標の最小値
    :param z: 建造物の角 z座標の最小値
    :param x_range: 建造物の大きさ x方向
    :param y_range: 建造物の大きさ y方向
    :param z_range: 建造物の大きさ z方向
    :return: out 座標とブロックデータのリスト[[x, y, z, id, data]]
    """
    out = [["x", "y", "z", "id", "data"]]
    for y_ in range(y_range):
        for x_ in range(x_range):
            for z_ in range(z_range):
                x_pos = x + x_
                y_pos = y + y_
                z_pos = z + z_
                block = mc.getBlockWithData(x_pos, y_pos, z_pos)
                out.append([x_, y_, z_, block.id, block.data])
    
    return out
```

#6. 不要ブロックの除去
　直方体の範囲を指定してブロックデータを取得すると不要なブロックも記録してしまいます。海底神殿の場合は水や昆布、砂利などです。これらのデータは記録しないように、指定したブロックデータのみ取得する、もしくは指定したブロックデータを取得しないようにします。
　指定したブロックのみを取得するには以下のようにします。海底神殿の場合は海洋ブロックシリーズ、スポンジ、金ブロックになります。

```Python:minecraft_get_block.py
# get_blocks = [[168, 0], [168, 1], [168, 2], [19, 1], [41, 0]]
# 168,0:海洋ブロック 168,1:海洋レンガ, 168,2:シーランタン
# 19,1:濡れたスポンジ, 41,0:金ブロック

def judge_get_block(get_blocks, target_block):
    """
    取得するブロックかどうかの判定
    :param get_blocks: 取得するブロックの種類を限定する ブロックid,dataのリスト
    :param target_block: minecraft内で配置されているブロック
    :return: target_blockがget_blocksに含まれているならTrue、含まれていないならFalse
    """
    for get_block in get_blocks:
        if get_block[0] == target_block.id and get_block[1] == target_block.data:
            return True
    return False
```

　逆に指定したブロックを取得しないようにするには以下のようにします。海底神殿の場合は水、砂利、砂、石、鉱石です。

```Python:minecraft_get_block.py
# ignore_blocks = [[8, 0], [13, 0], [12, 0], [16, 1], [15, 0]]
# 8,0:水 13,0:砂利, 12,0:砂
# 16,0:石炭鉱石, 15,0:鉄鉱石

def judge_ignore_block(ignore_blocks, target_block):
    """
    取得しないブロックかどうかの判定
    :param ignore_blocks: 取得しないブロックの種類を指定する ブロックidのリスト
    :param target_block: minecraft内で配置されているブロックid
    :return: target_blockがignore_blocksに含まれているならFalse、含まれていないならTrue
    """
    for ignore_block in ignore_blocks:
        if ignore_block[0] == target_block.id and ignore_block[1] == target_block.data:
            return False
    return True
```

#7. プログラム完成
　6.の取得ブロックの選択とcsvの保存を、5.のプログラムに組み込んで完成です！

```Python:minecraft_get_block.py
def get_building_data(x, y, z, x_range, y_range, z_range, output_path, get_blocks=None, ignore_blocks=None):
    """
    建造物をcsvデータとして出力する
    :param x: 建造物の角 x座標の最小値
    :param y: 建造物の角 y座標の最小値
    :param z: 建造物の角 z座標の最小値
    :param x_range: 建造物の大きさ x方向
    :param y_range: 建造物の大きさ y方向
    :param z_range: 建造物の大きさ z方向
    :param output_path: csvの出力先
    :param get_blocks: 取得するブロックの種類を限定する ブロックidのリスト
    :param ignore_blocks: 取得しないブロックの種類を指定する ブロックidのリスト
    :return:
    """

    out = [["x", "y", "z", "id", "data"]]
    for y_ in range(y_range):
        for x_ in range(x_range):
            for z_ in range(z_range):
                #ブロックデータの取得
                x_pos = x + x_
                y_pos = y + y_
                z_pos = z + z_
                block = mc.getBlockWithData(x_pos, y_pos, z_pos)
                #取得するブロックかどうかの判定
                if get_blocks is not None:
                    if judge_get_block(get_blocks, block) is False:
                        continue
                if ignore_blocks is not None:
                    if judge_ignore_block(ignore_blocks, block) is True:
                        continue
                out.append([x_, y_, z_, block.id, block.data])

    with open(output_path, "w", newline='') as f:
        writer = csv.writer(f)
        writer.writerows(out)

```

　海底神殿を保存すると以下のようなcsvができます。

```Monument.csv
# 0,0,0は建造物の角

x,y,z,id,data
0,0,0,168,1
0,0,1,168,1
0,0,2,168,1
...
30,28,28,168,0
30,28,29,168,0
30,28,30,168,1
```

　これでマイクラ内の構造物の設計図をcsv化できました！
　このcsvを見ながらブロックを置いていけば、好きな場所に好きな構造物が建てられます。
また、このcsvを読み込んで構造物をコピーするプログラムもあり、別記事で紹介しようと思います。

#8. インストール
　モジュールをPyPiに登録しましたので以下でインストールができます。

```
pip install minecraft_get_building
```

 使い方については以下githubにあげてあります。よかったら使ってみてください。
https://github.com/konishi0125/minecraft_get_building

#9. さいごに
　今回はマインクラフト内の構造物をcsv化するプログラムを紹介しました。
　マインクラフトには海底神殿の他にも森の洋館やエンドシティなど、魅力的な建造物がたくさんあります。これらをcsv化して好きな場所、好きな数配置してマインクラフトを遊ぼうと思います。また、自分が作った建物もcsv化して、設計図として保存しておきます。