GoでStar Wars

こんにちわ
ゴリラです。

先日、Twitter上でこちらのツイートを見かけました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/66178/a0d8204b-8da8-4b57-b98e-87f4f34e8d69.png" width="460px">

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Vim でスターウォーズを見れる様にしました。<a href="https://t.co/kTp3HqcLis">https://t.co/kTp3HqcLis</a> <a href="https://t.co/dM6c9WqVWA">pic.twitter.com/dM6c9WqVWA</a></p>&mdash; mattn (@mattn_jp) <a href="https://twitter.com/mattn_jp/status/1087939430164455425?ref_src=twsrc%5Etfw">2019年1月23日</a></blockquote>

さすがmattnさん。
ただちょっと残念なのが、これVimのプラグインなのでVimでしか使えないです。

ないものは作るしかない。
というわけでこれのGo版を作りました。
Goならクロスコンパイルができるので、どこの環境でもバイナリ一つで動くしいつでも見れて良いなと。

# 作ったもの
![go-starwars.gif](https://qiita-image-store.s3.amazonaws.com/0/66178/92237411-3114-d517-58a2-87782bc6e6b9.gif)

リポジトリこちらです。
<a href="https://github.com/skanehira/go-starwars"><img src="https://qiita-image-store.s3.amazonaws.com/0/66178/0ef750bc-03ed-1eb8-2b3b-cfdb9801f3b9.png" width="460px"></a>

この画像は @po3rin さんが作ったこちらのツールを使わせていただきました。
めっちゃ良いので、ぜひ使ってみてください。
<a href="https://github.com/po3rin/github_link_creator"><img src="https://qiita-image-store.s3.amazonaws.com/0/66178/3b513847-a74d-87e1-21c3-3d75372d20f0.png" width="460px"></a>

QiitaだとURLはそのままでは使用できないので、画像をコピーしてURLだけ変えて貼り付けてみてください。

```html
<a href="https://github.com/skanehira/go-starwars"><img src="https://qiita-image-store.s3.amazonaws.com/0/66178/0ef750bc-03ed-1eb8-2b3b-cfdb9801f3b9.png" width="460px"></a>
```

# 使い方
[README]()を読んでください。
Dockerがある方は

```sh
docker run --rm -it skanehira/go-starwars
```
で実行できます。

# 仕組み
テキストファイルを読み込んで出力しているだけです。

## 使用ライブラリ
- [packr](https://github.com/gobuffalo/packr)  
テキストをバイナリに埋め込むことができます。  
よく記事で見かける`go-bindata`というのがありますが、  
こちらはもうメンテされていないので、新たなにpackrを使うことにしました。  
使い方はREADMEを見ていただければと思いますが、バイナリをビルドするときは`packr build`するだけです。  
シンプルでわかりやすいですね。
- [go-colorable](https://github.com/mattn/go-colorable)  
画面クリアにエスケープシーケンスを使用していますが、Windowsの場合はそれを使用できないそうです。  
そこでmattnさんが作成されたライブラリを使用することで、windowsでも画面クリアできるようにしました。  
さすがmattnさん。

## 処理の流れ
`sw1.txt`の中身が単なるテキストで、それをfor分で読み取って出力しているだけです。
45や30というのは待ち時間になっていて、それを除いた13行の内容が1フレームになります。

```
45
                                                                  
                                                                  
  Original Work: Simon Jansen ( http://www.asciimation.co.nz/ )   
  Inspiration  : Martin W. Kirst
                 https://github.com/nitram509/ascii-telnet-server 
  Httpification: Martin Raison
                 https://twitter.com/braizh

  The hard work was done by Simon,                                
  I just wrote the http player                                  
  and placed it online using Simon's movie sources.               
                                                                                                                                    
                                                                  
30




                       WWW.ASCIIMATION.CO.NZ


                             presents





```

1行ずつ読み込んでいて時間の行はstringをintにパースして結合した文字を出力し、
指定された時間を待ってから画面クリア＆値初期化をします。
それ以外は文字列を結合するだけです。
めっちゃシンプルです。

```go
scanner := bufio.NewScanner(file)
for scanner.Scan() {
	if i%height == 0 {
		// get duration from text line
		duration, err = strconv.Atoi(scanner.Text())
		if err != nil {
			continue
		}

		// print buffered line
		fmt.Println(buffer)

		time.Sleep(time.Duration((*speed)*duration) * time.Millisecond)

		// clear terminal screen, init buffer and duration
		clear()
		buffer = ""
		duration = 0
	} else {
		// text line into buffer
		buffer += fmt.Sprintln(scanner.Text())
	}
	i++
}
```

## 画面クリア処理について　
```go
func clear() {
	// https://www.grapecity.com/developer/support/powernews/column/clang/047/page02.htm
	fmt.Fprint(stdOut, "\x1b[2J")
	fmt.Fprint(stdOut, "\x1b[0;0H")
	stdOut.Flush()
}
```
エスケープシーケンスを使用すると画面をクリアできます。
`\x1b[2J`は画面をクリアしますが、カーソルはそのままなので、カーソルの一を左上に戻さないと行けないです。
`\x1b[0;0H`はカーソルを座標0,0に戻します。
これを合わせることで画面をクリアすることが実現できました。

# 最後
ネタですが、こういうの作るのって楽しいですよね。
エンジニアは勉強する必要はないと思っていて、大事なのは自分がワクワク楽しみつつ知見と経験を深めることを見つけることなので、
これからも楽しんでいきますっ