Reactで今さらズンドコキヨシなコンポーネントを実装してみる

「ズンドコキヨシ」って、その昔に何度もこすり倒されたネタだから「今さら？」感は満載だけど、まぁそんな忘れたころにリバイバルする、みんな大好きズンドコキヨシだよね。

**ランダム生成**と**明確な終了条件**があるおかげで、ちょっとしたコードのサンプルにするにはちょうどいい題材だよね。

今回の記事では、**F-Box**という「値を箱に詰める」コンセプトのライブラリを使って、このズンドコキヨシを TypeScript と React で淡々と再現してみるよ。
F-Box 自体は決して「React 専用の状態管理ライブラリ」ってわけじゃないんだけど、**RBox**という仕組みを使えば状態管理ができるようになってて、さらに**f-box-react**を組み合わせると React のコンポーネントと自然に連携できるようになるんだ。

## ズンドコキヨシとは？

一応流れをおさらいすると、こんな感じだね。

1. 「ズン」か「ドコ」をランダムで並べる
2. 直近 5 回が「ズン」「ズン」「ズン」「ズン」「ドコ」になったら
3. 「キ・ヨ・シ！」を出して終了

この単純なロジックがいいよね。
最後の「キ・ヨ・シ！」が出るまでひたすらズンドコし続ける感じが妙にクセになるんだ。

## 全体コード

では、まずは完成版のコードをまとめて載せるね。
TypeScript と React を前提にしていて、F-Box の`RBox`と、その React 用のフックである`useRBox`を使っているよ。
後の解説でポイントを補足するからサラッと眺めてみて。

```tsx
import { RBox } from "f-box-core"
import { useRBox } from "f-box-react"

type ZUNDOKO = "ズン" | "ドコ" | "キ・ヨ・シ！"

const zun = "ズン"
const doko = "ドコ"
const kiyoshi = "キ・ヨ・シ！"

const zundokoBox = RBox.pack<ZUNDOKO[][]>([])

const rnd = () => Math.random() < 0.5

const zundoko = (s: ZUNDOKO[]): ZUNDOKO =>
  s.slice(-5).join("") === zun.repeat(4) + doko ? kiyoshi : rnd() ? zun : doko

const next = (s: ZUNDOKO[]) => RBox.pack([...s, zundoko(s)])

const run = (s: ZUNDOKO[]): RBox<ZUNDOKO[]> =>
  s.includes(kiyoshi)
    ? RBox.pack(s)
    : RBox.pack(s)
        [">>="](next)
        [">>="](run)

const set = (s: ZUNDOKO[]) => {
  zundokoBox.setValue((prev) => [...prev, s])
  return zundokoBox
}

const start = () =>
  RBox.pack([])
    [">>="](run)
    [">>="](set)

export default function ZunDokoKiyoshi() {
  const [zundoko] = useRBox(zundokoBox)

  return (
    <>
      <button onClick={start}>ズンドコキヨシ</button>
      <ol>
        {zundoko.map((v, i) => (
          <li key={i}>{v.join(" ")}</li>
        ))}
      </ol>
    </>
  )
}
```

### デモ

https://stackblitz.com/edit/zundokokisyoshi?file=src%2FApp.tsx

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43936/35d0fef2-cdcb-e57b-cf45-b92476620416.png)


## コード解説

### ZUNDOKO 型

```ts
type ZUNDOKO = "ズン" | "ドコ" | "キ・ヨ・シ！"
```

- まずは「ズン」か「ドコ」か「キ・ヨ・シ！」な文字列を表すユニオン型を定義しておくよ。

### F-Box と RBox

```ts
import { RBox } from 'f-box-core';

const zundokoBox = RBox.pack<ZUNDOKO[][]>([])
```

  この `RBox` が F-Box の中心となる型で、「箱」の概念を扱えるようになるんだ。
- `RBox.pack` は、与えられた値を箱に詰めて`RBox`を返す関数。
- `zundokoBox` は`ZUNDOKO[][]`を初期値にした`RBox`。ここに結果の値をセットしてコンポーネント上でリスト表示する。

F-Box 自体はただの箱を扱うだけなんだけど、`RBox`を使うとリアクティブに値が変わる仕組みがあって、**f-box-react** の `useRBox` と組み合わせると、React のコンポーネントでその値を購読できるようになるよ。

### useRBox

```ts
import { useRBox } from "f-box-react"

export default function ZunDokoKiyoshi() {
  const [zundoko] = useRBox(zundokoBox)
  ...
```

- Reactコンポーネントの中で `useRBox` を呼び出すと、`zundokoBox` に詰めた配列の最新の状態を受け取れる。
- 値が更新されるとコンポーネントが再レンダリングされる。

### ランダム判定と zundoko 関数

```ts
const rnd = () => Math.random() < 0.5
```

- シンプルに 1/2 の確率で `true` / `false`。
- 「ズン」か「ドコ」をランダムで決めるのに使ってるだけ。

```ts
const zundoko = (s: ZUNDOKO[]): ZUNDOKO =>
  s.slice(-5).join("") === zun.repeat(4) + doko ? kiyoshi : rnd() ? zun : doko
```

- もし直近 5 回が「ズン」「ズン」「ズン」「ズン」「ドコ」だったら「キ・ヨ・シ！」。
- そうでなければランダムに「ズン」か「ドコ」。
- `slice(-5).join('')` で末尾 5 つを文字列連結して判定してるだけだよ。

### ループ処理

```ts
const next = (s: ZUNDOKO[]) => RBox.pack([...s, zundoko(s)])
```

- 配列 `s` の末尾に「ズン」「ドコ」または「キ・ヨ・シ！」を 1 つ追加して、新しい配列を箱に詰める。
- “次のステップへ進む”って感じの処理だね。

```ts
const run = (s: ZUNDOKO[]): RBox<ZUNDOKO[]> =>
  s.includes(kiyoshi)
    ? RBox.pack(s)
    : RBox.pack(s)
        [">>="](next)
        [">>="](run)
```

- もし `s` にすでに「キ・ヨ・シ！」が含まれていたら終了。
- 含まれてなければ `next` で 1 ステップ進めて、その結果をさらに `run` に渡す。
- この `['>>='](...)` って書き方はちょっと特殊だけど、「箱の中身を次の関数に渡して、新しい箱を返す」ってイメージで読むとわかりやすいと思うよ。

### 結果のセットと実行

```ts
const set = (s: ZUNDOKO[]) => {
  zundokoBox.setValue((prev) => [...prev, s])
  return zundokoBox
}
```

- `run` の最終結果（ズンドコの流れが終わった配列）を、`zundokoBox` にまとめて保存してる。
- ボタンを押すたびに結果をリストアップしていくために、履歴を蓄積してるんだ。

```ts
const start = () =>
  RBox.pack([])
    [">>="](run)
    [">>="](set)
```

- ボタンが押されたらこの関数が呼ばれる。
- 空の配列を用意して `run` でズンドコを開始し、最終的に `set` で完了。
- この一連の流れが`['>>=']`で繋がってるのが面白いところだね。

## 実行してみると

- 「ズンドコキヨシ」ボタンを押すと、画面に「ズン」「ドコ」とかがランダムで生成されて、最後に「キ・ヨ・シ！」が登場するまで続く。
- 終了すると、その結果がリストに追加されるので、何度でも「ズンドコ」できるよ。

## おわりに

**F-Box の RBox**を使うと、箱に詰めた値を「次の処理」「その次の処理」と渡していく仕組みがシンプルに書けるし、**f-box-react**を併用すれば React コンポーネントとも簡単に連携できるよ。

興味を持ったら、ぜひあなたなりにズンドコをアレンジしてみたり、全然別のネタでも RBox を使って試してみると面白いと思う。

それじゃ、ズンドコライフを楽しんでね！


### F-Box ドキュメント

https://f-box-docs.com

https://qiita.com/KentaroMorishita/items/46af4f5c2f568fefa363