[前記事 1.全体設計と見積もり] (https://qiita.com/miyatahirotaka/items/9324abfe5fc2055e2e96)
これをTypeScript(クライアント側) + WebSocket(通信プロトコル) + Go(サーバー側)で作ります。
コーディング前の準備のために、設計書に落とし込みます。
データ構造
決めなければいけないことが多いが、まずはGoサーバー側が持つデータ構造から考えることにする。
最上層Systemの中に[]Roomがあり、Roomの中に1P,2PのPlayerが存在。
PlayerはPuyoの6*13配列を持つFieldなどを持つ構造。
自分のフィールド上部に表示される、降り注ぐ予定の予告おじゃまぷよ数をPlayerOjama.Count
予告おじゃまぷよが見えてるだけの状態(=相手が連鎖の最中)か、実際に降り注ぐ準備が完了した(=相手の連鎖が完了)かの状態をPlayerOjama.OjamaStatusで表現する。
以下のようになった。
package main
type Room struct {
id string
status RoomStatus
player1p Player
player2p Player
}
type RoomStatus int
// 定数名の重複を避けるために接頭語Roomをつける
const (
RoomBeforeMatch RoomStatus = iota // ルームが対戦前状態
RoomInMatch // ルームが対戦中状態
RoomAfterMatch // ルームが対戦後状態
)
type Player struct {
id string
status PlayerStatus
field [6][13]Puyo
score int
ojamas []PlayerOjama
}
type PlayerStatus int
const (
InRobby PlayerStatus = iota // ロピーにいる状態
WaitingInRoom // ルームに自分だけがいる対戦相手待ち状態
Ready // 対戦開始直前状態
EnableToOperate // 対戦中、かつぷよのツモを操作できる状態
IsBlockedByChaining // 対戦中、かつ連鎖中のため、自分のぷよのツモを動かすことができない状態
AfterMatch // 対戦後状態
)
type Puyo int
const (
None Puyo = iota
Ojama // おじゃまぷよ
Red
Yellow
Blue
Green
)
type PlayerOjama struct {
Count int
Status OjamaStatus
}
type OjamaStatus int
const (
Unfixed OjamaStatus = iota
Fixed
)
Goにはenumがないためconstとiotaを使ってenum風の実装をするのがよくあるテクニックらしい。
始めRoomStatus.AfterMatchとPlayerStatus.AfterMatchが重複してエラーになったため、違うenumでも重複するメンバ名は使えないことを学んだ。
シーケンス図
次にシーケンス図を書き、サーバー/クライアント側にそれぞれ具体的にどのような処理が必要かを詰めていく。
https://sequencediagram.org/ を使い書いていく。
対戦中のシーケンス図が重いので、対戦前後と対戦中の2つのシーケンス図に分けて書く。
対戦前後のシーケンス図
対戦中のシーケンス図
長い・・・が完全に書こうとするとまだ抜けが多いと思う。でももうモチベと頭のキャパシティが限界なのでこれくらいにする。
2割くらい抜けがありそうだが実装のサポートに十分なるレベルだと思う。
状態遷移図
シーケンス図の中にも登場した、RoomやPlayerのstatus値について整理する。
サクっと使えるツールが見つけられず、Googleスライドで作成。
対戦ルーム Room のステータス遷移図
プレイヤー Player のステータス遷移図
当然この設計書は完璧ではなく、実装時にアドリブで何かしなきゃいけなくなることは起こりえる想定。8割ぐらいできてるし、個人開発なのでまあいいやろうということで次のフェーズに進む。
ここまで工数10時間。
次は実装に着手する。