こんにちは。
前回の記事では、このプロジェクトで使用している開発環境を紹介しました。
今回は、実際にプログラムを書き始める前に考えた「設計」について紹介します。
プログラムは動けばそれで終わり、というものではありません。あとから機能を追加したり、不具合を修正したりすることを考えると、最初にどのような構成で作るかがとても重要になります。
もちろん、この設計も最初から完璧だったわけではありません。開発を進める中で何度も見直しながら、現在の形に落ち着いています。
このプロジェクトで実現したいこと
まずは、このアプリに必要な機能を整理しました。
- 手牌を認識する
- CPUが何を切るか考える
- CPUの打牌を画面に表示する
- プレイヤーの捨て牌を記録する
- 局の情報を管理する
- リーチやドラなどの状態を管理する
一つひとつはシンプルですが、すべてを一つのプログラムで管理しようとすると、あとから修正するのが大変になってしまいます。
そこで、「役割ごとにプログラムを分ける」という方針で設計することにしました。
全体構成

現在は、次のような構成を考えています。
- GUI
- GameManager
- MahjongDecision
- CameraWorker
- HaiDetection
それぞれが担当する役割を明確にし、お互いに必要以上の情報を持たないように設計しています。
GUIは「表示するだけ」

GUIは、ユーザーが実際に操作する画面です。
- 対局設定
- 局設定
- 手牌表示
- CPUが選んだ打牌の表示
- プレイヤーの捨て牌表示
などを担当します。
ここで一番意識したのは、GUIのプログラムに麻雀のルールを書かないことです。
GUIの役割は、「画面を表示すること」と「ユーザーの操作を受け取ること」。
麻雀のルールやCPUの思考は、別のクラスに任せています。
GameManagerは司令塔

GameManagerは、このプロジェクトの中心となるクラスです。
GUIや画像認識から受け取った情報を管理し、
- 現在の局
- 手牌
- プレイヤー情報
- 捨て牌
- リーチ状態
などをまとめています。
GUIとCPU、画像認識の橋渡しをする存在なので、「司令塔」のような役割です。
MahjongDecisionはCPUの頭脳

CPUが実際に何を切るかを考えるのがMahjongDecisionです。
ここでは、
- 何を切るか
- リーチするか
- 鳴くか(今後実装予定)
などを判断します。
逆に、画面のことや画像認識のことは一切知りません。
現在の局面だけ渡されれば、麻雀だけを考えることができるようになっています。
CameraWorkerとHaiDetection

画像認識も役割を分けています。
CameraWorkerはWebカメラから映像を取得する担当です。
HaiDetectionは、その映像から手牌を検出し、何の牌なのかを判定します。
現在は手牌認識用のカメラのみを使用しています。
今後は卓全体を認識するためのカメラを追加し、捨て牌や鳴き牌なども自動で認識できるような構成を検討しています。
クラス同士をできるだけ独立させる

今回の設計で一番意識したのが、「それぞれのクラスが余計なことを知らないようにする」ことです。
例えば、GUIから直接CPUを呼び出してしまうと、CPUの仕様を変更しただけでGUIまで修正する必要が出てきます。
そのため、
GUI → GameManager(管理クラス) → MahjongDecision(捨て牌選択などの麻雀CPU)
という流れにすることで、それぞれの役割を明確にしました。
これならCPUを改良してもGUIにはほとんど影響がなく、画像認識を変更しても麻雀ロジックを書き換える必要がありません。
設計は何度も作り直しました
実は、この設計は最初から決まっていたわけではありません。
最初はGUIから直接画像認識を呼び出したり、CPUの処理を書いたりしていました。
しかし、機能が増えてくると、
「このクラスの役割が多すぎる」
「あとから修正しづらい」
という問題が次々と出てきました。
そこで、少しずつ役割を分けながら現在の構成へ改善してきました。
私の開発スタイル
個人開発では人それぞれ進め方があると思いますが、私は最初から細かく設計することはあまりしません。
まずは多少エラーがあっても、多少動作がおかしくても構わないので、「とにかく動くもの」を作ることを優先しています。
もちろん、効率だけを考えれば最初からしっかり設計した方が良いのかもしれません。
それでも、実際に動くものができると達成感がありますし、何より実際に触ってみないと「どう作れば使いやすいのか」が見えてきません。
そのため、
- まずは動くものを作る
- 実際に使ってみる
- 問題点を洗い出す
- その結果をもとに設計を見直す
という流れで開発を進めています。
今回の麻雀CPUも、この流れで何度も設計を作り直してきました。
最初から完璧な設計を目指すよりも、動くものを作りながら少しずつ改善していく方が、自分には合っていると感じています。
次回
次回からはいよいよ実装編です。
まずは牌検出から作り始めます。
どのように牌の検出・認識を実現したのかを紹介しながら、実際の開発の様子をお届けしたいと思います。
おまけ
この記事では全体の設計について紹介しましたが、各技術については別の記事で詳しく紹介する予定です。
例えば、
- PyQt6で画面を作る方法
- YOLOで麻雀牌を検出する方法
- Pythonで画像分類モデルを学習する方法
- OpenCVでリアルタイム認識を行う方法
など、技術ごとに詳しく解説していきます。
シリーズ記事では「どのように開発を進めたか」を、技術記事では「どのように実装したか」を紹介しながら、両方楽しめるブログを目指していきたいと思います。



コメント