Minecraftのコマンド（四則演算）だけでルートを外して距離を求める

:::note warn
**ご注意**
この記事は初めて書く記事になります。ｵﾃﾔﾜﾗｶにお願いいたします。
:::

Minecraft(Java版/バニラ)で、座標2地点間の距離を求めたいと、ふと思いました。
しかし、マイクラで使用できるコマンドの中で直接距離を求められるものはありませんでした。(ｿﾀﾞﾖﾈ?)
そこで、scoreboardコマンドで使用できる四則演算(+-*/)のみで距離を求めることとなったのでした...

:::note info
四則演算のみでルートの外す計算だけ知りたい方は、[3.ルートを外す](#3-ルートを外す)へお急ぎください。
:::

# 1. どう計算する？
コマンドも何も、どういう計算をすれば距離を導き出せるのか。
もし、2つの座標がX/Y/Z軸座標のうちどれか一つの値しか変化しないことが分かっているならば、もしくはX/Y/Zどれか一つの距離だけを求めればいいだけならば、その軸の値の大きい方から小さい方を引くだけで距離を出せます。

でも今求めたい値は、軸に関係ない直線距離です。これを求めるために、**直角三角形**の斜辺の長さを求める計算式を用いました。
![距離計算.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2267514/3572e518-9537-c600-dd3f-68be1fa5e972.png)
**直角三角形の斜辺の長さを求める公式**(三平方の定理[$斜辺^2=底辺^2+高さ^2$]を変形)
```math
斜辺の長さ = \sqrt{(底辺)^2+(高さ)^2}
```
この公式を使えば、2つの軸が接する面における座標間の距離が求められるので、最初にX軸とZ軸の値から上から見た距離を求め、その距離を別の直角三角形の底辺とし、それとY軸の距離から、高さも含めた距離を求められます。

:::note
【2023/01/27 追記】
```math
\sqrt{X軸距離^2 + Z軸距離^2 + Y軸距離^2}
```
こう計算すれば、1回のルート外しで高さもまとめて計算できることがわかりました。（なんで気が付かなかったんだ...^^;）
参考: [あきとんとん (2022) 『見るだけで理解が加速する　得点アップ　数学公式図鑑』KADOKAWA](https://www.kadokawa.co.jp/product/322203002287/)

:::




# 2. 距離を求める計算式を考える
上で示した公式を元に、距離を求めるのに必要な計算式を書き出していきます。

#### 1. X/Y/Zそれぞれの距離を求める
直角三角形の底辺・高さの値となる、軸ごとの距離を求めます。
この計算は簡単で、$大きい方の値-小さい方の値$となります。
例えば、座標AのX座標が200、座標BのX座標が500の場合、$大きい方の値-小さい方の値=500-200=300$となり、X座標上では300ブロック離れていることがわかります。

#### 2. X軸とZ軸から、高さを計算に入れない距離を求める
求めた軸ごとの距離を元に、X軸とZ軸の距離を、それぞれ底辺と高さとする直角三角形の斜辺の長さを求めます。
```math
X/Z軸面距離 = \sqrt{X軸距離^2+Z軸距離^2}
```
$X軸距離^2$と$Z軸距離^2$は、同じ値を掛けるだけで求められます。
```math
X軸距離^2 = X軸距離 \times X軸距離
```
$X軸距離$を500、$Z軸距離$を320と仮定して、簡単に計算してみます。

```math
X/Z軸面距離 = \sqrt{X軸距離^2+Z軸距離^2}
```
```math
=\sqrt{500^2+320^2}
```
```math
=\sqrt{500\times500+320\times320}
```
```math
=\sqrt{250000+102400}
```
```math
=\sqrt{352400}
```
```math
=\cdots?
```

$\sqrt{352400}$までは求められましたが、ルート($\sqrt{}$)が外せていません。
ルートが付いたままでは、[2乗すれば352400になる値]となり、**結局その値はなんなのかを計算する(ルートを外す)必要があります**。

ルート計算に対応した電卓を使えば、この値はボタンひとつで求められます。
しかし、マイクラにルート計算機能はついていません。使えるのは**四則演算と剰余計算のみ**です。
それでは . . . (**ここからが本題です**)

#### 3. ルートを外す
四則演算だけでルートを外すには、ニュートン法を使用します。
(詳しいことは分かりませんが)この公式を使えば結果(近似値)が出ます。


$y=$答え(近似値)
$x=$外したいルートの中の値
$a=$前に求めた近似値
```math
y=a-(a^2-x)\div(2\times a)
```

公式を使うと答えが出るというよりは、答えに近づいていくという感じでしょうか?
求める値にもよりますが、この公式で出た値を、またこの公式で計算し、また計算してを繰り返す必要があります。
試しに$\sqrt{2}$のルートを外してみます。(有名であろうこの答えは1.41421356...です)

$x=$ルートの中身$=2$
$a=$前に求めた近似値$=5$
[前に求めた近似値]には、1回目の計算ではおおよそ答えに近いであろう値、2回目以降は前の計算で求めた答えを代入します。

:::note info
ここではなんとなく$a=5$としますが、作成したコマンドでは底辺+高さの値としました
:::

```math
y=a-(a^2-x)\div(2\times a)
```
```math
y＝5-(5^2-2)\div(2\times 5)
```
```math
y＝5-(25-2)\div10
```
```math
y＝5-23\div10
```
```math
y＝5-2.3
```
```math
y＝2.7
```

答えとして$2.7$が出ました。真の値に近づいてはいますが、まだ確実な値ではありません。
より近づけるために、値を変えてもう一度同じ計算をします。

$x=$ルートの中身$=2$
$a=$前に求めた近似値$=2.7$　<-前の計算で求められた値に更新

```math
y=a-(a^2-x)\div(2\times a)
```
```math
y＝2.7-(2.7^2-2)\div(2\times 2.7)
```
```math
(省略)
```
```math
y＝1.44145536...
```

さらに近づきました。
このように繰り返していけば、どんどん真の値に近づいていき、前の計算結果からの変化は小さくなっていきます。よって、上の大きい桁から決まっていき、その桁の値は変化しなくなります。

計算は無限に繰り返せるため、計算をやめる条件が必要です。
求めたい桁数を決め、その桁の値が変化しなくなるまで繰り返せば、必要な範囲の値を求めることができます。

:::note info
今回作成したコマンド(データパック)では、精度を小数第二位までとし、第三位以降を切り捨てた値が変化しなくなるまで繰り返しています。
:::

これで、四則演算だけでルートを外すことができました。

# 3. あとは組み合わすだけ
コマンドで使える四則演算のみでルートを外すことができ、直角三角形の斜辺の長さを求めることができました。あとは組むだけです！

2つの座標間の距離を求める手順は次のとおりです。
1. **X軸とZ軸それぞれの距離から高さを計算に入れない距離を求める**
    1. 2つの座標のX軸の差とZ軸の差を求める
    2. X軸とZ軸それぞれの差の値を、それぞれ底辺と高さとした直角三角形の斜辺の長さを求める
2. **1.で求めた値とY軸の距離から高さを計算に入れた距離を求める**
    1. 2つの座標のY軸の差を求める
    2. X軸・Z軸から求めた距離と、Y軸の差の値を、それぞれ底辺と高さとした直角三角形の斜辺の長さを求める

# 4. ご参考までに
**作成したデータパックのコマンド(ルート計算部分のみ)**

:::note info
主に変数として使用しているスコアボードは整数しか扱えないため、100倍した値で計算した後、表示するときに100で割るようにしています。これで小数第二位まで求められます。
また、使用しているスコアボードは読み込み時に定義しています。
:::

```
#[ルートを外す]
#ニュートン法[y-(y²-x)/2*y]
#x=ルートを外したい値
#y=近似値(初期値は適当な値=A+B/計算2回目以降は前の計算で求めた値を代入)

#近似値(前の計算結果)を仮の結果として代入
scoreboard players operation @s I-result_100-calc_distance_jyormany = @s I-workingArea_R-calc_distance_jyormany

#(y²-x)を計算--A
#yを代入&2乗
scoreboard players operation @s I-workingArea_A-calc_distance_jyormany = @s I-workingArea_R-calc_distance_jyormany
scoreboard players operation @s I-workingArea_A-calc_distance_jyormany *= @s I-workingArea_R-calc_distance_jyormany
#xを引く
scoreboard players operation @s I-workingArea_A-calc_distance_jyormany -= @s I-arg_B-calc_distance_jyormany

#2*yを計算--B
#2を代入
scoreboard players set @s I-workingArea_B-calc_distance_jyormany 2
#yを掛ける
scoreboard players operation @s I-workingArea_B-calc_distance_jyormany *= @s I-workingArea_R-calc_distance_jyormany

#A/Bを計算--a
scoreboard players operation @s I-workingArea_A-calc_distance_jyormany /= @s I-workingArea_B-calc_distance_jyormany
#y-aを計算
scoreboard players operation @s I-workingArea_R-calc_distance_jyormany -= @s I-workingArea_A-calc_distance_jyormany

#近似値を真の値に近づけるために、前の計算結果と値が異なればもう一度計算
execute unless score @s I-workingArea_R-calc_distance_jyormany = @s I-result_100-calc_distance_jyormany run function jyormany:calc_root
```


# 5. 参考文献

https://risalc.info/src/newton-method-example-square-root.html