[ネタ]ステートマシンでズンドコしたかった

# ステートマシンでズンドコする

TagFeedに流れてたのが目についたので書いてみた。[^1]

## 状態遷移表

「ズン → ズン → ズン → ズン → ドコ → キ・ヨ・シ！」を状態遷移表に起こすとこうなる。

| 入力＼状態 | 初期状態(0) | ズン(1) | ズン(2) | ズン(3) | ズン(4) | ドコ(5) | キヨシ(6) |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 「ズン」 | ズン(1) | ズン(2) | ズン(3) | ズン(4) | 初期状態(0) | キヨシ(6) | - |
| 「ドコ」 | 初期状態(0) | 初期状態(0) | 初期状態(0) | 初期状態(0) | ドコ(5) | キヨシ(6) | - |

## 状態遷移図

表があるなら要らないのだが、折角作ったので載せとく。(PlantUML)

![FSM]

## ソース

状態を表現するために似たようなオブジェクトが大量に生えるので書くのは面倒くさい。

```ts:ZunDocoStateMachine.ts
namespace StateMachine {
  type Phrase = 'ズン'|'ドコ';
  type Call = Phrase|'キ・ヨ・シ！';

  namespace StateID {
    export const S0 = Symbol('Initial(0)');
    export const S1 = Symbol('Zun(1)');
    export const S2 = Symbol('Zun(2)');
    export const S3 = Symbol('Zun(3)');
    export const S4 = Symbol('Zun(4)');
    export const S5 = Symbol('Doco(5)');
    export const S6 = Symbol('Kiyoshi(6)');
  }

  interface State {
    readonly id: Symbol;
    next(input: Phrase): State;
  }

  const State0: State = {
    get id() { return StateID.S0; },
    next(input: Phrase) {
      return (input === 'ズン')? State1 : State0;
    }
  };

  const State1: State = {
    get id() { return StateID.S1; },
    next(input: Phrase) {
      return (input === 'ズン')? State2 : State0;
    },
  };

  const State2: State = {
    get id() { return StateID.S2; },
    next(input: Phrase) {
      return (input === 'ズン')? State3 : State0;
    }
  };

  const State3: State = {
    get id() { return StateID.S3; },
    next(input: Phrase) {
      return (input === 'ズン')? State4 : State0;
    }
  };

  const State4: State = {
    get id() { return StateID.S4; },
    next(input: Phrase) {
      return (input === 'ズン')? State0 : State5;
    }
  };

  const State5: State = {
    get id() { return StateID.S5; },
    next(input: Phrase) {
      return State6;
    }
  };

  const State6: State = {
    get id() { return StateID.S6; },
    next(_: Phrase) {
      throw new Error('TERMINATE');
    }
  };

  class StateMachine {
    _state: State = State0;
    _count: number = 0;
    hasNext(): boolean {
      return this._state.id !== StateID.S6;
    }
    next(input: Phrase) {
      this._state = this._state.next(input);
      console.log(`${input} -- ${this._count}`);
      this._count++;
    }
    get count(): number {
      return this._count;
    }
  }

  export function create(): StateMachine {
    return new StateMachine();
  }
}

for (const machine = StateMachine.create();
  machine.hasNext();
  machine.next((Math.random() * 10) < 5? 'ズン' : 'ドコ'));
console.log('キ・ヨ・シ！');
```

### 実行結果

状態「ドコ(5)」から「キヨシ(6)」に移動する時に入力を無視する仕様にしたせいで『ズンズンズンズンドコドコキヨシ』になってしまったが、状態遷移は合ってるのでまあいいか！

<details open><summary>運がいい時</summary><div>

```bash
$ deno run -A ZunDocoStateMachine.ts
ズン -- 0
ズン -- 1
ズン -- 2
ズン -- 3
ドコ -- 4
ドコ -- 5
キ・ヨ・シ！
```

</div></details>

<details><summary>運が悪い時</summary><div>

```bash
$ deno run -A ZunDocoStateMachine.ts
ズン -- 0
ドコ -- 1
ドコ -- 2
ズン -- 3
ドコ -- 4
ズン -- 5
ズン -- 6
ズン -- 7
ドコ -- 8
ドコ -- 9
ドコ -- 10
ズン -- 11
ドコ -- 12
ズン -- 13
ズン -- 14
ズン -- 15
ズン -- 16
ズン -- 17
ドコ -- 18
ズン -- 19
ドコ -- 20
ドコ -- 21
ズン -- 22
ドコ -- 23
ドコ -- 24
ズン -- 25
ズン -- 26
ズン -- 27
ドコ -- 28
ズン -- 29
ドコ -- 30
ドコ -- 31
ドコ -- 32
ズン -- 33
ズン -- 34
ドコ -- 35
ズン -- 36
ズン -- 37
ズン -- 38
ドコ -- 39
ドコ -- 40
ズン -- 41
ズン -- 42
ズン -- 43
ドコ -- 44
ドコ -- 45
ズン -- 46
ドコ -- 47
ズン -- 48
ドコ -- 49
ズン -- 50
ズン -- 51
ズン -- 52
ズン -- 53
ズン -- 54
ズン -- 55
ズン -- 56
ズン -- 57
ドコ -- 58
ズン -- 59
ドコ -- 60
ズン -- 61
ズン -- 62
ズン -- 63
ドコ -- 64
ズン -- 65
ドコ -- 66
ズン -- 67
ズン -- 68
ズン -- 69
ドコ -- 70
ズン -- 71
ズン -- 72
ズン -- 73
ドコ -- 74
ズン -- 75
ズン -- 76
ドコ -- 77
ズン -- 78
ドコ -- 79
ズン -- 80
ズン -- 81
ドコ -- 82
ドコ -- 83
ズン -- 84
ドコ -- 85
ズン -- 86
ドコ -- 87
ドコ -- 88
ズン -- 89
ズン -- 90
ドコ -- 91
ズン -- 92
ズン -- 93
ドコ -- 94
ドコ -- 95
ドコ -- 96
ドコ -- 97
ドコ -- 98
ズン -- 99
ドコ -- 100
ドコ -- 101
ズン -- 102
ズン -- 103
ズン -- 104
ドコ -- 105
ズン -- 106
ズン -- 107
ズン -- 108
ズン -- 109
ドコ -- 110
ドコ -- 111
キ・ヨ・シ！
```

</div></details>

[FSM]: http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuG8oIb8Lb9vsp7qsP_xphctFMhir33IL51AB5JppCajWye-RTZrktgnX2969AiqpH9CnWiiOeSaOmsMCqMHCu38cS9dcpiTDcpLC8J8k-SdvfWXDQnythtZSj5t332BhdLcPNvoHQSQbm1Mj5QkWgsi7SJA80F70phHIY5Q7MASU2v61AWD9W6q3IO1KAa1q6C5r82HmQ362QZ56rsA4KuinL8i9kXPZd5fCW5eW8O4X0zU3OXfOBL0JF820ieavvOcLnR5SC26mJc2eSJcavgK0qm40

[^1]: 記事タグの設定がちゃんと反映されないバグっぽい挙動を見かけたが、そのうち直るだろう…