？？？「これぐらいの奇蹟…安いもの！！」1種2種混合機の 80% ループで大連荘を生み出せ！

# :rice::rice: 回想シーン

>  あずき （[このあいだ見かけたの](https://qiita.com/sikani/items/7c0eeac792a9cf0b1baa) みたく AA 使って何か作りたいな～）
>  あずき （そうだ！シ◯フォギアラッシュのシミュレーターとか面白そうじゃない？）
>  ザッ...（足音）
>  あずき「し、師匠ッ！」
>  ？？？「（バズる）勝算はあるのかッ！？」
>  あずき「思い付きを数字で語れるものかよッ！」

# :musical_score: TL;DR

『CRF戦姫絶唱シ◯フォギア [^1] 』をご存知でしょうか？

増大に次ぐ増大、噂の機種の継続率体験シミュレーターを作ってみました！
![不承不承ながら左打ちしましょう.gif](https://qiita-image-store.s3.amazonaws.com/0/135956/478bc519-edc4-0d21-50b1-946c73dcba59.gif)
こんな感じ！

> ？？？「安い…安さが爆発しすぎてる！！」



下の方にソースコード（GitHub）も公開していますが、辿り着くまでがまあまあ長いので読むのめんどくさい人は、

> ？？？「最速で最短でまっすぐに…一直線に！！」

[こちら](https://qiita.com/azukisiromochi/private/6a389f4dfdfc9fa8f2b3#milky_way-%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB) へどーぞ！


# :rocket::full_moon_with_face: はじめに



回想シーンの主人公（？）も言ってましたが、やってみたいことがいくつかありました。

- AA を使って何か作りたい
- パチンコ遊技機（以降、遊技台と記載 [^2] ）の複雑な仕様を一部でも実装してみたい
- シ◯フォギアで事故りたい（~~リアルでも~~）



> ？？？「胸の歌を信じなさい…」

ってことで作ってみました！



# :slot_machine: 仕様について

遊技台、ひいては1種2種混合機 [^3] の仕様はいささか難しいので少し説明します。

（わかりやすいように数値はシ◯フォギアのもので記載しています）



1. 通常時 `1/199` の確率で大当りに当選

2. 大当り後、電サポ 1 回＋保留 4 個の計 5 回転の間に `1 / 7.4` の確率に当選することでラッシュ突入（ラッシュ突入期待値は約 `52 %` ）

3. 以降ラッシュ中は電サポ 7 回 + 保留 4 個の計 11 回転の間に `1 / 7.4` の確率でラッシュ継続（継続期待値は約 `80 %` ）

（保留玉を先読み抽選して連荘を確定させる _V-STOCK_ という機能がありますが、この仕様の有無で継続率的に変化がないことと、抽選とは並行に電チューへの入賞をリアルタイムで行う（抽選数を上限まで一定時間ごとに増やしていく）実装はしんどいからパスｗ）



どうですか？けっこー複雑ですよねｗ

> ？？？「言ってること 全然分かりません」



# :microphone: どんなシミュレーターにするの？

### 実装範囲は

仕様が複雑なのは説明しました。

じゃあどの部分をシミュレーターとして実装するのか？



仕様の 1. は実装してしまうとダレてしまいますｗパス。

（シミュレーターでまでハマり体験したくないでしょう？ :sob:）



仕様の 2. を含めるかどうか...が悩んだところなんですが、個人的に最終決戦（ `52 %` ）を**乗り越えてこそ熱くなれる**ものです！

そんな謎の持論を振りかざし仕様 2. + 仕様 3. でシミュレーターを作ってみます！



### どんなUIなのか

上の方に動画載せちゃってますけど、CLIツールですね。

UIというかコマンドラインでぽちぽちしてればシミュレーション出来る感じです。



# :musical_note: サクッと機能説明

シミュレーターと言いつつゲーム感覚で楽しめるものにしています。

ラッシュを継続させて「継続数」と「ポイント数」を競う感じです。



### 1. シミュレーターを起動するデース！

```
＿人人人人人人人人人人＿
＞ 最 終 決 戦 開 幕 ＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣
```

というタイトルと共にゲームがスタートします。

（~~もうゲームって言っちゃってるよ~~）



### 2. 強い意志を持って [ Enter ] キーを押すデース！

```
勝 機 を 零 す な ！  掴 み 取 れ ！
```

とセリフが出るので、

>？？？「推して参る！」

って感じで押してください！



抽選がスタートし図柄が揃えば「シ◯フォギアチャンス」突入です。

```
// 当たるときの図柄例
７ ７ ７

// 外れたときの図柄例（ランダム）
１ ７ １
```

抽選のチャンスは5回あるので、それまでに当選できない場合はゲーム終了です。



### 3. シ◯フォギアチャンス突入デース！

```
＿人人人人人人人人人人人人人人人人人人人＿
＞ シ ◯ フ ォ ギ ア チ ャ ン ス 突 入 ＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣
```

というタイトルと共にラッシュスタートです！



以降は「最終決戦」の時同様に [ Enter ] キーをポチポチしながら抽選を繰り返し、**当選したらラッシュ継続**、規定ゲーム数で**当選できなかったらラッシュ終了**（ゲーム終了）です。



### 4. スコアを確認するデース！

ラッシュが終了したら次のようなスコアが表示されます。

```
// FEVER ・・・ 継続数
// TOTAL ・・・ ポイント数
┌ーーーーーーーーーーーーーーーーーー┐
│　　　　　　　　　　　　　　　　　　│
│　　Ｆ Ｅ Ｖ Ｅ Ｒ　✕　１０　　　　│
│　　　　　　　　　　　　　　　　　　│
│　　Ｔ Ｏ Ｔ Ａ Ｌ　　　　　　　　　│
│　　　　　  １ ０ ７ ８ ０ ｐ ｔ 　│
│　　　　　　　　　　　　　　　　　　│
└ーーーーーーーーーーーーーーーーーー┘
```

目指せ大連チャン！

リアルで50 連とかさせる豪運の持ち主もいるそうなので、ぜひ目指してみてください！（~~リアルで~~）



# :memo::zzz: やっとこさ実装のはなし

全部紹介しても仕方がないので、何点かピックアップして説明しようと思います。

（あずき [^4] の GO言語のスキルは十数時間の学習程度...あしからず...）



### 抽選のはなし

仕様的にメインになる部分です。

色々と調べていたら抽選用のライブラリーを作られている方がいて利用させていただきました。

:link: [Goでlotteryという抽選ライブラリを作った](https://kyokomi.hatenablog.com/entry/2015/07/26/151138)



##### インポートして

```go
import "github.com/kyokomi/lottery"
```



##### 初期化して

```go
lot := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))
```



##### こう書けば

```go
lot.Lot(20) 
```

20% の確率の抽選が行われます。



##### さらに、こう書けば

```go
lot.LotOf(1, 10)
```

1 / 10 の確率の抽選が行われます。



##### さらに、さらに、こう書けば

```go
// 大当りの種類（15R, 9R, 4R, みたいなの）
type feverKind struct {
	// 獲得 pt （期待値）
	Point int32
	// 図柄（７とか１とか）
	DispDesign string
	// 当選割合（’15R は 40%’とかの "40" 部分を設定）
	DropProb int
}

// lottery 使用のために必要な抽選用のメソッド
func (f feverKind) Prob() int {
	return f.DropProb
}

func main {
    // 大当りの種類を設定する
    ferverkinds := []lottery.Interface{
		feverKind{Point: 1470, DispDesign: "７", DropProb: 40},
		feverKind{Point: 1176, DispDesign: "３", DropProb: 3},
		feverKind{Point: 784, DispDesign: "５", DropProb: 7},
		feverKind{Point: 392, DispDesign: "１", DropProb: 50},
	}

	// 抽選
	lotIdx := lot.Lots(ferverkinds...)
	ferver := ferverkinds[lotIdx].(feverKind)
    
    // 当選した ferverKind の図柄が表示される
    fmt.Println(ferver.DispDesign)
}

```

ちょっと複雑ですが、インターフェースとして必須の `Prob` メソッドが返す `feverKind#DropProb` の値が当選確率（%）になります。

例えば、`DispDesign: "７"` のデータが当選する確率が 40% ってことですね！



##### すごく便利！

自分で実装するのも考えてましたが、作り始めたタイミングでインターフェースを理解できていませんでしたし、これほどのものが作れるとは思えませんでしたので、ありがたく利用させていただきました :yum:



> ？？？「ちきしょう...かなわないわけだ。」



##### ハマりポイント

ハマったと言うほどではないのですが、`lot.LotOf(1, 10) ` この書き方をしたときに調子に乗って分母に `65532` を設定したんですね。

そうしたら型が `int` だったので永遠に当たらないボッタ台が出来上がったのでしたｗ :smiling_imp:



### CLI ツールのはなし

正直、作ってて Web にすれば良かったなーなんて思ったりもしましたｗ

でも、まあ、乗りかかった船でしたので、コマンドラインツール立ち上げて完結させたいなぁと、この形になったわけです。



CLI ツール化するにあたり、ここでもライブラリーを使用させていただきました。

:link: [urfave/cli](https://github.com/urfave/cli)



##### インポートして

```go
import "github.com/urfave/cli"
```



##### 初期化して

```go
app := cli.NewApp()
```



##### 関数を登録して

```go
app.Action = func(c *cli.Context) error {
    // シ◯フォギアを遊戯する
	playSymphogear()
	return nil
}

app.Run(os.Args)

```



##### 実行する

```go
app.Run(os.Args)
```

ただ動かしたいだけなら簡単です。



##### 他にも

```go
app.Name = "greet"
app.Usage = "fight the loneliness!"
```

とか登録できて、`help` 付けて叩くと

```shell
$ greet help
NAME:
    greet - fight the loneliness!

USAGE:
    greet [global options] command [command options] [arguments...]

VERSION:
    0.0.0

COMMANDS:
    help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS
    --version Shows version information
```

みたいに実行されます。



##### とにかく便利！

自分で作るなんて現時点で無謀だし、助かりました。

シンプルな使い方しかできてないので、もう少し触ってみようと思います！



> ？？？「ちきしょう...かなわないわけだ。」



### ファイルの読み込みのはなし

AA を出力するのに `fmt` だとすごく大変だなーと考えていたので、ファイルを読み込んで１行ずつ出力する方法をとりました。

##### 開いて

```go
// ファイルオープン
file, err := os.Open(filePath)
// エラーチェック
if err != nil {
	panic(err)
}
// ファイルを閉じる関数を defer で最後に遅延実行
defer file.Close()
```



##### 読み込んで

```go
scanner := bufio.NewScanner(file)
```



##### 1行ずつ出力

```go
for scanner.Scan() {
	line := scanner.Text()
	fmt.Println(line)
}
```



##### 文章とか複数行に渡る文字列を出力するときには便利！

AA ってフォントによっては見づらいものがあるんですよね :disappointed_relieved:

色々な種類を出力させて試すんですが、そんなときに `fmt#Println` とかで１行ずつ出力してたら絶望します...

ファイルなら中身を貼り替えるだけなので、この方法を選んで正解でしたね！



### ビルドのはなし

ビルドして実行してみました。

```shell
> go build -o tmp/test.exe
> tmp/test
panic: open ./AA/challenge-start.txt: The system cannot find the path specified.
```

あら？エラー。

どうやら読み込むファイルが無いと駄目みたい... :tired_face:



##### rakyll/statik というライブラリー

を見つけまして、読み込みファイルをバイナリー化して一緒にビルドしてしまおうという代物。

さっそく使ってみました。



:link: https://github.com/rakyll/statik



##### ダウンロードしてきて

```shell
go get github.com/rakyll/statik
```



##### ファイルのあるフォルダを指定して実行

```shell
> statik -src=/path/to/your/project/public
# Windows 環境だとビルドして実行ファイル作らないと駄目だよー
```

次のようにファイルが出来上がります。

```shell
.
├── statik
│   └── statik.go
└── main.go
```



##### コード側で利用する

```go
import (
  "github.com/rakyll/statik/fs"

  _ "./statik" // TODO: Replace with the absolute import path
)

// ...

statikFS, err := fs.New()
if err != nil {
	log.Fatal(err)
}
```

`TODO: ` の部分は一度 GitHub に `push` してから自分のパッケージをインポートしましょう。



##### ファイル読み込みのときのように扱える

```go
file, err := statikFS.Open(filePath)
if err != nil {
	panic(err)
}
defer file.Close()
```

ファイル読み込みのときに `os.Open(filePath)` としていた部分を `statikFS.Open(filePath)` としています。

ちなみにファイルパスは `/fileName.txt` のようにカレント直下にあるかのように指定します。



##### これもまた便利！

このライブラリーを見つけるまでは「実行ファイルと読み込みファイルセットでいいかｗ」みたいに考えていましたけど、やっぱりまとまっている方がいいですよね！

作ってくださった方に感謝です！ :yum:



> ？？？「ちきしょう...かなわないわけだ。」



##### ハマりポイント

普通の人は無いと思うんですけど、

```shell
> go get github.com/rakyll/statik
# github.com/rakyll/statik
..\..\rakyll\statik\statik.go:163: fHeader.Modified undefined (type *zip.FileHeader has no field or method Modified)
```

みたいに出たわけです。

:link: https://golang.org/pkg/archive/zip/#FileHeader

ときて、

```shell
> go version
go version go1.8.3 windows/amd64
```

はい、バージョン古かったです :innocent:



>？？？「『どんくさい』なんて名前じゃない！！私は（ｒｙ」



# :syringe: やり残したこと

他にも実装してみたいことがあったのですが、時間の関係上できてないです。 :sleepy: 

ちょっとずつバージョンアップさせていこうかな？



### 機能面

- 文字色を変えて期待度をもたせる
- _V-STOCK_ をどうにか表現する 
- 各キャラの AA カットインしてプレミア感だす
- スコアに応じて AA を切り替える
- ~~スロット版を...~~



> ？？？「これで僕も英雄になれる…この星のラストアクションヒーローだ！！」



### 実装面

- テストコードを書く、テストする（書き方覚えるところから）
- GitHub での資産の管理方法を覚える（オープンなとこで管理したことないから）
- ~~かっこいい READ ME つける（他の人のかっこいい...）~~ （ちょっと作ってみた）
- そもそももっと GO言語 の知識付けないと...



> ？？？「飯食って、映画観て、寝る！男の鍛錬はそいつで十分よ！」

（~~師匠！なるほど！~~）



# :milky_way: おわりに

ネタのつもりで作り始めましたがなんとか作りきりました！

GO言語のスキルが乏しいため、コードにまずい点などありましたらアドバイスもらえると嬉しいです！
（コメント大歓迎です！）

:link: https://github.com/azukisiromochi/ikirunowo-akiramenaide




最後まで見てくださった方はきっとシ◯フォギア好きですよね？（違うかｗ）

最後はこの言葉で締めくくりましょう！

では、ご一緒に...

> ### ？？？「コードを書くのを諦めないで！」




[^1]: 導入当時は同時期に導入された「魔法少女まど◯☆マギカ」に人気が流れたこともありバラエティーコーナーに数台設置という店が多かった。だが、バランスの良い演出、熱い「最終決戦」モード、高い連チャン性能などから人気が出ていき、バラエティーコーナーの台の取り合いという状況が起こった。人気の上昇に伴い店舗側も増大を重ね、現在（2018/11）では**導入率は 75% を超えている**。また、通常 時 1/99 のライトバージョンも 12 月に発売をい控えている。
[^2]: 「以降、遊技台と記載」と記載した割に、それ以降で１度しか出てこなかったのは書いた本人も草。
[^3]: 1種は「デジパチ」、2種は「デジ羽根（羽根モノ）」を意味しそれぞれで当選してラッシュに突入する仕様の遊技台（ここで出てきたｗ）のこと。**新基準により確変継続率が 65% を超えることができなくなってしまった**ため、それらを複合することで高い継続率（と感じさせるがそれぞれ単独では 65% 未満）を体験することが出来る。 
[^4]: この記事の筆者。やけに詳しい感じで書いていますが**現役はとう退いています**。待ち時間に低貸しを打つことはありますが**退いています**。（念押し！）ちなみに遊戯中は技術介入に夢中で演出をよく見逃します。技術介入で思い出しましたがシ◯フォギアは右が甘いので技術介入すればかなりの確率でオーバー入賞が可能です（ただし切ちゃんには注意）。お試しください。