マインクラフトで表現する組み合わせ最適化問題 featuring K3Tunnel Part1

# はじめに
　[NSSOL Adentcallendar 2023](https://qiita.com/advent-calendar/2023/nssol)では、[K3Tunnel](https://k3tunnel.com/)で公開されているプログラムサンプルの中から、[ネズミ算](https://k3tunnel.com/k3t/sampleWork.html?sampleId=A0001)を題材としてマインクラフト上での表現に挑戦しました。
 　今回は、ミッションの一つとして公開されている「[家電買いかえ大作戦](https://k3tunnel.com/mission/001/)」を題材として、マインクラフト上での表現とMakeCodeを用いたプログラミングを試みてみようと思います。

# K3Tunnelのミッション：家電買い替え大作戦
　K3Tunnelのサイトで公開されている家電買いかえ大作戦の概要は以下の通りです。最適化問題の分類でいうと「ナップザック問題」に分類されそうです。

 - たかしくんのお家で、家電を買いかえることになった
 - 買いかえ対象の家電カテゴリは、「そうじ機」「クッション」「ホームベーカリー」の3種類
 - 家電カテゴリ毎に、買いかえ候補となる製品が4種類ある
 - 家電カテゴリ毎に、1つの製品を必ず買う
 - 製品ごとに、値段とたかしくん、おとうさん、おかあさんの満足ポイントが設定されている
 - 決められた予算（10万円）に収まる範囲で、たかしくん、おとうさん、おかあさんの満足ポイントの合計が最も高くなるように、製品を選ぶ

![家電買いかえ大作戦価格満足ポイント一覧表](https://k3tunnel.com/mission/001/img/mission01_list.png)
※K3Tunnelサイトより転載

# マインクラフト上での表現への変換
　前節で紹介した家電買いかえ大作戦は、そのままではマインクラフト上で表現することが難しいため、マインクラフトで表現できる形に変換します。標準的なマイクラの世界に家電はないため、買いかえの対象となる各種家電を別のアイテムに置き換えます。

　今回は、以下のように、家電を装備品に置き換えることにします。家電買いかえ大作戦改め「装備買いかえ大作戦」です。
- そうじ機は「ヘルメット（頭防具）」
- クッションは「チェストプレート（体防具）」
- ホームベーカリーは「レギンズ（脚防具）」

候補製品まで含めた対応関係は以下のように設定します。装備品毎の価格や満足ポイントは、家電買いかえ大作戦の設定値をそのまま使います。

|家電買いかえ大作戦|装備買いかえ大作戦|
|:--------------:|:---------------:|
|ロボット型そうじ機|ダイヤモンドのヘルメット|
|高性能そうじ機|金のヘルメット|
|軽くてかっこういいそうじ機|鉄のヘルメット|
|旧型そうじ機|革の帽子|
|マッサージ機能付クッション|ダイヤモンドのチェストプレート|
|大きいくまさんクッション|金のチェストプレート|
|マイクロビーズクッション|鉄のチェストプレート|
|普通のシンプルクッション|胸当て|
|本格派具材自動投入HB|ネザライトのレギンズ|
|お手軽派HB ピザやチーズも|ダイヤモンドのレギンズ|
|ご飯でパンが焼けるHB|金のレギンズ|
|食パン焼くだけHB|革のズボン|

　プレイヤーは、10万円の予算に収まる範囲で、たかしくん、おとうさん、おかあさんの満足ポイントを最大化するような装備品の組み合わせを選ぶことになります。

# デモ動画
　設計や実装の内容を説明する前に、デモ動画でどんなものが出来上がるのかを紹介します。プレイヤーは、チェストから装備を選び、防具立てに装着させてボタンを押します。すると、計算用のレッドストーン回路が作動し、満足ポイントや合計金額が画面右側のサイドバーに表示されます。防具立てに装着する装備の組み合わせを変えてボタンを押しなおすと、変更した組み合わせに応じた計算結果でサイドバーが更新されるようになっています。

<iframe width="560" height="315" src="https://www.youtube.com/embed/vjbatUhfBhA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


# マインクラフト上での実装
　ここからは、前節のデモ動画でお見せした仕組みをどのように実装したのかを説明していきます。今回は、選択した装備品の組み合わせに対する満足度ポイントや合計金額を計算するために、スコアボードの機能を使っています。スコアボードを使うと、得点項目（objective）を定義して、マイクラの世界の中のエンティティ（プレイヤーやMOB等）毎に、スコアを付けることができます。また、その結果をサイドバーとして表示することも可能です。

![装備買いかえ大作戦_サイドバー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/07f0a9cb-3dcc-78b5-b967-6738413d9772.jpeg)
 
　装備品の組み合わせに対して、①たかしくんの満足ポイント、②お父さんの満足ポイント、③おかあさんの満足ポイント、④家族全体の満足ポイント、⑤合計金額の5つのスコアをサイドバー表示できるようにしようと思います。

### スコアボード全体のデータ構造
　スコアボードに持たせたい情報を表に表すと以下のようになります。

![装備買いかえ大作戦_スコアボード.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/de3b05ac-8181-154b-ac92-2a1c33bf2419.jpeg)


　表の①の領域は、サイドバーに表示する項目です。選んだ装備品の組み合わせに対して、たかしくん、おとうさん、おかあさん、家族全員の満足ポイントと、合計金額を計算し格納します。合計金額を満足ポイントの行に入れるのに違和感がありますが、サイドバーの仕様上、同時に表示可能なのは一つの項目（表の中のどれか一つの行）のみとなるため、満足ポイントの行に含めてしまいます。
　②の領域は、装備品それぞれに対する満足ポイントです。後々の計算を楽にするため、たかしくん、おとうさん、おかあさんの満足ポイントの他、3人分の満足ポイントを合算した家族全員の満足ポイントも格納しておきます。
　③の領域は、装備品の値段です。この表をスコアボードに実装し、装備品の選択に応じた計算結果を得られるように仕掛けを作ります。
 
### 装備毎の価格や満足ポイント等の設定
　表の縦軸に相当する項目は、`scoreboard`コマンドを使って、`objectives`として定義します。

```
/scoreboard objectives add <objective_name> dummy <description>
```
`object_name`は、マイクラの中のコマンドで参照するときの名称、`description`は、画面上に表示するときの表示名になります。今回、定義したい12種類の装備品を設定するコマンド群は[付録1](https://qiita.com/yamamorisoba/items/24728c2554835b0b4e3f#%E4%BB%98%E9%8C%B21-%E3%82%B9%E3%82%B3%E3%82%A2%E3%83%9C%E3%83%BC%E3%83%89%E4%B8%8A%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%AE%9A%E7%BE%A9%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E4%BE%8B)に収録します。定義済みの`objectives`の情報を確認したい場合には、以下のコマンドを実行します。

 ```
 /scoreboard objectives list
 ```

![装備買いかえ大作戦_objectives_list.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/9acde4d5-0059-9f04-daed-e1435748249e.jpeg)

 
　表の横軸の項目を設定するには、ダミープレイヤーを用います。今回定義が必要なダミープレイヤーは、満足ポイントを保持するための「たかしくん」「おとうさん」「おかあさん」「家族全体」と、価格を保持するための「合計金額」の5つです。以下のコマンドを用いることで、`ダミープレイヤー`の`objective`項目に対して、`value`で指定する値を設定することができます。今回実行するコマンド群は、[付録１](https://qiita.com/yamamorisoba/items/24728c2554835b0b4e3f#%E4%BB%98%E9%8C%B21-%E3%82%B9%E3%82%B3%E3%82%A2%E3%83%9C%E3%83%BC%E3%83%89%E4%B8%8A%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%AE%9A%E7%BE%A9%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E4%BE%8B)に収録します。

```
/scoreboard players set <ダミープレイヤー> <objective> <value>
```
　これで、あらかじめ設定しておきたいパラメーターを、マインクラフトの中に設定することができます。

### 装備品の選択方法
　次のステップとして、装備品の組み合わせを選ぶ仕組みについて考えます。今回は頭防具、体防具、脚防具の各カテゴリに対して4種類の候補があります。各カテゴリの候補から1種類ずつ選んでもらう仕組みとして、防具立てを使います。プレイヤーが選んだ頭防具、体防具、脚防具を防具立てに装着させることで、組み合わせを確定させます。

 ![装備買いかえ大作戦_装備立て.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/6ebf9ffa-6c8c-23bb-24a7-811f741d482f.jpeg)

　組み合わせの選択方法として、防具立てを用いるのにはいくつか理由があります。

1. 重複排除
チェストなどをカート代わりとして選択した装備品を収納する方法を用いると、同じカテゴリから複数の装備品を収納してしまう重複ケースへの対処が必要になるが、防具立ての場合は、カテゴリ当たり必ず1つの装備になることが保証されるため、重複ケースへの対処が不要
1. 見た目
現在選んでいる装備が何なのか、外見から確認できる。着せ替え感覚で選択する装備を取り換えることができる。

　防具立てを設置するコマンドは以下のようになります。
 ```
 /summon armor_stand <x> <y> <z> <name>
 ```
　`x`、`y`、`z`は設置先の座標、`name`は設置した防具立てにつける名称です。わざわざ名称付きにしているのは、プレイヤーが別の防具立てをどこかに設置してしまった場合でも、判定対象を一つに特定することができるようにするためです。
　プレイヤーが選択した（防具立てに装着させた）装備がどれなのかを判定するには、以下のコマンドを用います。

 ```
/testfor @e[type=armor_stand,name=cart,hasitem={item=<item_id>}]
 ```
　このコマンドは、`item_id`で指定した装備を防具立てが装着しているときに真となります。今回は選択肢となる装備品が12種類と限られているので、すべての候補に対してこのコマンドを実行することで、選択された（コマンド実行結果が真となる）装備を判定することができます。

### 価格と満足ポイントの計算方法
　選択された装備品が特定できたら、装備品に応じた満足度ポイント・価格を合算し、スコアボードの`satisfaction`の項目に格納します。コマンドとしては以下のようになります。

```
/scoreboard players operation <dummy_player> satisfaction += <dummy_player> <item_id>
```

+=の左辺の値（dummy_playerの満足ポイント）に、右辺の値 （item_idの装備品に対するdummy_playerの満足ポイント）を加算する操作です。具体例として、金のヘルメットとダイヤモンドのチェストプレート、ネザライトのレギンズを選択した場合の、たかしくんの満足ポイントを計算するコマンドを書き下すと、以下のようになります。

```
/scoreboard players operation たかしくん satisfaction += たかしくん golden_helmet
/scoreboard players operation たかしくん satisfaction += たかしくん diamond_chestplate
/scoreboard players operation たかしくん satisfaction += たかしくん netherite_leggings
```

　組み合わせを変えて満足ポイントや合計金額を再計算する場合には、満足ポイントのリセットが必要です。満足ポイントをリセットするには以下のコマンドを用います。

 ```
/scoreboard players reset <dummy_player> satisfaction
 ```

### コマンドブロックでの実装
　計算に必要なコマンド群が整理できたので、マインクラフトの世界でこれらのコマンドが適切に実行されるように、コマンドブロックを設置していきます。コマンドブロックは以下のコマンドを実行して入手します。

 ```
/give @p command_block
 ```

　デモ動画の中で採用されているコマンドブロックの配置は以下の図のようになっています。

![スクリーンショット (56).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/a5679692-1f35-ce88-db1f-4bdd4b470121.png)


　それぞれのブロックがどのような役割を果たしているかを、4つの区画に分けて説明します。

![スクリーンショット 2024-03-07 223708.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/51c91936-aad3-b2fd-46e7-c499fed49a3a.png)

#### 区画①：スコアの初期化
過去に計算した満足ポイントなどのスコアをリセットする処理を行うブロックを並べています。
一番右側は、処理の起点となるブロックで「インパルス」「無条件」「動力が必要」で作動します。スイッチやレッドストーン回路などでこのブロックに動力が伝わると一連の処理が始まります。このブロックには、処理の開始をメッセージとして表示するコマンドとして、以下が設定されています。

```
/say "満足ポイントと合計金額を計算します"
```
このブロックから左側に続いている5つのコマンドブロックは、「鎖」「条件付き」「常時実行」という動作条件となっており、ひとつ前のブロックの処理が成功した場合に作動するつくりです。各ブロックで、たかしくんの満足ポイント、おとうさんの満足ポイント、おかあさんの満足ポイント、家族全員の満足ポイント、合計金額をリセットする処理を行っています。

　②以降の区画の処理は、区画①での初期化処理が完了した後に行われるように制御する必要があります。単純にレッドストーン回路を区画①の最後の処理を行うブロックからのばした場合、区画①のブロックの処理の完了を待たずに動力が伝わってしまいます。（コマンドブロック間の動力伝達は、コマンドの完了をまってくれないようです。）そのため、楽観的な処理ではありますが、リピーターをはさんで動力の伝達を遅延させることで、区画①の処理完了を待つ形にして、②以降の区画に動力を伝えるつくりにしています。

#### ②～④ スコア計算 

区画②～④は、②頭防具、③体防具、④脚防具に関するスコア計算を担っています。各区画にはコマンドブロックの列が4本あります。これは、それぞれの装備カテゴリで候補となっている4種類の装備品に対応しています。それぞれの列が、特定の装備品が選ばれた場合の処理を担っています。列中に並んでいるコマンドブロックは、上から順に以下の処理を実行します。
1. 防具立てに装着された装備の判定
1. 判定結果が真だった場合のメッセージ出力
※該当判定が偽だった場合には、以降の処理は行われない
1. たかしくんの満足ポイント計算
1. おとうさんの満足ポイント計算
1. おかあさんの満足ポイント計算
1. 家族全体の満足ポイント計算
1. 合計金額計算

　各コマンドブロックの具体的な設定内容は[付録2](https://qiita.com/yamamorisoba/items/24728c2554835b0b4e3f#%E4%BB%98%E9%8C%B22-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E8%A8%AD%E5%AE%9A)に収録します。最後は、防具屋の建物内に回路を起動するためのボタンを設置し、ボタンを設置したブロックから、回路の起点となるコマンドブロックまでを、レッドストーン回路で結びます。こうすることで、防具屋の店内から回路を起動し、満足ポイントと合計金額することができるようになります。

# まとめ
　K3Tunnelの「家電買いかえ大作戦」を「装備買いかえ大作戦」に変換して、マインクラフト上に再現しました。選択する装備の組み合わせに応じて、たかしくん、おとうさん、おかあさん、家族全体の満足ポイントと、合計金額を計算する回路を実装し、デモ動画にまとめました。Part2では、K3Tunnelに掲載されているプログラム例をベースに、Education EditionのMakeCodeをつかって、10万円の予算の中で家族全体の満足度が最も高くなる組み合わせを求めるプログラムを作成します。プログラムの実行結果を、今回のデモワールドの装備立てに装着させ、期待した通りの結果が得られるかを確かめてみます。

# 付録1 スコアボード上のデータ定義コマンド例
　今回使用した、満足ポイントや価格のデータを格納するスコアボードを設定するコマンド群を紹介します。

### 各種装備品などの項目（objectives）を設定するコマンド群

```
/scoreboard objectives add satisfaction dummy "満足ポイント"

/scoreboard objectives add diamond_helmet dummy "ダイヤモンドのヘルメット"
/scoreboard objectives add golden_helmet dummy "金のヘルメット"
/scoreboard objectives add iron_helmet dummy "鉄のヘルメット"
/scoreboard objectives add leather_helmet dummy "革の帽子"

/scoreboard objectives add diamond_chestplate dummy "ダイヤモンドのチェストプレート"
/scoreboard objectives add golden_chestplate dummy "金のチェストプレート"
/scoreboard objectives add iron_chestplate dummy "鉄のチェストプレート"
/scoreboard objectives add chainmail_chestplate dummy "胸当て"

/scoreboard objectives add netherite_leggings dummy "ネザライトのレギンズ"
/scoreboard objectives add diamond_leggings dummy "ダイヤモンドのレギンズ"
/scoreboard objectives add golden_leggings dummy "金のレギンズ"
/scoreboard objectives add leather_leggings dummy "革のズボン"
```
### 満足ポイントや価格を設定するコマンド群


```
#たかしくん満足ポイント設定
/scoreboard players set たかしくん diamond_helmet 100
/scoreboard players set たかしくん golden_helmet 100
/scoreboard players set たかしくん iron_helmet 50
/scoreboard players set たかしくん leather_helmet 30

/scoreboard players set たかしくん diamond_chestplate 50
/scoreboard players set たかしくん golden_chestplate 100
/scoreboard players set たかしくん iron_chestplate 70
/scoreboard players set たかしくん chainmail_chestplate 50

/scoreboard players set たかしくん netherite_leggings 100
/scoreboard players set たかしくん diamond_leggings 70
/scoreboard players set たかしくん golden_leggings 60
/scoreboard players set たかしくん leather_leggings 50

#おとうさん満足ポイント設定
/scoreboard players set おとうさん diamond_helmet 100
/scoreboard players set おとうさん golden_helmet 80
/scoreboard players set おとうさん iron_helmet 60
/scoreboard players set おとうさん leather_helmet 50

/scoreboard players set おとうさん diamond_chestplate 100
/scoreboard players set おとうさん golden_chestplate 50
/scoreboard players set おとうさん iron_chestplate 70
/scoreboard players set おとうさん chainmail_chestplate 70

/scoreboard players set おとうさん netherite_leggings 70
/scoreboard players set おとうさん diamond_leggings 70
/scoreboard players set おとうさん golden_leggings 50
/scoreboard players set おとうさん leather_leggings 100

#おかあさん満足ポイント設定
/scoreboard players set おかあさん diamond_helmet 70
/scoreboard players set おかあさん golden_helmet 90
/scoreboard players set おかあさん iron_helmet 90
/scoreboard players set おかあさん leather_helmet 50

/scoreboard players set おかあさん diamond_chestplate 60
/scoreboard players set おかあさん golden_chestplate 80
/scoreboard players set おかあさん iron_chestplate 80
/scoreboard players set おかあさん chainmail_chestplate 50

/scoreboard players set おかあさん netherite_leggings 50
/scoreboard players set おかあさん diamond_leggings 80
/scoreboard players set おかあさん golden_leggings 70
/scoreboard players set おかあさん leather_leggings 60

#家族全体満足ポイント設定
/scoreboard players set 家族全体 diamond_helmet 270
/scoreboard players set 家族全体 golden_helmet 270
/scoreboard players set 家族全体 iron_helmet 200
/scoreboard players set 家族全体 leather_helmet 130

/scoreboard players set 家族全体 diamond_chestplate 210
/scoreboard players set 家族全体 golden_chestplate 230
/scoreboard players set 家族全体 iron_chestplate 220
/scoreboard players set 家族全体 chainmail_chestplate 170

/scoreboard players set 家族全体 netherite_leggings 220
/scoreboard players set 家族全体 diamond_leggings 220
/scoreboard players set 家族全体 golden_leggings 180
/scoreboard players set 家族全体 leather_leggings 210

#合計金額値段設定
/scoreboard players set 合計金額 diamond_helmet 80000
/scoreboard players set 合計金額 golden_helmet 60000
/scoreboard players set 合計金額 iron_helmet 30000
/scoreboard players set 合計金額 leather_helmet 20000

/scoreboard players set 合計金額 diamond_chestplate 50000
/scoreboard players set 合計金額 golden_chestplate 30000
/scoreboard players set 合計金額 iron_chestplate 10000
/scoreboard players set 合計金額 chainmail_chestplate 5000

/scoreboard players set 合計金額 netherite_leggings 40000
/scoreboard players set 合計金額 diamond_leggings 30000
/scoreboard players set 合計金額 golden_leggings 20000
/scoreboard players set 合計金額 leather_leggings 5000
 ```

# 付録2 コマンドブロックの設定
　満足ポイントと合計金額を計算するレッドストーン回路に組み込むコマンドブロックの設定を記載します。回路全体で90個のコマンドブロックを使っています。表中の№列は、図中の番号に対応しています。表に記載している項目以外のコマンドブロックの設定値は初期値のままです。

![スクリーンショット 2024-03-09 215844.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/d53029d0-8392-8610-d76b-8d36d66f44bc.png)


|№|種類|条件|レッドストーン|コマンド|
|:-:|:-:|:-:|:-:|:-:|
|1|インパルス|無条件|動力が必要|/say "満足ポイントと合計金額を計算します"|
|2|鎖|条件付き|常時実行|/scoreboard players reset たかし satisfaction|
|3|鎖|条件付き|常時実行|/scoreboard players reset おとうさん satisfaction
|4|鎖|条件付き|常時実行|/scoreboard players reset おかあさん satisfaction
|5|鎖|条件付き|常時実行|/scoreboard players reset 合計金額 satisfaction
|6|鎖|条件付き|常時実行|/scoreboard players reset 家族全体 satisfaction
|7|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=diamond_helmet}]
|8|鎖|条件付き|常時実行|/say "ダイヤモンドのヘルメットが選ばれました"
|9|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん diamond_helmet
|10|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん diamond_helmet
|11|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん diamond_helmet
|12|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 diamond_helmet
|13|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 diamond_helmet
|14|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=golden_helmet}]
|15|鎖|条件付き|常時実行|/say "金のヘルメットが選ばれました"
|16|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん golden_helmet
|17|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん golden_helmet
|18|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん golden_helmet
|19|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 golden_helmet
|20|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 golden_helmet
|21|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=iron_helmet}]
|22|鎖|条件付き|常時実行|/say "鉄のヘルメットが選ばれました"
|23|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん iron_helmet
|24|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん iron_helmet
|25|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん iron_helmet
|26|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 iron_helmet
|27|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 iron_helmet
|28|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=leather_helmet}]
|29|鎖|条件付き|常時実行|/say "革の帽子が選ばれました"
|30|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん leather_helmet
|31|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん leather_helmet
|32|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん leather_helmet
|33|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 leather_helmet
|34|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 leather_helmet
|35|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=diamond_chestplate}]
|36|鎖|条件付き|常時実行|/say "ダイヤモンドのチェストプレートが選ばれました"
|37|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん diamond_chestplate
|38|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん diamond_chestplate
|39|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん diamond_chestplate
|40|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 diamond_chestplate
|41|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 diamond_chestplate
|42|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=golden_chestplate}]
|43|鎖|条件付き|常時実行|/say "金のチェストプレートが選ばれました"
|44|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん golden_chestplate
|45|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん golden_chestplate
|46|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん golden_chestplate
|47|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 golden_chestplate
|48|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 golden_chestplate
|49|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=iron_chestplate}]
|50|鎖|条件付き|常時実行|/say "鉄のチェストプレートが選ばれました"
|51|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん iron_chestplate
|52|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん iron_chestplate
|53|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん iron_chestplate
|54|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 iron_chestplate
|55|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 iron_chestplate
|56|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=chainmail_chestplate}]
|57|鎖|条件付き|常時実行|/say "胸当てが選ばれました"
|58|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん chainmail_chestplate
|59|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん chainmail_chestplate
|60|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん chainmail_chestplate
|61|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 chainmail_chestplate
|62|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 chainmail_chestplate
|63|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=netherite_leggings}]
|64|鎖|条件付き|常時実行|/say "ネザライトのレギンズが選ばれました"
|65|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん netherite_leggings
|66|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん netherite_leggings
|67|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん netherite_leggings
|68|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 netherite_leggings
|69|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 netherite_leggings
|70|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=diamond_leggings}]
|71|鎖|条件付き|常時実行|/say "ダイヤモンドのレギンズが選ばれました"
|72|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん diamond_leggings
|73|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん diamond_leggings
|74|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん diamond_leggings
|75|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 diamond_leggings
|76|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 diamond_leggings
|77|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=golden_leggings}]
|78|鎖|条件付き|常時実行|/say "金のレギンズが選ばれました"
|79|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん golden_leggings
|80|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん golden_leggings
|81|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん golden_leggings
|82|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 golden_leggings
|83|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 golden_leggings
|84|インパルス|無条件|動力が必要|/testfor @e[type=armor_stand,name=cart,hasitem={item=golden_leggings}]
|85|鎖|条件付き|常時実行|/say "金のレギンズが選ばれました"
|86|鎖|条件付き|常時実行|/scoreboard players operation たかしくん satisfaction += たかしくん golden_leggings
|87|鎖|条件付き|常時実行|/scoreboard players operation おとうさん satisfaction += おとうさん golden_leggings
|88|鎖|条件付き|常時実行|/scoreboard players operation おかあさん satisfaction += おかあさん golden_leggings|
|89|鎖|条件付き|常時実行|/scoreboard players operation 家族全体 satisfaction += 家族全体 golden_leggings|
|90|鎖|条件付き|常時実行|/scoreboard players operation 合計金額 satisfaction += 合計金額 golden_leggings|


# 付録3 話しかけてくる防具屋
 　デモ動画の中で、防具屋に入ると次のようなメッセージが表示されます。

  > ここはぼうぐのみせだ。チェストからすきなぼうぐをえらんで、ぼうぐたてにそうびさせてくれ。

　動画を撮影したときのカメラの構図上、見切れてしまっていますが、実はこの防具屋にはちゃんと店員（村人）がいるのでした。
 
 ![スクリーンショット (58).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/1416d2d6-4d78-cb35-4db6-9c6c1e3817c5.png)
デモ動画に出演し損ねた防具屋の店員
 
　プレイヤーと店員との距離が、一定の値より小さくなるとメッセージが表示されるように仕組みを作ってみたのですが、本文中で紹介するチャンスがなかったので、付録に載せておきます。この仕組みは、以下の図のような3つのブロックで作られています。

 ![スクリーンショット 2024-03-07 235125.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297527/0fc9d7ca-6ac7-d6d4-09be-b872e7920c02.png)

　左のコマンドブロックは、「リピート」「無条件」「動力が必要」という動作条件を設定しています。常に動力が供給されるよう、レバーを取り付けて「オン」の状態にしてあります。このブロックで実行されるコマンドは以下です。

 ```
/execute as @p at @p run testfor @e[name="ぼうぐや",r=3]
 ```

　ざっくり説明すると、プレイヤーの現在位置を起点に、距離3の範囲内に「ぼうぐや」という名前のエンティティ（村人など）がいるかどうかを判定するというコマンドです。リピートのコマンドブロックで実行されるため、反復的に判定が行われます。

　真ん中のブロックはコンパレーターです。コンパレーターをはさんで反対側にもう一つのコマンドブロックを置きます。こちらのコマンドブロックは、「インパルス」「無条件」「動力が必要」という動作条件となっていおり、実行されるコマンドは以下となります。

 ```
/say "ここはぼうぐのみせだ。チェストからすきなぼうぐをえらんで、ぼうぐたてにそうびさせてくれ。"
 ```
  
　左のコマンドブロックの判定が真となった場合、コンパレーターを通じて右のブロックに動力が供給され、メッセージを表示するコマンドが実行されるというつくりになっています。

:::note info
/sayコマンドを直接設定した場合、実態としてはコマンドブロックがメッセージを送った形となり、送信者が[!]になります。村人本人がメッセージを送付する形をとりたい場合には、
```
/execute as @e[name=<villager_name>] run /say <message>
```
とするのがよいのかもしれませんが、今回はメッセージが画面に表示されることのみを要件として、最も簡素に実装しました。上記コマンドを設定した結果については、後日追記します。
:::

　村人ひとりひとりに固有の名前を付けておけば、村人ごとに決まったセリフを言わせたり、特定の村人に近づいたことをトリガーに処理を実行させたりできるので、ちょっとしたゲームをつくるのにも応用できそうです。