「正」の数でオセロするWebComponentを作った

元ネタはこれです。

https://togetter.com/li/1359065

![othello.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93999/bb10e039-b776-5058-0756-c0c908a12fe3.gif)


突貫で作ったので見た目はガバガバだし、「正」の数じゃなくて数字が増えてくだけなんですが、
これ以上頑張る気力が沸かなかったので許してください~~なんでもしますから~~
~~オセロのロジックまわり作るのが一番大変でした~~

以下のURLで遊べるのでよければ遊んでみてください
https://codepen.io/kuwabatak/full/YbLrMa


<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="html,result" data-user="kuwabatak" data-slug-hash="YbLrMa" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="counting-othello">
  <span>See the Pen <a href="https://codepen.io/kuwabatak/pen/YbLrMa/">
  counting-othello</a> by KuwabataK (<a href="https://codepen.io/kuwabatak">@kuwabatak</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

### 遊び方

見たまんまオセロです。マス目をクリックするとオセロのルール的に置ける場所なら、自分の色でぬり潰されて、周りのマスの色が反転します。
普通のオセロと違うとこはマス目の中に数字が振ってあって、ひっくり返されるごとに1ずつ増えていくとこです。
偶数が青側、奇数が赤側ですね。
なので、青側がクリックしたとこだけ2増えちゃいます。

ひっくり返された回数がカウントされていくので、攻防の激しいとこがどこかわかって面白いんじゃないかと思って勢いで作ったんですが、一人遊びしてた感じでは、ぶっちゃけまんなかあたりの数字が大きくなるだけであまり面白くなかったです。。。他の人と対戦はしていないので、対戦すればまた変わるのかもしれません。

あと、せっかくなのでひっくり返せる上限値を設定できるようにしました、上限値に達すると、そのマスは変更不能の中立地帯になり、挟んでも色を変えることができなくなります。
また、自分のセルで挟んだ部分の中に中立地帯が含まれているとひっくり返らなくなります。空きマスと同じ判定ですね

あんまり上限値を低くしすぎると、中立地帯がどんどん多くなっていって、置ける場所が少なくなってしまうのですが、うまいこと設定してやると後半は中立地帯にすべきかどうかも考えながらプレイすることになってなかなか面白いんじゃないかと思います。

### 使い方

WebComponentで作ったので、以下のようなhtmlファイルを作ればそれだけで使えます。

```.html
<!DOCTYPE html>
<html>
<head>
  <!-- WebComponent読み込み -->
  <script src="https://kuwabatak.github.io/counting-othello/counting-othello.js"></script>
</head>
<body>
  <!-- custom elementを書く -->
  <ks-othello></ks-othello>
</body>
</html>
```

上でやったように、Code Penとかでも使えますし、VueやReactに組み込んで、Webアプリっぽくもできると思います。(とかいってWebComponentをVueやReactで使ったことないんですが)


一応、以下みたいな感じで縦横盤面の大きさだけ外から指定できるようにしました。ほんとはもっといろいろ引数設定できるようにしたかったんですが、力尽きましたごめんなさい

```.html
<ks-othello x="8" y="8" ></ks-othello>
```

### 内部の話

[Stencil](https://stenciljs.com/docs/introduction)というWebComponentを作れるフレームワークを使って作っています。(というかStencilの勉強のために作った)。ついこの前v1.0になったばっかりの新し目のフレームワークです。雰囲気はReactにかなり近いです。
参考までに、今回作ったオセロのソースの一部を以下に貼り付けておきます。

JSXベースでDom部分を書いてく感じですが、`@Prop()` `@Watch` などのデコレータが用意されていて、TypeScriptでVueを書いたときみたいな雰囲気になってるのが、なんとなくわかるんじゃないでしょうか。

[公式ドキュメント](https://stenciljs.com/docs/introduction)も大した量じゃないので、ReactやVueを触ったことのある人なら、すぐに書けるようになるんじゃないかと思います。


```othello.tsx
import { Component, Prop, h, State, Watch } from '@stencil/core';
import { generateField, calcReverseField } from '../../utils/utils';
import _ from 'lodash'

@Component({
  tag: 'ks-othello',
  styleUrl: 'othello.css',
  shadow: true
})
export class Othello {
  /**
   * xLength of Field
   */
  @Prop() x_length: number = 8

  /**
   * yLength of Field
   */
  @Prop() y_length: number = 8

  @State() field: number[][] = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

  @State() player: 0 | 1 = 0

  @State() maxval: number = 1000

  /**
   * コンポーネントロード時発火する関数
   */
  componentWillLoad() {
    this.field = generateField(this.x_length, this.y_length)
  }

  @Watch('x')
  xWatch() {
    this.field = generateField(this.x_length, this.y_length)
  }

  @Watch('y')
  yWatch() {
    this.field = generateField(this.x_length, this.y_length)
  }

  /**
   * オセロをおいたときの処理
   * 
   * @param e 
   * @param x 
   * @param y 
   */
  private clickSlot(e: Event, x: number, y: number) {
    ...
  }

  /**
   * 各色のコマの数を返す
   * @param color 
   */
  private countCellNum(color: 'blue' | 'red') {
    ...
  }

  /**
 * 各色の値の合計値を返す
 * @param color 
 */
  private countCellSum(color: 'blue' | 'red') {
    ...
  }

  /**
 * inputの中身が変わったときに結果を再計算し、
 * changeResultイベントを発火して呼び出し元に伝える
 * 
 * @param event  
 */
  handleMaxValChange(event: Event) {
    this.maxval = event.target["value"]
  }

  private reset() {
    this.field = generateField(this.x_length, this.y_length)
    this.player = 0
  }

  render() {
    return <div>
      <table>
        {this.field.map((xArr, yIndex) =>
          <tr>
            {xArr.map((num, xIndex) =>
              <th class={[
                'cell',
                num === 0 ? 'unuse' : 'use',
                num % 2 === 0 ? 'blue' : 'red'
              ].join(' ')}
                onClick={(e) => this.clickSlot(e, xIndex, yIndex)} >
                <div >
                  {num}
                </div>
              </th>
            )}
          </tr>
        )}
      </table>
      <div class={['teban',
        this.player === 0 ? 'red' : 'blue'
      ].join(' ')} >
        {this.player === 0
          ? 'レッドの番だよ!!'
          : 'ブルーの番だよ！！'}</div>
      <div>{'レッドのセルの数：' + this.countCellNum('red')}</div>
      <div>{'ブルーのセルの数：' + this.countCellNum('blue')}</div>
      <div>{'レッドのセルの中の合計値：' + this.countCellSum('red')}</div>
      <div>{'ブルーのセルの中の合計値：' + this.countCellSum('blue')}</div>
      <div class="cp_iptxt">
        許されるセルの値の最大値：
      <input type="number" value={this.maxval}
          onChange={(event) => { this.handleMaxValChange(event) }} />
      </div>
      <button class="btn-square-little-rich" onClick={() => this.reset()}>リセット</button>
    </div>
  }
}

```


汚いソースなのであれですが、[興味のある人向けにGitHubのソース](https://github.com/kuwabataK/counting-othello) をおいておきます。

CSS部分とかは全くわからないので完全にネットに落ちてたコードのコピペです。
CSSの勉強もしないと・・・


終わり