turn-based-mcp-shared 1.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/dist/constants/game-constants.d.ts +77 -0
- package/dist/constants/game-constants.d.ts.map +1 -0
- package/dist/constants/game-constants.js +68 -0
- package/dist/constants/game-constants.js.map +1 -0
- package/dist/constants/index.d.ts +5 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +5 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/games/index.d.ts +3 -0
- package/dist/games/index.d.ts.map +1 -0
- package/dist/games/index.js +3 -0
- package/dist/games/index.js.map +1 -0
- package/dist/games/rock-paper-scissors.d.ts +117 -0
- package/dist/games/rock-paper-scissors.d.ts.map +1 -0
- package/dist/games/rock-paper-scissors.js +247 -0
- package/dist/games/rock-paper-scissors.js.map +1 -0
- package/dist/games/tic-tac-toe.d.ts +121 -0
- package/dist/games/tic-tac-toe.d.ts.map +1 -0
- package/dist/games/tic-tac-toe.js +240 -0
- package/dist/games/tic-tac-toe.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/game-storage.d.ts +11 -0
- package/dist/storage/game-storage.d.ts.map +1 -0
- package/dist/storage/game-storage.js +28 -0
- package/dist/storage/game-storage.js.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/mcp-api-client.d.ts +8 -0
- package/dist/storage/mcp-api-client.d.ts.map +1 -0
- package/dist/storage/mcp-api-client.js +52 -0
- package/dist/storage/mcp-api-client.js.map +1 -0
- package/dist/storage/memory-storage.d.ts +12 -0
- package/dist/storage/memory-storage.d.ts.map +1 -0
- package/dist/storage/memory-storage.js +34 -0
- package/dist/storage/memory-storage.js.map +1 -0
- package/dist/storage/sqlite-storage.d.ts +2 -0
- package/dist/storage/sqlite-storage.d.ts.map +1 -0
- package/dist/storage/sqlite-storage.js +3 -0
- package/dist/storage/sqlite-storage.js.map +1 -0
- package/dist/testing/api-test-utils.d.ts +56 -0
- package/dist/testing/api-test-utils.d.ts.map +1 -0
- package/dist/testing/api-test-utils.js +125 -0
- package/dist/testing/api-test-utils.js.map +1 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +10 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/test-database.d.ts +24 -0
- package/dist/testing/test-database.d.ts.map +1 -0
- package/dist/testing/test-database.js +39 -0
- package/dist/testing/test-database.js.map +1 -0
- package/dist/testing/vitest-setup.d.ts +12 -0
- package/dist/testing/vitest-setup.d.ts.map +1 -0
- package/dist/testing/vitest-setup.js +35 -0
- package/dist/testing/vitest-setup.js.map +1 -0
- package/dist/types/game.d.ts +39 -0
- package/dist/types/game.d.ts.map +1 -0
- package/dist/types/game.js +2 -0
- package/dist/types/game.js.map +1 -0
- package/dist/types/games.d.ts +26 -0
- package/dist/types/games.d.ts.map +1 -0
- package/dist/types/games.js +2 -0
- package/dist/types/games.js.map +1 -0
- package/dist/utils/http-client.d.ts +25 -0
- package/dist/utils/http-client.d.ts.map +1 -0
- package/dist/utils/http-client.js +42 -0
- package/dist/utils/http-client.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +34 -0
- package/dist/utils/index.js.map +1 -0
- package/eslint.config.js +41 -0
- package/package.json +1 -0
- package/src/constants/game-constants.test.ts +102 -0
- package/src/constants/game-constants.ts +86 -0
- package/src/constants/index.ts +5 -0
- package/src/games/index.test.ts +22 -0
- package/src/games/index.ts +2 -0
- package/src/games/rock-paper-scissors.test.ts +313 -0
- package/src/games/rock-paper-scissors.ts +276 -0
- package/src/games/tic-tac-toe.test.ts +258 -0
- package/src/games/tic-tac-toe.ts +267 -0
- package/src/index.test.ts +64 -0
- package/src/index.ts +6 -0
- package/src/storage/game-storage.test.ts +204 -0
- package/src/storage/game-storage.ts +38 -0
- package/src/storage/index.test.ts +48 -0
- package/src/storage/index.ts +2 -0
- package/src/storage/mcp-api-client.test.ts +339 -0
- package/src/storage/mcp-api-client.ts +53 -0
- package/src/storage/memory-storage.ts +46 -0
- package/src/storage/sqlite-storage.test.ts +139 -0
- package/src/storage/sqlite-storage.ts +12 -0
- package/src/testing/api-test-utils.ts +144 -0
- package/src/testing/index.ts +29 -0
- package/src/testing/test-database.ts +46 -0
- package/src/testing/vitest-setup.ts +35 -0
- package/src/types/game.ts +48 -0
- package/src/types/games.ts +33 -0
- package/src/utils/http-client.ts +44 -0
- package/src/utils/index.test.ts +133 -0
- package/src/utils/index.ts +40 -0
- package/tsconfig-paths.json +12 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +21 -0
- package/vitest.setup.ts +10 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared game constants
|
|
3
|
+
* Single source of truth for game types, difficulties, player IDs, and default values
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Supported game types
|
|
7
|
+
*/
|
|
8
|
+
export declare const GAME_TYPES: readonly ["tic-tac-toe", "rock-paper-scissors"];
|
|
9
|
+
/**
|
|
10
|
+
* Available AI difficulty levels
|
|
11
|
+
*/
|
|
12
|
+
export declare const DIFFICULTIES: readonly ["easy", "medium", "hard"];
|
|
13
|
+
/**
|
|
14
|
+
* Standard player IDs used across the system
|
|
15
|
+
*/
|
|
16
|
+
export declare const PLAYER_IDS: {
|
|
17
|
+
readonly HUMAN: "player1";
|
|
18
|
+
readonly PLAYER2: "player2";
|
|
19
|
+
readonly AI: "ai";
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Game status values
|
|
23
|
+
*/
|
|
24
|
+
export declare const GAME_STATUSES: readonly ["waiting", "playing", "finished"];
|
|
25
|
+
/**
|
|
26
|
+
* Derive types from constants
|
|
27
|
+
*/
|
|
28
|
+
export type GameType = typeof GAME_TYPES[number];
|
|
29
|
+
export type Difficulty = typeof DIFFICULTIES[number];
|
|
30
|
+
export type PlayerId = typeof PLAYER_IDS[keyof typeof PLAYER_IDS];
|
|
31
|
+
export type GameStatus = typeof GAME_STATUSES[number];
|
|
32
|
+
/**
|
|
33
|
+
* Default player configurations
|
|
34
|
+
*/
|
|
35
|
+
export declare const DEFAULT_PLAYER_NAME = "Player";
|
|
36
|
+
export declare const DEFAULT_AI_DIFFICULTY: Difficulty;
|
|
37
|
+
/**
|
|
38
|
+
* Difficulty display configuration
|
|
39
|
+
*/
|
|
40
|
+
export declare const DIFFICULTY_DISPLAY: {
|
|
41
|
+
readonly easy: {
|
|
42
|
+
readonly emoji: "😌";
|
|
43
|
+
readonly label: "Easy";
|
|
44
|
+
};
|
|
45
|
+
readonly medium: {
|
|
46
|
+
readonly emoji: "🎯";
|
|
47
|
+
readonly label: "Medium";
|
|
48
|
+
};
|
|
49
|
+
readonly hard: {
|
|
50
|
+
readonly emoji: "🔥";
|
|
51
|
+
readonly label: "Hard";
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Type guard to check if a string is a supported game type
|
|
56
|
+
*/
|
|
57
|
+
export declare function isSupportedGameType(gameType: string): gameType is GameType;
|
|
58
|
+
/**
|
|
59
|
+
* Type guard to check if a string is a valid difficulty level
|
|
60
|
+
*/
|
|
61
|
+
export declare function isValidDifficulty(difficulty: string): difficulty is Difficulty;
|
|
62
|
+
/**
|
|
63
|
+
* Type guard to check if a string is a valid player ID
|
|
64
|
+
*/
|
|
65
|
+
export declare function isValidPlayerId(playerId: string): playerId is PlayerId;
|
|
66
|
+
/**
|
|
67
|
+
* Type guard to check if a string is a valid game status
|
|
68
|
+
*/
|
|
69
|
+
export declare function isValidGameStatus(status: string): status is GameStatus;
|
|
70
|
+
/**
|
|
71
|
+
* Get difficulty display configuration
|
|
72
|
+
*/
|
|
73
|
+
export declare function getDifficultyDisplay(difficulty: Difficulty): {
|
|
74
|
+
emoji: string;
|
|
75
|
+
label: string;
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=game-constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-constants.d.ts","sourceRoot":"","sources":["../../src/constants/game-constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU,iDAAkD,CAAA;AAEzE;;GAEG;AACH,eAAO,MAAM,YAAY,qCAAsC,CAAA;AAE/D;;GAEG;AACH,eAAO,MAAM,UAAU;;;;CAIb,CAAA;AAEV;;GAEG;AACH,eAAO,MAAM,aAAa,6CAA8C,CAAA;AAExE;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAA;AAChD,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAA;AACpD,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC,MAAM,OAAO,UAAU,CAAC,CAAA;AACjE,MAAM,MAAM,UAAU,GAAG,OAAO,aAAa,CAAC,MAAM,CAAC,CAAA;AAErD;;GAEG;AACH,eAAO,MAAM,mBAAmB,WAAW,CAAA;AAC3C,eAAO,MAAM,qBAAqB,EAAE,UAAqB,CAAA;AAEzD;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;CAIrB,CAAA;AAEV;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,QAAQ,CAE1E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,IAAI,UAAU,CAE9E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,QAAQ,CAEtE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAE7F"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared game constants
|
|
3
|
+
* Single source of truth for game types, difficulties, player IDs, and default values
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Supported game types
|
|
7
|
+
*/
|
|
8
|
+
export const GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'];
|
|
9
|
+
/**
|
|
10
|
+
* Available AI difficulty levels
|
|
11
|
+
*/
|
|
12
|
+
export const DIFFICULTIES = ['easy', 'medium', 'hard'];
|
|
13
|
+
/**
|
|
14
|
+
* Standard player IDs used across the system
|
|
15
|
+
*/
|
|
16
|
+
export const PLAYER_IDS = {
|
|
17
|
+
HUMAN: 'player1',
|
|
18
|
+
PLAYER2: 'player2',
|
|
19
|
+
AI: 'ai'
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Game status values
|
|
23
|
+
*/
|
|
24
|
+
export const GAME_STATUSES = ['waiting', 'playing', 'finished'];
|
|
25
|
+
/**
|
|
26
|
+
* Default player configurations
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_PLAYER_NAME = 'Player';
|
|
29
|
+
export const DEFAULT_AI_DIFFICULTY = 'medium';
|
|
30
|
+
/**
|
|
31
|
+
* Difficulty display configuration
|
|
32
|
+
*/
|
|
33
|
+
export const DIFFICULTY_DISPLAY = {
|
|
34
|
+
easy: { emoji: '😌', label: 'Easy' },
|
|
35
|
+
medium: { emoji: '🎯', label: 'Medium' },
|
|
36
|
+
hard: { emoji: '🔥', label: 'Hard' }
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Type guard to check if a string is a supported game type
|
|
40
|
+
*/
|
|
41
|
+
export function isSupportedGameType(gameType) {
|
|
42
|
+
return GAME_TYPES.includes(gameType);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Type guard to check if a string is a valid difficulty level
|
|
46
|
+
*/
|
|
47
|
+
export function isValidDifficulty(difficulty) {
|
|
48
|
+
return DIFFICULTIES.includes(difficulty);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Type guard to check if a string is a valid player ID
|
|
52
|
+
*/
|
|
53
|
+
export function isValidPlayerId(playerId) {
|
|
54
|
+
return Object.values(PLAYER_IDS).includes(playerId);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Type guard to check if a string is a valid game status
|
|
58
|
+
*/
|
|
59
|
+
export function isValidGameStatus(status) {
|
|
60
|
+
return GAME_STATUSES.includes(status);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get difficulty display configuration
|
|
64
|
+
*/
|
|
65
|
+
export function getDifficultyDisplay(difficulty) {
|
|
66
|
+
return DIFFICULTY_DISPLAY[difficulty];
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=game-constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-constants.js","sourceRoot":"","sources":["../../src/constants/game-constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,aAAa,EAAE,qBAAqB,CAAU,CAAA;AAEzE;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAA;AAE/D;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,EAAE,EAAE,IAAI;CACA,CAAA;AAEV;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,CAAU,CAAA;AAUxE;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAA;AAC3C,MAAM,CAAC,MAAM,qBAAqB,GAAe,QAAQ,CAAA;AAEzD;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;IACpC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IACxC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE;CAC5B,CAAA;AAEV;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,QAAoB,CAAC,CAAA;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,OAAO,YAAY,CAAC,QAAQ,CAAC,UAAwB,CAAC,CAAA;AACxD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAoB,CAAC,CAAA;AACjE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,aAAa,CAAC,QAAQ,CAAC,MAAoB,CAAC,CAAA;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAsB;IACzD,OAAO,kBAAkB,CAAC,UAAU,CAAC,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,kBAAkB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Game, Player, PlayerId, GameResult } from '../types/game.js';
|
|
2
|
+
import { RPSGameState, RPSMove } from '../types/games.js';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of the classic Rock Paper Scissors game
|
|
5
|
+
*
|
|
6
|
+
* A best-of-N rounds game where players simultaneously choose rock, paper, or scissors.
|
|
7
|
+
* Rock beats scissors, scissors beats paper, paper beats rock.
|
|
8
|
+
* The first player to win the majority of rounds wins the match.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const game = new RockPaperScissorsGame();
|
|
13
|
+
* const players = [
|
|
14
|
+
* { id: 'player1', name: 'Human', isAI: false },
|
|
15
|
+
* { id: 'ai', name: 'Computer', isAI: true }
|
|
16
|
+
* ];
|
|
17
|
+
* const initialState = game.getInitialState(players); // Creates best-of-3 game
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class RockPaperScissorsGame implements Game<RPSGameState, RPSMove> {
|
|
21
|
+
/**
|
|
22
|
+
* Validates whether a move is legal in the current game state
|
|
23
|
+
*
|
|
24
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
25
|
+
* @param move - The move to validate (choice of rock, paper, or scissors)
|
|
26
|
+
* @param playerId - The ID of the player attempting the move
|
|
27
|
+
* @returns true if the move is valid, false otherwise
|
|
28
|
+
*
|
|
29
|
+
* @description
|
|
30
|
+
* A move is valid if:
|
|
31
|
+
* - The choice is one of: 'rock', 'paper', 'scissors'
|
|
32
|
+
* - The game status is 'playing'
|
|
33
|
+
* - The current round hasn't exceeded max rounds
|
|
34
|
+
* - The player hasn't already made a choice in the current round
|
|
35
|
+
*/
|
|
36
|
+
validateMove(gameState: RPSGameState, move: RPSMove, playerId: PlayerId): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Applies a validated move to the game state and returns the new state
|
|
39
|
+
*
|
|
40
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
41
|
+
* @param move - The move to apply (must be valid)
|
|
42
|
+
* @param playerId - The ID of the player making the move
|
|
43
|
+
* @returns A new game state with the move applied
|
|
44
|
+
* @throws Error if the move is invalid
|
|
45
|
+
*
|
|
46
|
+
* @description
|
|
47
|
+
* This method handles both partial and complete rounds:
|
|
48
|
+
* - If first player choice: records choice and switches to other player
|
|
49
|
+
* - If second player choice: records choice, determines round winner,
|
|
50
|
+
* updates scores, and advances to next round or ends game
|
|
51
|
+
*/
|
|
52
|
+
applyMove(gameState: RPSGameState, move: RPSMove, playerId: PlayerId): RPSGameState;
|
|
53
|
+
/**
|
|
54
|
+
* Checks if the game has ended and determines the winner
|
|
55
|
+
*
|
|
56
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
57
|
+
* @returns GameResult with winner and reason if game ended, null if game continues
|
|
58
|
+
*
|
|
59
|
+
* @description
|
|
60
|
+
* The game ends when all rounds are complete (currentRound >= maxRounds).
|
|
61
|
+
* Winner is determined by comparing final scores:
|
|
62
|
+
* - Player with higher score wins
|
|
63
|
+
* - Equal scores result in a draw
|
|
64
|
+
* - Reason includes the final score (e.g., "Won 2-1")
|
|
65
|
+
*/
|
|
66
|
+
checkGameEnd(gameState: RPSGameState): GameResult | null;
|
|
67
|
+
/**
|
|
68
|
+
* Gets all valid moves for a player in the current game state
|
|
69
|
+
*
|
|
70
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
71
|
+
* @param playerId - The ID of the player to get moves for
|
|
72
|
+
* @returns Array of valid RPSMove objects (all choices if player can move)
|
|
73
|
+
*
|
|
74
|
+
* @description
|
|
75
|
+
* Returns all three choices [rock, paper, scissors] if:
|
|
76
|
+
* - Game is still playing
|
|
77
|
+
* - Current round is within max rounds
|
|
78
|
+
* - Player hasn't made a choice in the current round
|
|
79
|
+
* Otherwise returns an empty array.
|
|
80
|
+
*/
|
|
81
|
+
getValidMoves(gameState: RPSGameState, playerId: PlayerId): RPSMove[];
|
|
82
|
+
/**
|
|
83
|
+
* Creates the initial game state for a new rock-paper-scissors game
|
|
84
|
+
*
|
|
85
|
+
* @param players - Array of exactly 2 players
|
|
86
|
+
* @param options - Optional configuration including maxRounds
|
|
87
|
+
* @returns Initial RPSGameState set up for a configurable number of rounds
|
|
88
|
+
*
|
|
89
|
+
* @description
|
|
90
|
+
* Sets up a new game with:
|
|
91
|
+
* - Configurable number of rounds (default: 3 for best-of-3 format)
|
|
92
|
+
* - All scores initialized to 0
|
|
93
|
+
* - First player goes first
|
|
94
|
+
* - Game status set to 'playing'
|
|
95
|
+
* - Current round set to 0
|
|
96
|
+
*/
|
|
97
|
+
getInitialState(players: Player[], options?: {
|
|
98
|
+
maxRounds?: number;
|
|
99
|
+
}): RPSGameState;
|
|
100
|
+
/**
|
|
101
|
+
* Determines the winner of a single round based on the classic RPS rules
|
|
102
|
+
*
|
|
103
|
+
* @param choice1 - First player's choice
|
|
104
|
+
* @param choice2 - Second player's choice
|
|
105
|
+
* @returns 'player1' if choice1 wins, 'player2' if choice2 wins, 'draw' if same
|
|
106
|
+
*
|
|
107
|
+
* @private
|
|
108
|
+
* @description
|
|
109
|
+
* Implements the classic rules:
|
|
110
|
+
* - Rock beats Scissors
|
|
111
|
+
* - Paper beats Rock
|
|
112
|
+
* - Scissors beats Paper
|
|
113
|
+
* - Same choices result in a draw
|
|
114
|
+
*/
|
|
115
|
+
private determineRoundWinner;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=rock-paper-scissors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rock-paper-scissors.d.ts","sourceRoot":"","sources":["../../src/games/rock-paper-scissors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAa,MAAM,gBAAgB,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAsB,YAAW,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC;IACvE;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO;IA4BjF;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,YAAY;IAsDnF;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,SAAS,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI;IA2BxD;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,EAAE;IAsBrE;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY;IAuBlF;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;CAa7B"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementation of the classic Rock Paper Scissors game
|
|
3
|
+
*
|
|
4
|
+
* A best-of-N rounds game where players simultaneously choose rock, paper, or scissors.
|
|
5
|
+
* Rock beats scissors, scissors beats paper, paper beats rock.
|
|
6
|
+
* The first player to win the majority of rounds wins the match.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const game = new RockPaperScissorsGame();
|
|
11
|
+
* const players = [
|
|
12
|
+
* { id: 'player1', name: 'Human', isAI: false },
|
|
13
|
+
* { id: 'ai', name: 'Computer', isAI: true }
|
|
14
|
+
* ];
|
|
15
|
+
* const initialState = game.getInitialState(players); // Creates best-of-3 game
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export class RockPaperScissorsGame {
|
|
19
|
+
/**
|
|
20
|
+
* Validates whether a move is legal in the current game state
|
|
21
|
+
*
|
|
22
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
23
|
+
* @param move - The move to validate (choice of rock, paper, or scissors)
|
|
24
|
+
* @param playerId - The ID of the player attempting the move
|
|
25
|
+
* @returns true if the move is valid, false otherwise
|
|
26
|
+
*
|
|
27
|
+
* @description
|
|
28
|
+
* A move is valid if:
|
|
29
|
+
* - The choice is one of: 'rock', 'paper', 'scissors'
|
|
30
|
+
* - The game status is 'playing'
|
|
31
|
+
* - The current round hasn't exceeded max rounds
|
|
32
|
+
* - The player hasn't already made a choice in the current round
|
|
33
|
+
*/
|
|
34
|
+
validateMove(gameState, move, playerId) {
|
|
35
|
+
const { choice } = move;
|
|
36
|
+
// Check if it's a valid choice
|
|
37
|
+
if (!['rock', 'paper', 'scissors'].includes(choice)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Check if game is still ongoing
|
|
41
|
+
if (gameState.status !== 'playing') {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Check if current round is valid
|
|
45
|
+
if (gameState.currentRound >= gameState.maxRounds) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const currentRound = gameState.rounds[gameState.currentRound];
|
|
49
|
+
// Check if player hasn't made a choice yet in this round
|
|
50
|
+
if (playerId === 'player1' || playerId === gameState.players[0].id) {
|
|
51
|
+
return !currentRound.player1Choice;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return !currentRound.player2Choice;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Applies a validated move to the game state and returns the new state
|
|
59
|
+
*
|
|
60
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
61
|
+
* @param move - The move to apply (must be valid)
|
|
62
|
+
* @param playerId - The ID of the player making the move
|
|
63
|
+
* @returns A new game state with the move applied
|
|
64
|
+
* @throws Error if the move is invalid
|
|
65
|
+
*
|
|
66
|
+
* @description
|
|
67
|
+
* This method handles both partial and complete rounds:
|
|
68
|
+
* - If first player choice: records choice and switches to other player
|
|
69
|
+
* - If second player choice: records choice, determines round winner,
|
|
70
|
+
* updates scores, and advances to next round or ends game
|
|
71
|
+
*/
|
|
72
|
+
applyMove(gameState, move, playerId) {
|
|
73
|
+
if (!this.validateMove(gameState, move, playerId)) {
|
|
74
|
+
throw new Error('Invalid move');
|
|
75
|
+
}
|
|
76
|
+
const newRounds = [...gameState.rounds];
|
|
77
|
+
const currentRound = { ...newRounds[gameState.currentRound] };
|
|
78
|
+
// Apply the move
|
|
79
|
+
if (playerId === gameState.players[0].id) {
|
|
80
|
+
currentRound.player1Choice = move.choice;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
currentRound.player2Choice = move.choice;
|
|
84
|
+
}
|
|
85
|
+
newRounds[gameState.currentRound] = currentRound;
|
|
86
|
+
let newCurrentRound = gameState.currentRound;
|
|
87
|
+
const newScores = { ...gameState.scores };
|
|
88
|
+
let newCurrentPlayerId = gameState.currentPlayerId;
|
|
89
|
+
// If both players have made their choices, resolve the round
|
|
90
|
+
if (currentRound.player1Choice && currentRound.player2Choice) {
|
|
91
|
+
const roundWinner = this.determineRoundWinner(currentRound.player1Choice, currentRound.player2Choice);
|
|
92
|
+
currentRound.winner = roundWinner;
|
|
93
|
+
// Update scores
|
|
94
|
+
if (roundWinner !== 'draw') {
|
|
95
|
+
const winnerId = roundWinner === 'player1' ? gameState.players[0].id : gameState.players[1].id;
|
|
96
|
+
newScores[winnerId] = (newScores[winnerId] || 0) + 1;
|
|
97
|
+
}
|
|
98
|
+
// Move to next round
|
|
99
|
+
newCurrentRound++;
|
|
100
|
+
newCurrentPlayerId = gameState.players[0].id; // Reset to first player
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Switch to other player
|
|
104
|
+
newCurrentPlayerId = gameState.players.find(p => p.id !== playerId)?.id || gameState.players[0].id;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
...gameState,
|
|
108
|
+
rounds: newRounds,
|
|
109
|
+
currentRound: newCurrentRound,
|
|
110
|
+
scores: newScores,
|
|
111
|
+
currentPlayerId: newCurrentPlayerId,
|
|
112
|
+
updatedAt: new Date(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Checks if the game has ended and determines the winner
|
|
117
|
+
*
|
|
118
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
119
|
+
* @returns GameResult with winner and reason if game ended, null if game continues
|
|
120
|
+
*
|
|
121
|
+
* @description
|
|
122
|
+
* The game ends when all rounds are complete (currentRound >= maxRounds).
|
|
123
|
+
* Winner is determined by comparing final scores:
|
|
124
|
+
* - Player with higher score wins
|
|
125
|
+
* - Equal scores result in a draw
|
|
126
|
+
* - Reason includes the final score (e.g., "Won 2-1")
|
|
127
|
+
*/
|
|
128
|
+
checkGameEnd(gameState) {
|
|
129
|
+
// Game ends when all rounds are complete
|
|
130
|
+
if (gameState.currentRound >= gameState.maxRounds) {
|
|
131
|
+
const player1Score = gameState.scores[gameState.players[0].id] || 0;
|
|
132
|
+
const player2Score = gameState.scores[gameState.players[1].id] || 0;
|
|
133
|
+
if (player1Score > player2Score) {
|
|
134
|
+
return {
|
|
135
|
+
winner: gameState.players[0].id,
|
|
136
|
+
reason: `Won ${player1Score}-${player2Score}`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
else if (player2Score > player1Score) {
|
|
140
|
+
return {
|
|
141
|
+
winner: gameState.players[1].id,
|
|
142
|
+
reason: `Won ${player2Score}-${player1Score}`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
return {
|
|
147
|
+
winner: 'draw',
|
|
148
|
+
reason: `Tied ${player1Score}-${player2Score}`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null; // Game continues
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Gets all valid moves for a player in the current game state
|
|
156
|
+
*
|
|
157
|
+
* @param gameState - The current state of the rock-paper-scissors game
|
|
158
|
+
* @param playerId - The ID of the player to get moves for
|
|
159
|
+
* @returns Array of valid RPSMove objects (all choices if player can move)
|
|
160
|
+
*
|
|
161
|
+
* @description
|
|
162
|
+
* Returns all three choices [rock, paper, scissors] if:
|
|
163
|
+
* - Game is still playing
|
|
164
|
+
* - Current round is within max rounds
|
|
165
|
+
* - Player hasn't made a choice in the current round
|
|
166
|
+
* Otherwise returns an empty array.
|
|
167
|
+
*/
|
|
168
|
+
getValidMoves(gameState, playerId) {
|
|
169
|
+
if (gameState.status !== 'playing' || gameState.currentRound >= gameState.maxRounds) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
const currentRound = gameState.rounds[gameState.currentRound];
|
|
173
|
+
// Check if player can make a move in current round
|
|
174
|
+
const canMove = (playerId === gameState.players[0].id && !currentRound.player1Choice) ||
|
|
175
|
+
(playerId === gameState.players[1].id && !currentRound.player2Choice);
|
|
176
|
+
if (!canMove) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
return [
|
|
180
|
+
{ choice: 'rock' },
|
|
181
|
+
{ choice: 'paper' },
|
|
182
|
+
{ choice: 'scissors' }
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Creates the initial game state for a new rock-paper-scissors game
|
|
187
|
+
*
|
|
188
|
+
* @param players - Array of exactly 2 players
|
|
189
|
+
* @param options - Optional configuration including maxRounds
|
|
190
|
+
* @returns Initial RPSGameState set up for a configurable number of rounds
|
|
191
|
+
*
|
|
192
|
+
* @description
|
|
193
|
+
* Sets up a new game with:
|
|
194
|
+
* - Configurable number of rounds (default: 3 for best-of-3 format)
|
|
195
|
+
* - All scores initialized to 0
|
|
196
|
+
* - First player goes first
|
|
197
|
+
* - Game status set to 'playing'
|
|
198
|
+
* - Current round set to 0
|
|
199
|
+
*/
|
|
200
|
+
getInitialState(players, options) {
|
|
201
|
+
const maxRounds = options?.maxRounds || 3; // Default to best of 3
|
|
202
|
+
const rounds = Array.from({ length: maxRounds }, () => ({}));
|
|
203
|
+
const scores = {};
|
|
204
|
+
players.forEach(player => {
|
|
205
|
+
scores[player.id] = 0;
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
id: crypto.randomUUID(),
|
|
209
|
+
players,
|
|
210
|
+
currentPlayerId: players[0].id,
|
|
211
|
+
status: 'playing',
|
|
212
|
+
createdAt: new Date(),
|
|
213
|
+
updatedAt: new Date(),
|
|
214
|
+
rounds,
|
|
215
|
+
currentRound: 0,
|
|
216
|
+
maxRounds,
|
|
217
|
+
scores,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Determines the winner of a single round based on the classic RPS rules
|
|
222
|
+
*
|
|
223
|
+
* @param choice1 - First player's choice
|
|
224
|
+
* @param choice2 - Second player's choice
|
|
225
|
+
* @returns 'player1' if choice1 wins, 'player2' if choice2 wins, 'draw' if same
|
|
226
|
+
*
|
|
227
|
+
* @private
|
|
228
|
+
* @description
|
|
229
|
+
* Implements the classic rules:
|
|
230
|
+
* - Rock beats Scissors
|
|
231
|
+
* - Paper beats Rock
|
|
232
|
+
* - Scissors beats Paper
|
|
233
|
+
* - Same choices result in a draw
|
|
234
|
+
*/
|
|
235
|
+
determineRoundWinner(choice1, choice2) {
|
|
236
|
+
if (choice1 === choice2) {
|
|
237
|
+
return 'draw';
|
|
238
|
+
}
|
|
239
|
+
const winConditions = {
|
|
240
|
+
rock: 'scissors',
|
|
241
|
+
paper: 'rock',
|
|
242
|
+
scissors: 'paper'
|
|
243
|
+
};
|
|
244
|
+
return winConditions[choice1] === choice2 ? 'player1' : 'player2';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=rock-paper-scissors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rock-paper-scissors.js","sourceRoot":"","sources":["../../src/games/rock-paper-scissors.ts"],"names":[],"mappings":"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"}
|