chess-mcp-server 0.1.0__tar.gz

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.
@@ -0,0 +1,47 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+
24
+ # Virtual Environments
25
+ .env
26
+ .venv
27
+ env/
28
+ venv/
29
+ ENV/
30
+ env.bak/
31
+ venv.bak/
32
+
33
+ # Environment Variables
34
+ .env
35
+ .env.*
36
+
37
+ # IDEs
38
+ .vscode/
39
+ .idea/
40
+ *.swp
41
+
42
+ # OS
43
+ .DS_Store
44
+ Thumbs.db
45
+
46
+ # MCP
47
+ .gemini/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fritzprix
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: chess-mcp-server
3
+ Version: 0.1.0
4
+ Summary: A Model Context Protocol (MCP) server for playing Chess with AI Agents.
5
+ Project-URL: Homepage, https://github.com/fritzprix/chess-mcp-server
6
+ Project-URL: Issues, https://github.com/fritzprix/chess-mcp-server/issues
7
+ Author-email: fritzprix <your-email@example.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: fastapi
15
+ Requires-Dist: jinja2
16
+ Requires-Dist: mcp[cli]
17
+ Requires-Dist: python-chess
18
+ Requires-Dist: uvicorn
19
+ Description-Content-Type: text/markdown
20
+
21
+ # ♟️ Chess MCP Server
22
+
23
+ **Give your AI Agent eyes to see the board and hands to make the move.**
24
+
25
+ This is not just a chess API. It's a **Model Context Protocol (MCP)** server designed to let Large Language Models (LLMs) like Claude play chess *agentically*.
26
+
27
+ Capable of visualizing the board in real-time HTML, understanding spatial relationships via Markdown, and challenging you with a hybrid difficulty engine (Levels 1-10)—or simply facilitating a game between you and your Agent.
28
+
29
+ ## 🚀 Features
30
+
31
+ - **MCP-UI Support**: Interactive HTML board embedded directly in the chat (where supported).
32
+ - **Hybrid AI Engine**: Adjustable difficulty from "Random Blunderer" (Level 1) to "Minimax Master" (Level 10).
33
+ - **Agent vs. Agent**: Let two AI personalities battle it out.
34
+ - **Web Dashboard**: Automatically launches a local sidecar dashboard (`http://localhost:8080`) to monitor all active games.
35
+
36
+ ## 📦 Installation
37
+
38
+ ### Prerequisites
39
+ - Python 3.10+
40
+ - An MCP Client (e.g., [Claude Desktop](https://claude.ai/download), [Cursor](https://cursor.sh/))
41
+
42
+ ### 1. Installation
43
+ You can install directly from PyPI:
44
+
45
+ ```bash
46
+ pip install chess-mcp-server
47
+ ```
48
+
49
+ ### 2. Configure MCP Client
50
+
51
+ Add the following to your MCP Client configuration file (e.g., `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "chess": {
57
+ "command": "uvx",
58
+ "args": ["chess-mcp-server"]
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ *Alternatively, using pip installation:*
65
+ ```json
66
+ {
67
+ "mcpServers": {
68
+ "chess": {
69
+ "command": "python",
70
+ "args": ["-m", "src.mcp_server"]
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## 🛠️ Development
77
+
78
+ If you want to modify the code:
79
+
80
+ 1. **Clone & Setup**
81
+ ```bash
82
+ git clone https://github.com/fritzprix/chess-mcp-server.git
83
+ cd chess-mcp-server
84
+
85
+ python -m venv .venv
86
+ source .venv/bin/activate
87
+ pip install -r requirements.txt
88
+ ```
89
+
90
+ ## 🎮 How to Play
91
+
92
+ Once the server is connected, you can ask your Agent to start a game.
93
+
94
+ ### Start a Game
95
+ Ask: *"Start a new chess game against the computer at level 5."*
96
+ - The Agent calls `createGame`.
97
+ - **Pro Tip**: You can also ask *"I want to play against YOU. Create a game where you are White."*
98
+
99
+ ### The Game Loop
100
+ 1. **Your Move**:
101
+ - Interact with the **HTML Board** if shown. Drag your piece and click **Confirm**.
102
+ - *Or* tell the Agent: *"Move pawn to e4."*
103
+ 2. **Agent's Turn**:
104
+ - The Agent calls `waitForNextTurn`.
105
+ - It sees the board (Markdown or HTML) and thinks about the move.
106
+ - It calls `finishTurn` to submit its move.
107
+ 3. **Checkmate**:
108
+ - If you deliver the final blow, you can check the **"Claim Checkmate"** box on the UI or tell the Agent *"Checkmate!"*.
109
+
110
+ ### Dashboard
111
+ When the server starts, it will try to open **http://localhost:8080**. You can view the list of all active games and spectator views there.
@@ -0,0 +1,91 @@
1
+ # ♟️ Chess MCP Server
2
+
3
+ **Give your AI Agent eyes to see the board and hands to make the move.**
4
+
5
+ This is not just a chess API. It's a **Model Context Protocol (MCP)** server designed to let Large Language Models (LLMs) like Claude play chess *agentically*.
6
+
7
+ Capable of visualizing the board in real-time HTML, understanding spatial relationships via Markdown, and challenging you with a hybrid difficulty engine (Levels 1-10)—or simply facilitating a game between you and your Agent.
8
+
9
+ ## 🚀 Features
10
+
11
+ - **MCP-UI Support**: Interactive HTML board embedded directly in the chat (where supported).
12
+ - **Hybrid AI Engine**: Adjustable difficulty from "Random Blunderer" (Level 1) to "Minimax Master" (Level 10).
13
+ - **Agent vs. Agent**: Let two AI personalities battle it out.
14
+ - **Web Dashboard**: Automatically launches a local sidecar dashboard (`http://localhost:8080`) to monitor all active games.
15
+
16
+ ## 📦 Installation
17
+
18
+ ### Prerequisites
19
+ - Python 3.10+
20
+ - An MCP Client (e.g., [Claude Desktop](https://claude.ai/download), [Cursor](https://cursor.sh/))
21
+
22
+ ### 1. Installation
23
+ You can install directly from PyPI:
24
+
25
+ ```bash
26
+ pip install chess-mcp-server
27
+ ```
28
+
29
+ ### 2. Configure MCP Client
30
+
31
+ Add the following to your MCP Client configuration file (e.g., `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "chess": {
37
+ "command": "uvx",
38
+ "args": ["chess-mcp-server"]
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ *Alternatively, using pip installation:*
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "chess": {
49
+ "command": "python",
50
+ "args": ["-m", "src.mcp_server"]
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ## 🛠️ Development
57
+
58
+ If you want to modify the code:
59
+
60
+ 1. **Clone & Setup**
61
+ ```bash
62
+ git clone https://github.com/fritzprix/chess-mcp-server.git
63
+ cd chess-mcp-server
64
+
65
+ python -m venv .venv
66
+ source .venv/bin/activate
67
+ pip install -r requirements.txt
68
+ ```
69
+
70
+ ## 🎮 How to Play
71
+
72
+ Once the server is connected, you can ask your Agent to start a game.
73
+
74
+ ### Start a Game
75
+ Ask: *"Start a new chess game against the computer at level 5."*
76
+ - The Agent calls `createGame`.
77
+ - **Pro Tip**: You can also ask *"I want to play against YOU. Create a game where you are White."*
78
+
79
+ ### The Game Loop
80
+ 1. **Your Move**:
81
+ - Interact with the **HTML Board** if shown. Drag your piece and click **Confirm**.
82
+ - *Or* tell the Agent: *"Move pawn to e4."*
83
+ 2. **Agent's Turn**:
84
+ - The Agent calls `waitForNextTurn`.
85
+ - It sees the board (Markdown or HTML) and thinks about the move.
86
+ - It calls `finishTurn` to submit its move.
87
+ 3. **Checkmate**:
88
+ - If you deliver the final blow, you can check the **"Claim Checkmate"** box on the UI or tell the Agent *"Checkmate!"*.
89
+
90
+ ### Dashboard
91
+ When the server starts, it will try to open **http://localhost:8080**. You can view the list of all active games and spectator views there.
@@ -0,0 +1,160 @@
1
+ # MCP Tools Specification Feasibility Analysis
2
+
3
+ ## 1. Overview
4
+ This document analyzes the feasibility of the tools defined in `docs/spec/tools.md` based on the `docs/mcp_reference_en.md` standard.
5
+
6
+ **Conclusion:** The proposed specification is **FEASIBLE**.
7
+ **Updates:**
8
+ - Generic `finishTurn` is recommended.
9
+ - `waitForNextTurn` with 30s long-polling is feasible.
10
+ - **Checkmate Claiming**: Explicit checkmate acknowledgment by the user is feasible and supported.
11
+ - **Web Dashboard**: Sidecar web server with auto-launch is feasible.
12
+
13
+ ## 2. Detailed Verification
14
+
15
+ | Feature spec | MCP Reference Component | Feasibility | Notes |
16
+ | :--- | :--- | :--- | :--- |
17
+ | **Tool: `createGame`** | Section 2.4 (Tools) | ✅ Supported | Standard tool definition. Returns text. |
18
+ | **Tool: `waitForNextTurn`** | Section 2.4 (Tools) | ✅ Supported | Standard tool definition. Returns text + resource. |
19
+ | **Tool: `finishTurn`** | Section 2.4 (Tools) | ✅ Supported | Generic submission of moves. Replaces `finishUserTurn`. |
20
+ | **Text Rendering** | N/A (Client Rendering) | ✅ Supported | Markdown tables are standard in LLM responses. |
21
+ | **UI Resource** | Section 3.1 & 3.2 | ✅ Supported | `text/html` is a valid `UIResource` MIME type (Strategy 1). |
22
+ | **UI Interaction (Move)** | Section 3.3 (UI Actions) | ✅ Supported* | Requires Host to handle `postMessage` and strict `onUIAction` loop. |
23
+
24
+ ## 3. Implementation Details & Considerations
25
+
26
+ ### 3.1 UI Resource Implementation
27
+ The specification calls for returning an HTML board when `showUi` is true.
28
+ According to Reference Section 3.2 (Strategy 1):
29
+ - Server returns: simple HTML string.
30
+ - Host behavior: Injects into sandboxed iframe.
31
+ - **Feasible**: Yes, the Python `show_dashboard` example (Reference Section 4.2) directly demonstrates this pattern using `text/html`.
32
+
33
+ ### 3.2 Bi-directional Interaction (The "User Move")
34
+ The spec mentions: "User... selects Confirm... user's move is sent to server... usage choice is `finishUserTurn`... via MCP UI ui action".
35
+
36
+ **Mechanism:**
37
+ 1. **HTML Logic**: The embedded HTML board must contain JavaScript to capture clicks and send a message.
38
+ ```javascript
39
+ // Inside the HTML returned by waitForNextTurn
40
+ window.parent.postMessage({
41
+ type: 'action',
42
+ action: 'finishTurn', // GENERIC NAME RECOMMENDED
43
+ payload: { from: 'e2', to: 'e4' }
44
+ }, '*');
45
+ ```
46
+ 2. **Host Handling**: The MCP Host (e.g., Claude Desktop, IDE) receives this event.
47
+ 3. **Loopback**: The Host must route this action to a tool call.
48
+ - *Note*: The MCP reference Section 3.3 says "The host then cycles this back by calling MCP tools".
49
+ - You will likely need to expose `finishUserTurn` as an MCP Tool so the host (or the Agent reacting to the UI event) can call it.
50
+
51
+ ### 3.3 The `waitForNextTurn` Blocking Issue
52
+ One logical ambiguity in the spec is the behavior of `waitForNextTurn` when it is the **Human's** turn.
53
+ - If the LLM calls `waitForNextTurn`, and it is the Human's turn, the tool should likely:
54
+ 1. Return the board state immediately with a status "Waiting for User".
55
+ 2. The LLM should then perhaps yield or wait?
56
+ 3. **Recommended Pattern**: The `waitForNextTurn` tool serves as a "Refresh/Poll" mechanism. The LLM sees the state. If it's human turn, LLM does nothing (or just outputs "Waiting for human move..."). When Human moves (via UI -> `finishUserTurn`), the state updates. The LLM might be re-triggered or manually check again.
57
+
58
+ ## 4. Analysis: `finishTurn` vs `finishUserTurn`
59
+ The user questioned if a generic `finishTurn` is sufficient for both scenarios (Agent move vs User move).
60
+
61
+ **Recommendation: Use `finishTurn` for ALL moves.**
62
+
63
+ ### Reasons:
64
+ 1. **Protocol Simplicity**: The server logic is "Execute Move X on Game Y". The mechanics of *who* originated the move (Agent thinking process vs User UI click) are transport details.
65
+ 2. **State-Driven Validation**: The Server already knows whose turn it is.
66
+ * If requests `finishTurn` and it is **Agent's Turn**: Accepted.
67
+ * If requests `finishTurn` and it is **User's Turn**: Accepted (assuming the UI triggered it).
68
+ * *Security Note*: Strictly speaking, if the Agent (LLM) hallucinates and calls `finishTurn` during the User's turn, the server MUST return an error ("Not your turn"). This state check is required regardless of the tool name.
69
+ 3. **Unified Schema**:
70
+ ```typescript
71
+ finishTurn(game_id: string, move: string)
72
+ ```
73
+ This single signature covers both cases.
74
+
75
+ ### Revised Flow with `finishTurn`:
76
+ 1. **Agent Turn**: LLM thinks -> Calls `finishTurn(game_id, move)`. Server updates state.
77
+ 2. **User Turn**:
78
+ * LLM Calls `waitForNextTurn`.
79
+ * Server returns UI.
80
+ * User interacts with UI -> `postMessage({ action: 'finishTurn', ... })`.
81
+ * Host receives message -> Calls `finishTurn(game_id, move)`.
82
+ * Server updates state.
83
+
84
+ ## 5. Analysis: `waitForNextTurn` Blocking Strategy
85
+ The proposed strategy is:
86
+ - **Blocking**: 30 seconds wait for opponent move.
87
+ - **Return Condition 1 (Activity)**: Opponent moves -> Return State Immediately.
88
+ - **Return Condition 2 (Timeout)**: 30s elapsed -> Return "Timeout, try again" message.
89
+
90
+ **Feasibility: ✅ Confirmed**
91
+
92
+ ### Considerations:
93
+ 1. **Host Timeout**: Most MCP Clients (Host applications) set a timeout for tool calls.
94
+ - *Typical Default*: Often 60s or more.
95
+ - *Risk*: If the Host has a very short timeout (e.g., 10s), this call will fail with a Protocol Error before the server returns the custom "Timeout" message.
96
+ - *Mitigation*: 30 seconds is generally a safe lower bound. It allows for a "Long Poll" pattern without triggering aggressive read timeouts.
97
+ 2. **Server Implementation**:
98
+ - Must use asynchronous sleep (`await asyncio.sleep(0.1)` or `threading.Event`).
99
+ - Do NOT use `time.sleep(30)` in a single-threaded blocking server, as it will freeze the entire server for all users/requests.
100
+ - *Recommendation*: Use `async def` and event loops (FastMCP supports this natively).
101
+ 3. **LLM Loop**:
102
+ - The LLM must be instructed (via the System Prompt or Tool Description) that receiving a "Timeout" is **normal** and it should simply call the tool again. Without this, the LLM might interpret the timeout as a failure and stop or hallucinate complications.
103
+
104
+ **Revised Tool Description Suggestion:**
105
+ > "Waits for the opponent to move. Blocks for up to 30 seconds. If the opponent moves, returns the new board state. If no move occurs within 30 seconds, returns a timeout message. You should immediately call this tool again if you receive a timeout."
106
+
107
+ ## 6. Analysis: Checkmate Acknowledgement
108
+ The user requested that the user must "explicitly decide" whether a move is checkmate.
109
+
110
+ **Feasibility: ✅ Confirmed**
111
+
112
+ To support this:
113
+ 1. **Protocol**: Update `finishTurn` to accept `claim: string` (values: `null`, `'checkmate'`, `'draw'`).
114
+ 2. **Validation**:
115
+ - If `claim='checkmate'` is sent, the server MUST verify `board.is_checkmate()` AFTER the move.
116
+ - If valid -> Win acknowledged.
117
+ - If invalid -> Return error "Accusation failed: Not checkmate".
118
+ 3. **UI**: The HTML Board requires an interactive element (e.g., specific "Move & Claim Checkmate" button or a toggle).
119
+
120
+ **Modified Schema**:
121
+ ```typescript
122
+ finishTurn(
123
+ game_id: string,
124
+ move: string,
125
+ claim_win?: boolean // if true, asserts this move ends the game
126
+ )
127
+ ```
128
+
129
+ **Recommendation for Spec**: Include `claim_win` as an optional parameter to support this "Explicit Decision" mechanic.
130
+
131
+ ## 7. Analysis: Web Dashboard & Sidecar Server
132
+ **Use Case**: When the MCP server starts, it should satisfy "start web server + open browser" to show a game list.
133
+
134
+ **Feasibility: ✅ Confirmed (with considerations)**
135
+
136
+ ### Architecture
137
+ - **Dual Interface**:
138
+ 1. **MCP Interface**: Stdio/SSE handling JSON-RPC.
139
+ 2. **HTTP Interface**: Sidecar web server (e.g., Flask/FastAPI/Starlette) running on localhost (random or fixed port).
140
+ - **Execution**: The MCP server entry point must launch the HTTP server background thread/task on startup (`lifespan` event or `__main__`).
141
+
142
+ ### Mechanism
143
+ 1. **Startup**:
144
+ - Initialize `FastMCP` (or equivalent).
145
+ - Start HTTP Server (e.g. `uvicorn.run` in a thread).
146
+ - Get Port (e.g., `8080`).
147
+ 2. **Browser Launch**:
148
+ - Use Python `webbrowser` module: `webbrowser.open("http://localhost:8080")`.
149
+ - *Note*: This works well on local machines (macOS/Windows/Linux). It may fail silently in headless environments (SSH/Cloud), which is acceptable.
150
+
151
+ ### UX Consideration
152
+ - **Auto-Open**: If the server crashes/restarts frequently, the browser popping up repeatedly can be annoying.
153
+ - **Mitigation**: Add a logic to only open if not already "running" or check a flag/env var `MCP_DISABLE_BROWSER=1`.
154
+
155
+ ### Integration with Game State
156
+ - The Game State manager must be a singleton shared between the MCP Tools context and the HTTP Server endpoints.
157
+ - **Endpoints**:
158
+ - `GET /`: HTML Dashboard showing list of active games.
159
+ - `GET /api/games`: JSON list.
160
+ - `GET /game/{id}`: View specific game board (spectator mode).
@@ -0,0 +1,161 @@
1
+ Here is a proposal to quantify the difficulty into 10 distinct levels using a hybrid approach of **Search Depth** (foresight) and **Error Rate** (probability of making a random move instead of the best move).
2
+
3
+ ### Logic: The Hybrid Model
4
+
5
+ * **Depth (1-4):** Controls how far the AI calculates. Python is relatively slow, so Depth 4 is a realistic maximum for a responsive game without C++ extensions.
6
+ * **Error Rate (0.0 - 0.6):** The probability that the AI ignores the "best move" and picks a random legal move (simulating a blunder).
7
+
8
+ ### Configuration Dictionary
9
+
10
+ | Level | Depth | Error Rate | Description |
11
+ | --- | --- | --- | --- |
12
+ | **1** | 1 | 60% | Calculates 1 move ahead, frequently plays randomly. |
13
+ | **2** | 1 | 40% | Still shortsighted, but slightly more coherent. |
14
+ | **3** | 1 | 20% | Rarely misses immediate captures, but has no plan. |
15
+ | **4** | 2 | 20% | Starts seeing 2 moves ahead, but makes mistakes. |
16
+ | **5** | 2 | 10% | Standard amateur level. Solid 2-move calculation. |
17
+ | **6** | 3 | 10% | Stronger. Sees 3 moves ahead, occasional errors. |
18
+ | **7** | 3 | 5% | Competent. Very few unforced errors. |
19
+ | **8** | 3 | 0% | Best possible play at Depth 3. |
20
+ | **9** | 4 | 5% | Deep calculation (slower), slight human-like noise. |
21
+ | **10** | 4 | 0% | Maximum strength. Pure Minimax at Depth 4. |
22
+
23
+ ### Python Implementation
24
+
25
+ This code wraps your previous Minimax logic into a class structure that accepts a level (1-10).
26
+
27
+ ```python
28
+ import chess
29
+ import math
30
+ import random
31
+
32
+ class ChessAI:
33
+ def __init__(self):
34
+ # Configuration for 10 levels
35
+ self.levels = {
36
+ 1: {"depth": 1, "error_rate": 0.60},
37
+ 2: {"depth": 1, "error_rate": 0.40},
38
+ 3: {"depth": 1, "error_rate": 0.20},
39
+ 4: {"depth": 2, "error_rate": 0.20},
40
+ 5: {"depth": 2, "error_rate": 0.10},
41
+ 6: {"depth": 3, "error_rate": 0.10},
42
+ 7: {"depth": 3, "error_rate": 0.05},
43
+ 8: {"depth": 3, "error_rate": 0.00},
44
+ 9: {"depth": 4, "error_rate": 0.05},
45
+ 10: {"depth": 4, "error_rate": 0.00},
46
+ }
47
+
48
+ # Piece values for evaluation
49
+ self.piece_values = {
50
+ chess.PAWN: 10, chess.KNIGHT: 30, chess.BISHOP: 30,
51
+ chess.ROOK: 50, chess.QUEEN: 90, chess.KING: 900
52
+ }
53
+
54
+ def get_move(self, board, level):
55
+ """
56
+ Returns the best move based on the difficulty level (1-10).
57
+ """
58
+ if level not in self.levels:
59
+ level = 5 # Default to medium
60
+
61
+ settings = self.levels[level]
62
+ depth = settings["depth"]
63
+ error_rate = settings["error_rate"]
64
+
65
+ # 1. Error Chance (Simulate Blunder)
66
+ # If the random roll is within the error rate, pick a random legal move.
67
+ if random.random() < error_rate:
68
+ return random.choice(list(board.legal_moves))
69
+
70
+ # 2. Strategic Calculation (Minimax)
71
+ # Otherwise, calculate the best move using Minimax.
72
+ return self._get_best_move_minimax(board, depth)
73
+
74
+ def _evaluate_board(self, board):
75
+ if board.is_checkmate():
76
+ return -9999 if board.turn else 9999
77
+ if board.is_stalemate() or board.is_insufficient_material():
78
+ return 0
79
+
80
+ score = 0
81
+ for square in chess.SQUARES:
82
+ piece = board.piece_at(square)
83
+ if piece:
84
+ value = self.piece_values[piece.piece_type]
85
+ if piece.color == chess.WHITE:
86
+ score += value
87
+ else:
88
+ score -= value
89
+ return score
90
+
91
+ def _minimax(self, board, depth, alpha, beta, maximizing):
92
+ if depth == 0 or board.is_game_over():
93
+ return self._evaluate_board(board)
94
+
95
+ legal_moves = list(board.legal_moves)
96
+
97
+ if maximizing:
98
+ max_eval = -math.inf
99
+ for move in legal_moves:
100
+ board.push(move)
101
+ eval = self._minimax(board, depth - 1, alpha, beta, False)
102
+ board.pop()
103
+ max_eval = max(max_eval, eval)
104
+ alpha = max(alpha, eval)
105
+ if beta <= alpha:
106
+ break
107
+ return max_eval
108
+ else:
109
+ min_eval = math.inf
110
+ for move in legal_moves:
111
+ board.push(move)
112
+ eval = self._minimax(board, depth - 1, alpha, beta, True)
113
+ board.pop()
114
+ min_eval = min(min_eval, eval)
115
+ beta = min(beta, eval)
116
+ if beta <= alpha:
117
+ break
118
+ return min_eval
119
+
120
+ def _get_best_move_minimax(self, board, depth):
121
+ best_move = None
122
+ best_value = -math.inf if board.turn == chess.WHITE else math.inf
123
+ alpha = -math.inf
124
+ beta = math.inf
125
+
126
+ legal_moves = list(board.legal_moves)
127
+
128
+ # Simple optimization: shuffle moves to prevent deterministic behavior on equal scores
129
+ random.shuffle(legal_moves)
130
+
131
+ for move in legal_moves:
132
+ board.push(move)
133
+ value = self._minimax(board, depth - 1, alpha, beta, not board.turn)
134
+ board.pop()
135
+
136
+ if board.turn == chess.WHITE:
137
+ if value > best_value:
138
+ best_value = value
139
+ best_move = move
140
+ alpha = max(alpha, best_value)
141
+ else:
142
+ if value < best_value:
143
+ best_value = value
144
+ best_move = move
145
+ beta = min(beta, best_value)
146
+
147
+ return best_move
148
+
149
+ # --- Usage Example ---
150
+ if __name__ == "__main__":
151
+ board = chess.Board()
152
+ ai = ChessAI()
153
+
154
+ # User selects Level 7
155
+ user_level = 7
156
+ print(f"AI Level: {user_level} (Depth: {ai.levels[user_level]['depth']}, Error: {ai.levels[user_level]['error_rate']*100}%)")
157
+
158
+ best_move = ai.get_move(board, level=user_level)
159
+ print(f"AI plays: {best_move}")
160
+
161
+ ```