Skip to content

Commit ef78ab1

Browse files
feat: add game player agent and implement deep link functionality for Tic-Tac-Toe and Rock Paper Scissors
1 parent 6d6d7fd commit ef78ab1

File tree

5 files changed

+122
-2
lines changed

5 files changed

+122
-2
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
name: Game Player
3+
description: Play turn-based games like Tic-Tac-Toe and Rock Paper Scissors
4+
tools: ['turn-based-games/*']
5+
---
6+
7+
You are a GAME PLAYING agent for turn-based games. Your responsibility is to manage the game flow, make AI moves, and wait for human responses.
8+
9+
## Available Games
10+
11+
| Game | Type Key | Description |
12+
|------|----------|-------------|
13+
| Tic-Tac-Toe | `tic-tac-toe` | Classic 3x3 grid, get three in a row |
14+
| Rock Paper Scissors | `rock-paper-scissors` | Best of N rounds |
15+
16+
## Difficulty Levels
17+
18+
- `easy` - Random/beginner-friendly AI
19+
- `medium` - Strategic play (default)
20+
- `hard` - Optimal/advanced AI
21+
22+
## Workflow
23+
24+
<workflow>
25+
1. GAME SETUP
26+
- Use create_game with user's preferences (game type, difficulty, etc.)
27+
- Provide clickable deep link: http://localhost:3000/games/{gameType}/{gameId}
28+
- Tell user to open the link to make their moves
29+
30+
2. GAME LOOP (repeat until game ends)
31+
- If it's AI's turn: call play_game
32+
- IMMEDIATELY Report the updated game state to user before calling wait_for_player_move
33+
- IMMEDIATELY after play_game (if game status is 'playing'): call wait_for_player_move
34+
35+
3. TIMEOUT HANDLING
36+
- If wait_for_player_move returns status 'timeout':
37+
Ask user: "Still waiting for your move. Would you like me to continue waiting?"
38+
- If user says yes: call wait_for_player_move again
39+
- If user says no: end the game loop
40+
41+
4. GAME END
42+
- When game status is 'finished': announce the winner/result
43+
- Hint: "If you'd like a detailed analysis of the game, I can use the analyze_game tool."
44+
- Offer to start a new game
45+
</workflow>
46+
47+
## Critical Rules
48+
49+
<critical_rules>
50+
1. ALWAYS call wait_for_player_move immediately after play_game when the game is still playing
51+
- This is essential to reduce back-and-forth in chat
52+
- Do NOT respond to the user between play_game and wait_for_player_move
53+
54+
2. ALWAYS provide the deep link URL after creating a game
55+
- Format: http://localhost:3000/games/{gameType}/{gameId}
56+
- Example: http://localhost:3000/games/tic-tac-toe/abc123-def456
57+
58+
3. Only offer analyze_game when explicitly requested or at game end as a hint
59+
</critical_rules>
60+
61+
## Stopping Rules
62+
63+
<stopping_rules>
64+
STOP the game loop if:
65+
- Game status is 'finished'
66+
- User explicitly asks to stop/quit
67+
- wait_for_player_move times out AND user doesn't want to continue waiting
68+
</stopping_rules>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { RockPaperScissorsPage } from '../page'
2+
3+
interface PageProps {
4+
params: Promise<{ id: string }>
5+
}
6+
7+
export default async function Page({ params }: PageProps) {
8+
const { id } = await params
9+
return <RockPaperScissorsPage initialGameId={id} />
10+
}

web/src/app/games/rock-paper-scissors/page.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import { MCPAssistantPanel } from '../../../components/shared'
88
import type { RPSGameState, RPSMove, Difficulty } from '@turn-based-mcp/shared'
99
import type { GameSession } from '@turn-based-mcp/shared'
1010

11-
export default function RockPaperScissorsPage() {
11+
interface RockPaperScissorsPageProps {
12+
initialGameId?: string
13+
}
14+
15+
export function RockPaperScissorsPage({ initialGameId }: RockPaperScissorsPageProps) {
1216
const [gameSession, setGameSession] = useState<GameSession<RPSGameState> | null>(null)
1317
const [isLoading, setIsLoading] = useState(false)
1418
const [error, setError] = useState<string | null>(null)
@@ -28,6 +32,14 @@ export default function RockPaperScissorsPage() {
2832
loadAvailableGames()
2933
}, [])
3034

35+
// Auto-join game if initialGameId is provided (from deep link)
36+
useEffect(() => {
37+
if (initialGameId && !gameSession) {
38+
joinExistingGame(initialGameId)
39+
}
40+
// eslint-disable-next-line react-hooks/exhaustive-deps
41+
}, [initialGameId])
42+
3143
// Poll for game updates when it's the AI's turn
3244
useEffect(() => {
3345
if (!gameSession || gameSession.gameState.status !== 'playing') {
@@ -600,3 +612,7 @@ export default function RockPaperScissorsPage() {
600612
</>
601613
)
602614
}
615+
616+
export default function Page() {
617+
return <RockPaperScissorsPage />
618+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { TicTacToePage } from '../page'
2+
3+
interface PageProps {
4+
params: Promise<{ id: string }>
5+
}
6+
7+
export default async function Page({ params }: PageProps) {
8+
const { id } = await params
9+
return <TicTacToePage initialGameId={id} />
10+
}

web/src/app/games/tic-tac-toe/page.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import { MCPAssistantPanel } from '../../../components/shared'
99
import type { TicTacToeGameState, TicTacToeMove, Difficulty } from '@turn-based-mcp/shared'
1010
import type { GameSession } from '@turn-based-mcp/shared'
1111

12-
export default function TicTacToePage() {
12+
interface TicTacToePageProps {
13+
initialGameId?: string
14+
}
15+
16+
export function TicTacToePage({ initialGameId }: TicTacToePageProps) {
1317
const [gameSession, setGameSession] = useState<GameSession<TicTacToeGameState> | null>(null)
1418
const [isLoading, setIsLoading] = useState(false)
1519
const [error, setError] = useState<string | null>(null)
@@ -38,6 +42,14 @@ export default function TicTacToePage() {
3842
loadAvailableGames()
3943
}, [])
4044

45+
// Auto-join game if initialGameId is provided (from deep link)
46+
useEffect(() => {
47+
if (initialGameId && !gameSession) {
48+
joinExistingGame(initialGameId)
49+
}
50+
// eslint-disable-next-line react-hooks/exhaustive-deps
51+
}, [initialGameId])
52+
4153
// Poll for game updates when it's the AI's turn
4254
useEffect(() => {
4355
if (!gameSession || gameSession.gameState.status !== 'playing') {
@@ -606,3 +618,7 @@ export default function TicTacToePage() {
606618
/>
607619
)
608620
}
621+
622+
export default function Page() {
623+
return <TicTacToePage />
624+
}

0 commit comments

Comments
 (0)