tl;dr
-
python
をつかってconsoleで動くoxゲームを作りました。ソフトウェア・アーキテクチャ構築練習のために、スケーラビリティ、コードの読みやすさ、使いやすさ を考慮して作成しています。 -
CPU vs CPU
,CPU vs Player
,Player vs Player
の3モードを制作しています。ゆくゆくはネットワークでの対戦機能も追加したり、Console以外でのinput / outputも追加してみたいと思います。 - ゲームとしては特に難しいロジックはないですが、コードを読みやすくすることや、モジュール同士の組み合わせなどに想定以上に時間がかかりました。ただ自分で満足いく形になった時はレゴブロックを完成させたような達成感を味わえました。
制作物
Publicにしているため、もしよければ、コードレビューいただけると大変嬉しいです。
https://github.com/ZawaPaP/TicTacToe
学んだこと
-
コード内にマジックナンバーは使わない
- マジックナンバーは何を意味しているのかわからない。
Enum
などを使うようにする
- マジックナンバーは何を意味しているのかわからない。
-
関数はBehaviorを表す
- 各関数をどのクラスに配置するべきかに悩まされた。一つ指標になりそうなのが、関数は
behavior
を表すということ。関数名を動詞にするというのと相まって、しっくり来た - クラス、インスタンス変数に直接アクセスするのではなく、関数を使ってアクセスする。以下は例だがクラスの定数を取得するだけの関数を作成して、外部モジュールからは関数を使用して、アクセスする。
- 各関数をどのクラスに配置するべきかに悩まされた。一つ指標になりそうなのが、関数は
from game_mark import GameMark
from typing import Tuple
class BoardCell:
def __init__(self) -> None:
self.mark = GameMark.EMPTY.value
class GameBoard:
ROW = 3
COLUMN = 3
def __init__(self) -> None:
self.board = [[BoardCell() for _ in range(self.column())] for _ in range(self.row())]
@staticmethod
def row() -> int:
return GameBoard.ROW
@staticmethod
def column() -> int:
return GameBoard.COLUMN
-
再帰について
- 再帰はコードを追いづらく、エラーハンドリングもしづらいため可能な限り使わない。
- ツリーやグラフなど親でも子でも帰ってくるデータが同じように見えるケースの場合のみ、使用を検討する
-
命名
- Handlerという名前をつけるなら、Handler内で処理を完結させ、Returnしないのが基本
-
Enum
- Enumとは Enum(列挙型)は、プログラミングにおいて特定の値の集合を表現するためのデータ型です。Enumは、あるデータが特定の値の中から選ばれることを保証するために使用する。
-
IOとIO Controllerを分ける意味は?
- IOはデータの入出力や外部リソースの操作に関する責任を持ち、IOコントローラモジュールはデータの制御やビジネスロジックに関する責任を持つ。これにより、コードの再利用性やテスト容易性が向上し、各モジュールの責務が明確になる。
-
@staticmethod
- 静的メソッドは、クラスの中で共有のユーティリティ関数や、インスタンスに依存しない処理を実装する場合に便利。インスタンスの状態にアクセスする必要がない場合や、外部の依存関係を持たない単純な処理を実行する場合に使用。
-
try-exceptについて
- 予期できるエラーは例外処理をすべき。予期できないエラーは異なる処理をしてexitすべき。例外処理は低い階層ではなく、controllerなどの層で制御を行う。そうすることにより例外発生した際の処理をまとめて書きやすくなる。