def-game 3.1.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,236 +1,55 @@
1
- # def-game
2
- - a framework for creating online board games
1
+ # def-game V5
3
2
 
4
- ---
3
+ ターン制ゲームのルールを通信・永続化から独立させる、TypeScript の最小 contract とメモリ上の simulator です。
5
4
 
6
- このフレームワークは、オンラインボードゲームを作成する際の開発を支援する。
7
- フレームワークは、開発者が定義するべき情報を明確に分類する。
8
-
9
- このフレームワークを使って、ゲームルールを定義した共通モジュールを作成する。
10
- その共通モジュールはサーバーサイドプロジェクトとクライアントサイドプロジェクトからimportすることを前提とする。
11
- 開発者は、サーバーサイド側のコードを修正する必要がない。
12
- 共通モジュールで定義したゲームロジックがそのまま動くようになっているからである。
13
- また、サーバークライアント間での型共有が容易になるのも利点の一つである。
14
-
15
- ここからは、このフレームワークが要求するデータ型や関数についての説明を行う。
16
- `固有の用語`と`データ型`については、インラインコードブロックで示す。
17
-
18
-
19
- # 設計 | design
20
-
21
- ## アウトライン
22
- このフレームワークが要求する関数のすべては、`GameRule`に定義されている。
23
- ```ts
24
- export interface GameRule<T extends Map, S extends Map, C extends ReqMap, L extends DefaultGameLogicState<S>, P extends GameParameters> {
25
- initialGameLogicState: (staticValues: any) => L;
26
- createRoom: (state: GameState<T, S, C, L, P>, param: GameParameters) => GameState<T, S, C, L, P>;
27
- onStartGame: (state: GameState<T, S, C, L, P>, joiner: Player) => GameState<T, S, C, L, P>;
28
- generateTasks(state: GameState<T, S, C, L, P>, event: ToServer<C>): Task<C, T>[];
29
- prioritizeTasks(newTasks: Task<C, T>[], gameState: GameState<T, S, C, L, P>): GameState<T, S, C, L, P>;
30
- doTasks(state: GameState<T, S, C, L, P>): GameState<T, S, C, L, P>;
31
- }
32
- ```
33
-
34
- ### initialGameLogicState
35
- `initialGameLogicState: (staticValues: any) => L;`
36
- `ゲームロジック状態`のデフォルトを作成する関数である。
37
- これはプレイヤーが参加する前に仮に作成される`ゲーム状態`である。
38
- 引数の`staticValues`は、静的な設定値のオブジェクトである。これはプログラム上にハードコードされたものである。
39
-
40
- ここで重要なのは、できる限り関数(計算)の中で`staticValues`から他の値を算出することである。
41
- これは、ランダムマップの生成や、ゲーム内の計算で使用される値などを算出することを念頭においている。
42
- 逆に`initialGameLogicState`の中でのハードコードは避け、`staticValues`にすべてを集約することも大切である。
43
-
44
- このようにして`ゲームロジック状態`のデフォルト`L`が作成される。これは`ルーム`が作成される前の段階である。
45
-
46
- ### createRoom
47
- `createRoom: (state: GameState<T, S, C, L, P>, param: GameParameters) => GameState<T, S, C, L, P>;`
48
- この関数は、`ルーム`が作成されるタイミングで実行される関数である。
49
- 開発者は、`GameParameters`を設定することができる。
50
- これは`ルーム`を作成する段階で、入力可能な設定値のことである。画面から入力されることを想定している。
51
-
52
- 例えば、プレイヤーの人数や制限時間、ゲームに固有なパラメータなどなんでも`ゲームロジック状態`に設定できる。
53
- そして、この関数は、`GameState`が持つ`GameParameters`を更新することを行う。
54
- これは、プレイヤーの待ち合わせ処理などの`ゲームロジック状態`とは分離して処理を行う場合を想定しており、`GameParameters`はそこで使用される。
55
-
56
- それに加えて、必要であれば、`ゲームロジック状態`に含まれる設定値などもこのタイミングで更新を行う。
57
- それらを全ての更新を行なった`GameState<T, S, C, L, P>`を返す。
58
-
59
- この段階で、`ルーム`に参加しているプレイヤーに対しては、UIを提供する必要があり、
60
- UIに関する情報は、`ゲームロジック状態`の中に保持することを推奨している。
61
-
62
- ### onStartGame
63
- `onStartGame: (state: GameState<T, S, C, L, P>, joiner: Player) => GameState<T, S, C, L, P>;`
64
- これは、プレイヤーの参加ごとに呼び出される関数である。
65
- プレイヤーの待ち合わせ処理を実装する。
66
- 参加したプレイヤーに対しては適切なUI情報を返す。
67
- この関数が実行されている段階ですでにWebSocket接続が確立されている。
68
-
69
-
70
- ### generateTasks | prioritizeTasks | doTasks
71
- この3つの関数は、ゲームの進行を定義するのに重要な関数となる。
72
- サーバーサイドプロジェクトにデフォルトで実装されている処理フローは、`src/simulatir.ts`で確認することができる。
73
- 以下の部分である。
74
- ```ts
75
- // ------ Game Logic ------
76
- const tasks = this.rules.generateTasks(state, action);
77
- const updatedQueueState = this.rules.prioritizeTasks(tasks, state);
78
- const newState = this.rules.doTasks(updatedQueueState);
79
- ```
80
-
81
- `タスク`というゲーム状態を更新する最小の単位となる概念を導入している。
82
- `ゲーム状態`は`タスクキュー`をもつ。
83
-
84
-
85
- 1. `generateTasks(state: GameState<T, S, C, L, P>, event: ToServer<C>): Task<C, T>[];`
86
-
87
- プレイヤーからの`イベント`を受け取った時に、実行されるのがこの関数である。
88
- この関数では`ゲーム状態`を更新しない。新しく発生する`タスク`の配列を返す。
89
-
90
- この関数では、`イベント`を受け取った際のバリデーションを実装することができる。
91
-
92
- 2. `prioritizeTasks(newTasks: Task<C, T>[], gameState: GameState<T, S, C, L, P>): GameState<T, S, C, L, P>;`
93
-
94
- この関数では、`ゲーム状態`が持つ`タスクキュー`と`generateTasks`により生成された新しい`タスク`の配列をマージする処理を実装する。
95
- `タスク`の優先度に応じて並び替える処理を実装できる。
96
-
97
- 3. `doTasks(state: GameState<T, S, C, L, P>): GameState<T, S, C, L, P>;`
98
-
99
- この関数は、`タスクキュー`をループして、一つ一つのタスクを実行する処理を記述する。
100
- ここで重要なのは、一つのタスクが`タスクリザルト`を返すことである。
101
-
102
- `タスクリザルト`は、次のタスクを実行するのか、一旦停止して処理を終えるのかと言った情報を持つことができる。
103
- この処理は、特にユーザー間のやり取りが発生する場合に有用である。
104
-
105
- - ユーザーA: request
106
- - タスク生成: (task1, task2, task3, task4)
107
- - task1はユーザーBからの応答を要請する
108
- - タスクの実行を一時中断: (task2, task3, task4)
109
- - ユーザーB: request
110
- - タスク生成: (task1B)
111
- - 優先度による並び替え: (task1B, task2, task3, task4)
112
- - タスクキューの実行再開
113
-
114
- また、より堅牢なシステムを作るために、次に期待されるタスクを`ゲーム状態`に持たせることが可能である。
115
- この例でいえば、"task1はユーザーBからの応答を要請する"際に、`タスクリザルト`は、タスクの実行を一時中断するだけでなく、次に期待されるタスクを"task1B"に設定する。
116
- これにより、再開時に、"task1B"以外の実行をキャンセルする。
117
-
118
-
119
- ### privaten
120
- `privaten(destinationId: PlayerId, l: L): L`
121
-
122
- `privaten(destinationId, gameLogicState): GameLogicState`は情報秘匿のための関数である。
123
- 基本的には、`ゲームロジック状態`が更新されるたびに、`ゲームロジック状態`をシリアライズしたものをクライアント側に通知する。
124
- クライアントから`ゲームロジック状態`の一部を隠さなければならない状況に対処するため、この関数を実装する。
125
-
126
- これはサーバーサイドに実装されている`broadcast`関数であり、各プレイヤーごとに`privaten`を呼び出す。
127
- ```ts
128
- async broadcast<T>(result: Game1State) {
129
- Object.keys(result.gameLogicState.forClient).forEach(async to => {
130
- await this.sendTo(to, JSON.stringify(gameRule.privaten(to, result.gameLogicState)));
131
- });
132
- }
5
+ ```sh
6
+ npm install def-game@^5
133
7
  ```
134
8
 
9
+ ## 公開 API
135
10
 
136
- ## 型による支援
11
+ - `GameDefinition`: 初期状態の生成、コマンドによる状態遷移、Actor 別の projection。
12
+ - `TransitionResult`: 成功時の次状態と Effect、または失敗理由。
13
+ - `CommandContext`: Actor と system の実行元の区別。
14
+ - `GameSimulator`: 同じゲーム定義へコマンドを順に流し、状態・View・Effect を検証する実行器。
137
15
 
138
- 規則的な命名と強い型付によって、型システムやAIによる補完の精度向上を狙っている。
16
+ 型の詳細と契約は [game-definition.ts](src/game-definition.ts)、実行器は [game-simulator.ts](src/game-simulator.ts) を参照してください。
17
+ [シナリオテスト](test/game-simulator.test.cjs) に、2人の入力待ちと system timeout を扱う実行例があります。
139
18
 
140
- `GameRule<T extends Map, S extends Map, C extends ReqMap, L extends DefaultGameLogicState<S>, P extends GameParameters>`
141
- を見てもらえれば、分かる通りいくつかのRecord型(`Map`)定義を要請している。
19
+ ## 責務
142
20
 
143
- ### `T extends Map`
144
- これは、`タスク`を定義するための`Map`である。
21
+ 各ゲームは GameState Command を定義し、1コマンドで次の外部入力を待てる安定状態まで遷移させます。入力状態は変更せず、失敗時はエラーだけを返します。現在の入力待ちはゲーム自身の状態として表現します。
145
22
 
146
- ```ts
147
- interface Tasks {
148
- "sample": SampleTask
149
- "update": UpdateTask
150
- }
151
- ```
152
- と言った具体である。
153
- タスクハンドラーは、`ゲームロジック状態`とこのタスク(例えば`SampleTask`型のデータ)を受け取り、`ゲームロジック状態`を更新する処理を行う。
154
-
155
- ### `S extends ResMap`
156
- これは、サーバーサイドから返却されるUI状態を定義するための`Map`である。
23
+ 接続、認証、永続化、時刻の取得、Alarm、外部 I/O は実行側の責務です。実行側が検証済みの Actor または system を context に指定します。リクエストに書かれた実行元をそのまま信用してはいけません。ゲーム側でも実行元と現在の状態に対してコマンドを検証します。
157
24
 
158
- ```ts
159
- interface Res {
160
- "updated": Notify<UpdatedRes>
161
- "accept": Require<AcceptRes>
162
- }
163
- ```
25
+ Effect は副作用の宣言です。本番の実行側は次状態を保存してから Effect を実行します。simulator は Effect を実行せず、遷移結果に含めて返します。
164
26
 
165
- `ResMap`は、値の型として、`Notify`か`Require`のいずれかでラップすることを要請する。
166
- これは、UI上で、ただの情報の通知を表すか、ユーザーに対してのなんらかのアクションの要求を表している。
167
- `Require`は、アクションに関する制限時間を持っており、ユーザーに伝えることが可能である。
27
+ projection は秘密情報を除いた Actor 別の View と availableActions を生成します。availableActions は表示補助であり、コマンド実行時の検証を置き換えません。
168
28
 
169
- ### `C extends ReqMap`
170
- これは、クライアントサイドからイベントは発生させるためのリクエストを定義するための`Map`である。
29
+ simulator getState は検証用の全状態への参照です。呼び出し側も状態を直接変更しないでください。deep clone、freeze、失敗時の rollback は行いません。クライアントへ公開する情報には getView を使用します。
171
30
 
172
- ```ts
173
- interface Req {
174
- "propose": Custom<ProposeReq>
175
- "accept": Simple<AcceptReq>
176
- }
177
- ```
178
- `ReqMap`は、値の型として、`Custom`か`Simple`のいずれかでラップすることを要請する。
179
- これはサーバーサイド側での実装を簡略化するために導入された型である。
180
-
181
- なんらかの`Req`を受け取ってから`Task[]`に変換する処理はしばしば冗長である。
182
- そこでリクエストペイロードをそのまま`タスク`として定義することができるようになっている。
183
- この例では、`Simple<AcceptReq>`のように`Simple`によってラップされたペイロード(`AcceptReq`)は、そのままタスクとしてキューに追加される。
184
-
185
- この仕組みは、以下の型定義により、型的にもサポートされている。
186
- ```ts
187
- export type TaskMapType<A extends ReqMap, B extends Map> = Merge<SimpleOnlyPayload<A>, CustomTaskMap<B>>;
188
- export type Task<A extends ReqMap, B extends Map> = {
189
- [K in keyof TaskMapType<A, B>]: {
190
- type: K;
191
- payload: TaskMapType<A, B>[K];
192
- }
193
- }[keyof TaskMapType<A, B>];
194
- ```
31
+ ## V4 からの移行
195
32
 
33
+ V5 は破壊的変更です。GameRule、GameEngine、TaskQueue に関する型、旧通信型と helper は削除しています。V4 を利用する既存ゲームは移植が完了するまで V4 を使用してください。
196
34
 
35
+ - 旧ライフサイクルを GameDefinition の初期化・コマンド処理・projection に移します。
36
+ - task queue に未来の処理を保存する代わりに、ゲーム固有の状態で現在の入力待ちを表現します。
37
+ - 参加・開始などもゲームの Command として扱います。
38
+ - 旧 GameEngine の代わりに GameSimulator でコマンド列を実行します。
197
39
 
40
+ Worker generator はこのリリースには含みません。Selfish の Worker 実装で共通部分を確認した後に追加する予定です。
198
41
 
42
+ ## 開発と公開
199
43
 
200
-
201
- ## その他の設計
202
-
203
- ### ルームへの接続
204
-
205
- - プラットフォームAPIにより認証されたユーザーは、ルームIDを指定して入室することができる。
206
- - このタイミングでroomKeyと呼ばれるJWTを返す。
207
- `{ playerId: playerId, roomId: body.roomId }`
208
- - クライアントは、そのJWTをWebSocket接続時のパラメータに加える。
209
- - これにより、接続とplayerIdが関連づけられる。
210
- - WebSocket接続を通じて送られる全てのJSONデータはplayerIdプロパティを持っている。
211
- - サーバーサイドは、そのplayerIdプロパティがWebSocket接続に紐づいたplayerIdと一致することを確認する。
212
-
213
-
214
- ### 時間制限
215
-
216
- `ルーム`に接続中のクライアントは、webSocketハートビートを定期的に送信する。
217
- これは、要求されたアクションの時間制限に関する確認処理をトリガーする。
218
-
219
- `gameState.nextTaskExpectation`は以下のような情報を持っていて、
220
- ```js
221
- {
222
- expectedTaskType: "accept",
223
- deadline: 1744589266,
224
- defaultTask: {
225
- playerId: "samplePlayer",
226
- proposerId: "proposer",
227
- isAccept: false,
228
- }
229
- }
44
+ ```sh
45
+ npm ci
46
+ npm run typecheck
47
+ npm test
48
+ npm pack
230
49
  ```
231
- deadlineを過ぎていれば、`defaultTask`が`タスクキュー`の先頭に配置され、実行が再開するという具体である。
232
-
233
-
234
50
 
51
+ 開発時のテストには Node.js 20 以降を使用してください。npm test はビルドと Node 標準のシナリオテストを実行します。
235
52
 
53
+ npm pack / npm publish の前に prepack で型チェック・ビルド・テストが実行されます。build は dist を作り直すため、削除した旧 API の生成物が混入しません。公開対象は dist、package.json、README、LICENSE です。
236
54
 
55
+ main にレビュー済みの変更を反映した後、公開する version と認証アカウントを確認し、検証済みのパッケージを npm publish で公開します。
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Worker や simulator が確定したコマンドの実行元。
3
+ * actorId は認証済みの主体を表し、接続 ID ではない。
4
+ * 外部リクエストの自己申告をそのまま使わず、system は信頼できる実行側だけが指定する。
5
+ */
6
+ export type CommandContext<ActorId> = {
7
+ readonly origin: "actor";
8
+ readonly actorId: ActorId;
9
+ } | {
10
+ readonly origin: "system";
11
+ };
12
+ /**
13
+ * 成功時は次の安定状態と副作用の宣言、失敗時はゲーム上の拒否理由を返す。
14
+ * 実行側は成功した状態を保存してから Effect を処理する。
15
+ */
16
+ export type TransitionResult<State, Effect, Error> = {
17
+ readonly ok: true;
18
+ readonly state: State;
19
+ readonly effects: readonly Effect[];
20
+ } | {
21
+ readonly ok: false;
22
+ readonly error: Error;
23
+ };
24
+ /**
25
+ * 通信・永続化から独立したゲームそのものの契約。
26
+ * State の構造や入力待ちの表現は各ゲームが定義し、kernel は task queue を要求しない。
27
+ */
28
+ export interface GameDefinition<State, Command, ActorId, View, Effect, Error> {
29
+ /** Session や接続の情報に依存しない、ゲームの初期状態を生成する。 */
30
+ createInitialState(): State;
31
+ /**
32
+ * 実行元とコマンドを再検証し、次の外部入力を待てる安定状態まで遷移する。
33
+ * 入力 state を変更せず、拒否時は error のみを返す。外部 I/O は行わない。
34
+ * system 起点でも、そのコマンドが現在の状態で有効かを検証する。
35
+ */
36
+ handleCommand(state: State, command: Command, context: CommandContext<ActorId>): TransitionResult<State, Effect, Error>;
37
+ /**
38
+ * state を変更せず、Actor に公開できる情報と availableActions を持つ View を生成する。
39
+ * availableActions は表示補助であり、コマンド実行時の検証を省略する根拠にはしない。
40
+ */
41
+ project(state: State, actorId: ActorId): View;
42
+ }
43
+ //# sourceMappingURL=game-definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-definition.d.ts","sourceRoot":"","sources":["../src/game-definition.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,cAAc,CAAC,OAAO,IAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEpC;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAC3C;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GACC;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;IACxE,0CAA0C;IAC1C,kBAAkB,IAAI,KAAK,CAAC;IAE5B;;;;OAIG;IACH,aAAa,CACT,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,GACjC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAE1C;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACjD"}
@@ -1,3 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- ;
@@ -0,0 +1,19 @@
1
+ import type { CommandContext, GameDefinition, TransitionResult } from "./game-definition";
2
+ /**
3
+ * Worker なしで同じゲーム定義にコマンドを順番に流す実行器。
4
+ * 状態はメモリだけに保存し、Effect は実行せず検証用に返す。
5
+ * ゲーム定義と呼び出し側は、受け取った状態を直接変更しない契約に従う。
6
+ */
7
+ export declare class GameSimulator<State, Command, ActorId, View, Effect, Error> {
8
+ private readonly definition;
9
+ private state;
10
+ /** ゲーム定義が生成した初期状態から、独立した試行を開始する。 */
11
+ constructor(definition: GameDefinition<State, Command, ActorId, View, Effect, Error>);
12
+ /** コマンドが成功した場合だけ次の状態を採用し、結果をそのまま返す。 */
13
+ executeCommand(command: Command, context: CommandContext<ActorId>): TransitionResult<State, Effect, Error>;
14
+ /** シナリオの検証用に全状態を参照する。クライアント配信用ではない。 */
15
+ getState(): State;
16
+ /** 現在の状態を指定 Actor 向けに投影する。 */
17
+ getView(actorId: ActorId): View;
18
+ }
19
+ //# sourceMappingURL=game-simulator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-simulator.d.ts","sourceRoot":"","sources":["../src/game-simulator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1F;;;;GAIG;AACH,qBAAa,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;IAK/D,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ/B,OAAO,CAAC,KAAK,CAAQ;IAErB,oCAAoC;gBAEf,UAAU,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC;IAK7F,uCAAuC;IACvC,cAAc,CACV,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,GACjC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC;IAQzC,uCAAuC;IACvC,QAAQ,IAAI,KAAK;IAIjB,8BAA8B;IAC9B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAGlC"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GameSimulator = void 0;
4
+ /**
5
+ * Worker なしで同じゲーム定義にコマンドを順番に流す実行器。
6
+ * 状態はメモリだけに保存し、Effect は実行せず検証用に返す。
7
+ * ゲーム定義と呼び出し側は、受け取った状態を直接変更しない契約に従う。
8
+ */
9
+ class GameSimulator {
10
+ /** ゲーム定義が生成した初期状態から、独立した試行を開始する。 */
11
+ constructor(definition) {
12
+ this.definition = definition;
13
+ this.state = definition.createInitialState();
14
+ }
15
+ /** コマンドが成功した場合だけ次の状態を採用し、結果をそのまま返す。 */
16
+ executeCommand(command, context) {
17
+ const result = this.definition.handleCommand(this.state, command, context);
18
+ if (result.ok) {
19
+ this.state = result.state;
20
+ }
21
+ return result;
22
+ }
23
+ /** シナリオの検証用に全状態を参照する。クライアント配信用ではない。 */
24
+ getState() {
25
+ return this.state;
26
+ }
27
+ /** 現在の状態を指定 Actor 向けに投影する。 */
28
+ getView(actorId) {
29
+ return this.definition.project(this.state, actorId);
30
+ }
31
+ }
32
+ exports.GameSimulator = GameSimulator;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from './types';
2
- export * from './simulator';
3
- export * from './helper';
1
+ export * from './game-definition';
2
+ export * from './game-simulator';
4
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -14,6 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./types"), exports);
18
- __exportStar(require("./simulator"), exports);
19
- __exportStar(require("./helper"), exports);
17
+ __exportStar(require("./game-definition"), exports);
18
+ __exportStar(require("./game-simulator"), exports);
package/package.json CHANGED
@@ -1,17 +1,26 @@
1
1
  {
2
2
  "name": "def-game",
3
- "version": "3.1.1",
4
- "description": "",
3
+ "version": "5.0.0",
4
+ "description": "Game definition contracts and an in-memory simulator for turn-based games",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
- "build": "tsc",
9
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc",
9
+ "typecheck": "tsc --noEmit",
10
+ "test": "npm run build && node --test test/*.test.cjs",
11
+ "prepack": "npm run typecheck && npm test"
10
12
  },
11
13
  "keywords": [],
12
14
  "author": "",
13
15
  "license": "ISC",
14
16
  "devDependencies": {
15
17
  "typescript": "^5.8.2"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/MFQWKMR4/def-game.git"
16
25
  }
17
- }
26
+ }
package/dist/example.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=example.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":""}
package/dist/example.js DELETED
@@ -1,104 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const helper_1 = require("./helper");
4
- // A equals to SimpleOnly<Req>
5
- const test = {
6
- type: "login",
7
- payload: (0, helper_1.custom)({
8
- playerId: "a",
9
- name: "b",
10
- }),
11
- };
12
- const customEventHander = (r) => {
13
- switch (r.type) {
14
- case "login":
15
- return [
16
- {
17
- type: "authorize",
18
- payload: {
19
- name: "a",
20
- password: "b",
21
- },
22
- },
23
- {
24
- type: "authenticate",
25
- payload: {
26
- name: "a",
27
- level: 1,
28
- },
29
- }
30
- ];
31
- case "complicatedReq":
32
- return [
33
- {
34
- type: "task1",
35
- payload: {
36
- name: "a",
37
- password: "b",
38
- },
39
- },
40
- {
41
- type: "task2",
42
- payload: {
43
- name: "a",
44
- level: 1,
45
- },
46
- },
47
- {
48
- type: "task3",
49
- payload: {
50
- name: "a",
51
- level: 1,
52
- },
53
- }
54
- ];
55
- default:
56
- return [];
57
- }
58
- };
59
- // no need to write process that handles simple requests
60
- const _sampleGenerateTasks = (0, helper_1.simplify)(customEventHander);
61
- // ------------------------------------------------------------
62
- // this is how you handle tasks
63
- // task cases are composed
64
- // - keyof SimpleOnlyPayload<Req>
65
- // - keyof Tasks
66
- const _switchTask = (task) => {
67
- switch (task.type) {
68
- case "move":
69
- return moveTaskHandler(task.payload);
70
- // login is a custom request, so we need to handle it differently
71
- case "authorize":
72
- return authorizeTaskHandler(task.payload);
73
- case "authenticate":
74
- return authenticateTaskHandler(task.payload);
75
- case "task1":
76
- return task1Handler(task.payload);
77
- case "task2":
78
- return task2Handler(task.payload);
79
- case "task3":
80
- return task3Handler(task.payload);
81
- default:
82
- const _exhaustiveCheck = task;
83
- console.log("unknown event", task);
84
- }
85
- };
86
- const moveTaskHandler = (payload) => {
87
- console.log(payload);
88
- };
89
- const authorizeTaskHandler = (payload) => {
90
- console.log(payload);
91
- };
92
- const authenticateTaskHandler = (payload) => {
93
- console.log(payload);
94
- };
95
- const task1Handler = (payload) => {
96
- console.log(payload);
97
- };
98
- const task2Handler = (payload) => {
99
- console.log(payload);
100
- };
101
- const task3Handler = (payload) => {
102
- console.log(payload);
103
- };
104
- // ------------------------------------------------------------
package/dist/helper.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import { Custom, CustomOnly, Notify, ReqMap, ReqPayload, Require, RequireSetting, Simple, Task, ToServer } from "./types";
2
- export declare const simple: <T extends ReqPayload>(payload: T) => Simple<T>;
3
- export declare const custom: <T extends ReqPayload>(payload: T) => Custom<T>;
4
- export declare const requiring: <T>(payload: T, setting: RequireSetting) => Require<T>;
5
- export declare const notifying: <T>(payload: T) => Notify<T>;
6
- export type CustomEventHander<A extends ReqMap, B extends Record<string, any>> = (r: ToServer<CustomOnly<A>>) => Task<A, B>[];
7
- export declare const simplify: <A extends ReqMap, B extends Record<string, any>>(h: CustomEventHander<A, B>) => (a: ToServer<A>) => Task<A, B>[];
8
- //# sourceMappingURL=helper.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAqB,IAAI,EAAe,QAAQ,EAAE,MAAM,SAAS,CAAA;AAEzJ,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,UAAU,EAAE,SAAS,CAAC,KAAG,MAAM,CAAC,CAAC,CAKjE,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,UAAU,EAAE,SAAS,CAAC,KAAG,MAAM,CAAC,CAAC,CAKjE,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS,cAAc,KAAG,OAAO,CAAC,CAAC,CAM3E,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,SAAS,CAAC,KAAG,MAAM,CAAC,CAAC,CAKjD,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IACvE,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;AAGlD,eAAO,MAAM,QAAQ,GAChB,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,MACvE,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAe3B,CAAA"}
package/dist/helper.js DELETED
@@ -1,49 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.simplify = exports.notifying = exports.requiring = exports.custom = exports.simple = void 0;
4
- const simple = (payload) => {
5
- return {
6
- kind: "simple",
7
- value: payload
8
- };
9
- };
10
- exports.simple = simple;
11
- const custom = (payload) => {
12
- return {
13
- kind: "custom",
14
- value: payload
15
- };
16
- };
17
- exports.custom = custom;
18
- const requiring = (payload, setting) => {
19
- return {
20
- kind: "require",
21
- value: payload,
22
- setting
23
- };
24
- };
25
- exports.requiring = requiring;
26
- const notifying = (payload) => {
27
- return {
28
- kind: "notify",
29
- value: payload
30
- };
31
- };
32
- exports.notifying = notifying;
33
- const simplify = (h) => (a) => {
34
- switch (a.payload.kind) {
35
- case "simple":
36
- const key = a.type;
37
- const payload = a.payload.value;
38
- return [
39
- {
40
- type: key,
41
- payload,
42
- }
43
- ];
44
- case "custom":
45
- const b = a;
46
- return h(b);
47
- }
48
- };
49
- exports.simplify = simplify;
@@ -1,12 +0,0 @@
1
- import { DefaultGameLogicState, GameParameters, GameRule, GameState, Player, ReqMap, ToServer } from "./types";
2
- export declare class GameEngine<T extends Record<string, any>, S extends Record<string, any>, C extends ReqMap, L extends DefaultGameLogicState<S>, P extends GameParameters> {
3
- private rules;
4
- private storage;
5
- constructor(rules: GameRule<T, S, C, L, P>);
6
- createRoom(p: P): void;
7
- join(player: Player): void;
8
- executeAction(action: ToServer<C>): GameState<T, S, C, L, P>;
9
- getState(): GameState<T, S, C, L, P>;
10
- setState(state: GameState<T, S, C, L, P>): void;
11
- }
12
- //# sourceMappingURL=simulator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"simulator.d.ts","sourceRoot":"","sources":["../src/simulator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAU,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGvH,qBAAa,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,cAAc;IAChK,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,OAAO,CAAkC;gBAErC,KAAK,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAa1C,UAAU,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI;IAMtB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM1B,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAc5D,QAAQ,IAAI,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAIpC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI;CAGlD"}
package/dist/simulator.js DELETED
@@ -1,58 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GameEngine = void 0;
4
- // This is a simulator
5
- class GameEngine {
6
- constructor(rules) {
7
- this.rules = rules;
8
- this.storage = new InMemoryGameStateStorage();
9
- // Initialize the game state
10
- const gameState = {
11
- gameParameters: null,
12
- gameLogicState: this.rules.initialGameLogicState(),
13
- taskQueue: []
14
- };
15
- this.storage.saveGameState(gameState);
16
- }
17
- createRoom(p) {
18
- const state = this.storage.loadGameState();
19
- const configured = this.rules.createRoom(state, p);
20
- this.storage.saveGameState(configured);
21
- }
22
- join(player) {
23
- const state = this.storage.loadGameState();
24
- const started = this.rules.onStartGame(state, player);
25
- this.storage.saveGameState(started);
26
- }
27
- executeAction(action) {
28
- const state = this.storage.loadGameState();
29
- // ------ Game Logic ------
30
- const tasks = this.rules.generateTasks(state, action);
31
- const updatedQueueState = this.rules.prioritizeTasks(tasks, state);
32
- const newState = this.rules.doTasks(updatedQueueState);
33
- // ------------------------
34
- this.storage.saveGameState(newState);
35
- return newState; // this means that this game state is broadcasted to all players
36
- }
37
- getState() {
38
- return this.storage.loadGameState();
39
- }
40
- setState(state) {
41
- this.storage.saveGameState(state);
42
- }
43
- }
44
- exports.GameEngine = GameEngine;
45
- class InMemoryGameStateStorage {
46
- constructor() {
47
- this.state = null;
48
- }
49
- loadGameState() {
50
- if (this.state === null) {
51
- throw new Error('Game state is not initialized');
52
- }
53
- return this.state;
54
- }
55
- saveGameState(state) {
56
- this.state = state;
57
- }
58
- }
package/dist/types.d.ts DELETED
@@ -1,122 +0,0 @@
1
- type Map = Record<string, any>;
2
- type Merge<A, B> = {
3
- [K in keyof A | keyof B]: K extends keyof B ? B[K] : K extends keyof A ? A[K] : never;
4
- };
5
- export interface ReqPayload {
6
- playerId: PlayerId;
7
- }
8
- export type Second = number;
9
- export type ReqMap = Record<string, Simple<ReqPayload> | Custom<ReqPayload>>;
10
- export interface RequireSetting {
11
- limitedTime: Second;
12
- }
13
- export type ResMap = Record<string, Require<any> | Notify<any>>;
14
- export type Simple<T extends ReqPayload> = {
15
- kind: "simple";
16
- value: T;
17
- };
18
- export type Custom<T extends ReqPayload> = {
19
- kind: "custom";
20
- value: T;
21
- };
22
- export type Require<T> = {
23
- kind: "require";
24
- value: T;
25
- setting: RequireSetting;
26
- };
27
- export type Notify<T> = {
28
- kind: "notify";
29
- value: T;
30
- };
31
- export type FromServerMap<T extends ResMap> = T;
32
- export type ToServerMap<T extends ReqMap> = T;
33
- export type CustomTaskMap<T extends Map> = T;
34
- export type ExtractSimplePayload<T> = T extends Simple<infer U> ? U : never;
35
- export type ExtractCustomPayload<T> = T extends Custom<infer U> ? U : never;
36
- export type SimpleOnlyPayload<T extends ReqMap> = {
37
- [K in keyof T as T[K] extends Simple<any> ? K : never]: ExtractSimplePayload<T[K]>;
38
- };
39
- export type CustomOnlyPayload<T extends ReqMap> = {
40
- [K in keyof T as T[K] extends Custom<any> ? K : never]: ExtractCustomPayload<T[K]>;
41
- };
42
- export type CustomOnly<T extends ReqMap> = {
43
- [K in keyof T as T[K] extends Custom<any> ? K : never]: T[K];
44
- };
45
- export type RequireOnly<T extends ReqMap> = {
46
- [K in keyof T as T[K] extends Require<any> ? K : never]: T[K];
47
- };
48
- export type NotifyOnly<T extends ReqMap> = {
49
- [K in keyof T as T[K] extends Notify<any> ? K : never]: T[K];
50
- };
51
- export type TaskMapType<A extends ReqMap, B extends Map> = Merge<SimpleOnlyPayload<A>, CustomTaskMap<B>>;
52
- export type Player = {
53
- id: PlayerId;
54
- name: string;
55
- };
56
- export type Joiner = {
57
- numberOfPlayers: number;
58
- enteredPlayers: Player[];
59
- };
60
- export interface GameParameters {
61
- owner: Player;
62
- joiner: Joiner;
63
- }
64
- export interface DefaultGameLogicState<S extends Map> {
65
- forClient: ForClient<S>;
66
- }
67
- export type FromServerDataType<T extends Map> = keyof FromServerMap<T>;
68
- export type ToServerDataType<T extends Map> = keyof ToServerMap<T>;
69
- export type FromServerData<T extends ResMap> = {
70
- [K in keyof T]: {
71
- type: K;
72
- payload: T[K];
73
- };
74
- }[keyof T];
75
- export type ToServerData<T extends ReqMap> = {
76
- [K in keyof T]: {
77
- type: K;
78
- payload: T[K];
79
- };
80
- }[keyof T];
81
- export type FromServer<S extends ResMap> = FromServerData<S>;
82
- export type ToServer<C extends ReqMap> = ToServerData<C>;
83
- export type ToServerWithNonStringKey<C extends ReqMap> = {
84
- [K in keyof C]: {
85
- type: K;
86
- payload: C[K];
87
- };
88
- }[keyof C];
89
- export type PlayerId = string;
90
- export type ForUI<S extends ResMap> = {
91
- requiredAction: FromServer<S> | null;
92
- notifications: FromServer<S>[];
93
- };
94
- export type ForClient<S extends Map> = {
95
- [key: PlayerId]: ForUI<S>;
96
- };
97
- export type Task<A extends ReqMap, B extends Map> = {
98
- [K in keyof TaskMapType<A, B>]: {
99
- type: K;
100
- payload: TaskMapType<A, B>[K];
101
- };
102
- }[keyof TaskMapType<A, B>];
103
- export interface TaskResult<S extends Map, L extends DefaultGameLogicState<S>> {
104
- type: string;
105
- state: L;
106
- }
107
- export type GameState<T extends Map, S extends Map, C extends ReqMap, L extends DefaultGameLogicState<S>, P extends GameParameters> = {
108
- gameLogicState: L;
109
- taskQueue: Task<C, T>[];
110
- gameParameters: P | null;
111
- };
112
- export interface GameRule<T extends Map, S extends Map, C extends ReqMap, L extends DefaultGameLogicState<S>, P extends GameParameters> {
113
- initialGameLogicState: () => L;
114
- createRoom: (state: GameState<T, S, C, L, P>, param: GameParameters) => GameState<T, S, C, L, P>;
115
- onStartGame: (state: GameState<T, S, C, L, P>, joiner: Player) => GameState<T, S, C, L, P>;
116
- generateTasks(state: GameState<T, S, C, L, P>, event: ToServer<C>): Task<C, T>[];
117
- prioritizeTasks(newTasks: Task<C, T>[], gameState: GameState<T, S, C, L, P>): GameState<T, S, C, L, P>;
118
- doTasks(state: GameState<T, S, C, L, P>): GameState<T, S, C, L, P>;
119
- privaten(destinationId: PlayerId, l: L): L;
120
- }
121
- export {};
122
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}