【ポケモン】ゼロからはじめるダメージ計算ツールづくり【第1回:プロトタイプ編】

#まえがき
**ダイマックスちょーかっこよかった！**

ポケモンシリーズの第8世代「ソード・シールド」が発売されました。
全作品プレイしている僕は当然のごとく今作も買いました。
メガシンカもZわざもないなんて…とバカにしていましたが、ダイマックスちょーかっこよかったです。
殿堂入りするまでノンストップでテンションあがりました。

さて、殿堂入りまでがチュートリアルといわれるポケモンシリーズですが、
僕もレート対戦がんばりたいと思っています。
そんななかで発生するのがこんな状況だと思います。

---
この相手にこのわざ撃って倒せるのか？？
倒せないなら交代or別のわざ撃ったほうがいいなぁ～
具体的なダメージが分かれば戦略がたてられるのに…

---

こんなとき、使いたくなるのが「ダメージ計算ツール」ですよね。

でも、僕みたいな初心者からすると、いちいちステータス打ち込んでわざ打ち込んで…って
ダメージ計算するの、対戦中にやるには億劫だし慣れてないツールだと時間も足りないんですよね。

実際の対戦画面みたいに、わざのボタン押したらダメージ計算してくれるツールがあったらいいのにな～
と思いました。

…
よし！つくろう！

#目標
1. レート対戦(今作はランクバトル)初心者でも使いやすいダメージ計算ツールを**ゼロから**つくること
1. このツールで計算した結果が視覚的にわかりやすいこと
1. エンジニアとしての知識・技術を身につけること
1. SEとして名乗るのに具体的に見せることができる成果物をつくること
1. このツールを使ってランクバトルに勝つこと

#ほしい仕様
1. 攻撃側、防御側のポケモンのステータスが計算できること
1. 実際のバトルの状況に沿ってダメージ計算ができること
1. 自分の使うポケモン・パーティー、流行っているポケモンの型が登録でき、なるべく少ない手順でそれが呼び出せること
1. わざボタンを押すとダメージ計算の結果が表示されること
1. ダメージ計算の結果が相手ポケモンのHPバーで表示されること(乱数による幅を含む)

#使う技術、環境
HTML,JavaScript,CSS
~~Python~~(いったんやめた)

いままでプログラミングでつくったことがあるものは、PythonでExcelファイルを操作して業務効率化を図るコンソールアプリだけだったので、GUIのあるものをつくるのはこれがほぼはじめてです。
Python大好きエンジニアなので、はじめはPythonでつくろうと思っていたのですが、

1. PythonのGUIライブラリよくわかんない(tkinterとか)
2. 最近ポートフォリオサイトをgithub.ioでつくったのでHTMLに慣れてた
3. ダメージ計算だけだったらとりあえずJavaScriptでもつくれそう

の理由でいったんHTML,JavaScriptをベースにプロトタイプをつくることにしました。
PythonをベースにHTML,CSSでGUIアプリをつくることができるEelというライブラリも試してみましたが、途中で3に気づき、複雑になってしまうことを避けたかったので、今回はHTML,JavaScriptを勉強しながらつくってみることにしました。

#今回の進捗
数値を直接入力して使うタイプのダメージ計算ツールを避けるためにつくるのですが、まずはステータス計算、ダメージ計算の仕組みなども理解しながらつくる必要があると思ったので、よくあるシンプルなダメージ計算ツールを作成することにしました。

[ここまでの成果物](https://kobayu0902art.github.io/content/damcalc_simple_.html)
![proto.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499408/31cff76b-0817-aca7-1e65-e6531f41cd23.png)
[HTMLソース](https://github.com/kobayu0902art/kobayu0902art.github.io/blob/master/content/damcalc_simple_.html)

<details><summary>JavaScriptソース</summary><div>

```javascript
/* 味方側 */
function hcalc(){
	let hbs =Number(document.getElementById("hbs").value);
	let hiv =Number(document.getElementById("hiv").value);
	let hev =Number(document.getElementById("hev").value);
	let hresult = Math.floor((hbs+hiv/2+hev/8+60))
	document.getElementById("hresult").innerHTML = hresult;
}

function acalc(){
	let abs =Number(document.getElementById("abs").value);
	let aiv =Number(document.getElementById("aiv").value);
	let aev =Number(document.getElementById("aev").value);
	let anc =Number(document.getElementById("anc").value);
	aresult = Math.floor((abs+aiv/2+aev/8+5)*anc)
	document.getElementById("aresult").innerHTML = aresult;
	document.getElementById("adisp").innerHTML = aresult;
}
													
function bcalc(){
	let bbs =Number(document.getElementById("bbs").value);
	let biv =Number(document.getElementById("biv").value);
	let bev =Number(document.getElementById("bev").value);
	let bnc =Number(document.getElementById("bnc").value);
	let bresult = Math.floor((bbs+biv/2+bev/8+5)*bnc)
	document.getElementById("bresult").innerHTML = bresult;
}

function ccalc(){
	let cbs =Number(document.getElementById("cbs").value);
	let civ =Number(document.getElementById("civ").value);
	let cev =Number(document.getElementById("cev").value);
	let cnc =Number(document.getElementById("cnc").value);
	cresult = Math.floor((cbs+civ/2+cev/8+5)*cnc)
	document.getElementById("cresult").innerHTML = cresult;
	document.getElementById("cdisp").innerHTML = cresult;
}

function dcalc(){
	let dbs =Number(document.getElementById("dbs").value);
	let div =Number(document.getElementById("div").value);
	let dev =Number(document.getElementById("dev").value);
	let dnc =Number(document.getElementById("dnc").value);
	let dresult = Math.floor((dbs+div/2+dev/8+5)*dnc)
	document.getElementById("dresult").innerHTML = dresult;
}

function scalc(){
	let sbs =Number(document.getElementById("sbs").value);
	let siv =Number(document.getElementById("siv").value);
	let sev =Number(document.getElementById("sev").value);
	let snc =Number(document.getElementById("snc").value);
	let sresult = Math.floor((sbs+siv/2+sev/8+5)*snc)
	document.getElementById("sresult").innerHTML = sresult;
}


/* 相手側 */
function ehcalc(){
	let ehbs =Number(document.getElementById("ehbs").value);
	let ehiv =Number(document.getElementById("ehiv").value);
	let ehev =Number(document.getElementById("ehev").value);
	ehresult = Math.floor((ehbs+ehiv/2+ehev/8+60))
	document.getElementById("ehresult").innerHTML = ehresult;
	document.getElementById("ehdisp").innerHTML = ehresult;
}

function eacalc(){
	let eabs =Number(document.getElementById("eabs").value);
	let eaiv =Number(document.getElementById("eaiv").value);
	let eaev =Number(document.getElementById("eaev").value);
	let eanc =Number(document.getElementById("eanc").value);
	let earesult = Math.floor((eabs+eaiv/2+eaev/8+5)*eanc)
	document.getElementById("earesult").innerHTML = earesult;
}

function ebcalc(){
	let ebbs =Number(document.getElementById("ebbs").value);
	let ebiv =Number(document.getElementById("ebiv").value);
	let ebev =Number(document.getElementById("ebev").value);
	let ebnc =Number(document.getElementById("ebnc").value);
	ebresult = Math.floor((ebbs+ebiv/2+ebev/8+5)*ebnc)
	document.getElementById("ebresult").innerHTML = ebresult;
	document.getElementById("ebdisp").innerHTML = ebresult;
}

function eccalc(){
	let ecbs =Number(document.getElementById("ecbs").value);
	let eciv =Number(document.getElementById("eciv").value);
	let ecev =Number(document.getElementById("ecev").value);
	let ecnc =Number(document.getElementById("ecnc").value);
	let ecresult = Math.floor((ecbs+eciv/2+ecev/8+5)*ecnc)
	document.getElementById("ecresult").innerHTML = ecresult;
}

function edcalc(){
	let edbs =Number(document.getElementById("edbs").value);
	let ediv =Number(document.getElementById("ediv").value);
	let edev =Number(document.getElementById("edev").value);
	let ednc =Number(document.getElementById("ednc").value);
	edresult = Math.floor((edbs+ediv/2+edev/8+5)*ednc)
	document.getElementById("edresult").innerHTML = edresult;
	document.getElementById("eddisp").innerHTML = edresult;
}

function escalc(){
	let esbs =Number(document.getElementById("esbs").value);
	let esiv =Number(document.getElementById("esiv").value);
	let esev =Number(document.getElementById("esev").value);
	let esnc =Number(document.getElementById("esnc").value);
	let esresult = Math.floor((esbs+esiv/2+esev/8+5)*esnc)
	document.getElementById("esresult").innerHTML = esresult;
}

function damagecalc(){
	let aorc =Number(document.getElementById("aorc").value);
	let power =Number(document.getElementById("power").value);
	let match =Number(document.getElementById("match").value);
	let comp =Number(document.getElementById("comp").value);
	//let ehdresult =Number(document.getElementById("ehdisp").value);
	if (aorc == 0){
		//let adresult =Number(document.getElementById("adisp").value);
		//let ebdresult =Number(document.getElementById("ebdisp").value);
		temp = Math.floor(22*power*aresult/ebresult)
		temp = Math.floor(temp/50+2)
		mintemp = Math.floor(temp*0.85)
		temp = Math.floor(temp*comp)
		mintemp = Math.floor(mintemp*comp)
		temp = Math.ceil(temp*match-0.5)
		mintemp = Math.ceil(mintemp*match-0.5)
	}
	else{
		//let cdresult =Number(document.getElementById("cdisp").value);
		//let eddresult =Number(document.getElementById("eddisp").value);
		temp = Math.floor(22*power*cresult/edresult)
		temp = Math.floor(temp/50+2)
		mintemp = Math.floor(temp*0.85)
		temp = Math.floor(temp*comp)
		mintemp = Math.floor(mintemp*comp)
		temp = Math.ceil(temp*match-0.5)
		mintemp = Math.ceil(mintemp*match-0.5)
	}
	document.getElementById("mintemp").innerHTML = mintemp;
	document.getElementById("temp").innerHTML = temp;
	document.getElementById("mincurrentHP").innerHTML = ehresult-temp;
	document.getElementById("maxcurrentHP").innerHTML = ehresult-mintemp;
	document.getElementById("minrate").innerHTML = (ehresult-temp)/ehresult*100;
	document.getElementById("maxrate").innerHTML = (ehresult-mintemp)/ehresult*100;
}
```
</div></details>

基本的な計算ツールとしての要件を満たすものはつくれたかなと思います。
具体的には、[ほしい仕様](#ほしい仕様)の
1 は完璧に満たしている
2 はダメージ計算はできているが、どうぐや積み技によるランク上昇、天候などまだまだ足りない要素がある
3以降 はこれから
です。

##反省点として
JavaScriptのコードが不必要に長いです。汚いです。

機能実装最優先でゴリ押しで書きましたが、
1. Javascript側の関数を1つにしてHTML側でonclick=の引数を指定することでステータスの計算に適用
2. JavaScript側で親関数を作って各ステータスを計算する子関数を作り、HTML側はいまのまま
のどちらかできれいなコードになる気がします。

このへんはまだオブジェクト指向な考え方が身についてないのですぐスマートな書き方にならないと思っています。
勉強します。

あと、随時コメントを書くクセをつけていかないとなと思っています。
いまは1人でつくっていますが、複数人で開発するときにコメントがないのは大問題ですよね。

#課題
1. ほしい仕様2の道具などの対応
1. ほしい仕様3について、いきなりいろんなポケモンに対応するのは難しいので、いくつかのポケモンのプリセットボタンでダメージ計算のデモができるようにする
1. ダメージ計算の結果をHPバーで表示する
1. 機能はできてきたがデザインがダメ

見えている課題についてはこんなところです。

#次回予告
次回はプリセットボタン、HPバーの実装について書きたいと思います。
また、今回は技術的なこと、苦労した点などについて書けていませんので、次回からはそのあたりも書いていきます。
(この記事についても追記したい)

#さいごに
**いっしょにつくりたい!という方絶賛募集中です！**
デザイン強いよ！、WEBアプリ強いよ！、勉強したいよ！という方、コメントなどにてお待ちしております！

つぎ→　[次回:プリセットボタン・HPバー編]()