Client + MCTS helpers for board game AI integration.
Project description
tictactoe was taken from https://gist.github.com/qianguigui1104/edb3b11b33c78e5894aad7908c773353
Usage of the library
import via a single client
from game_sdk.mcts_client import AIGameClient
Things that must be provided:
You keep full control over your game logic. To use the framework, you only need to implement: A players list A legal moves function An apply-move function for the AI
- Players list:
players = [
{"id": "P1", "type": "human", "symbol": "X"},
{"id": "P2", "type": "ai_mcts","symbol": "O"},
]
- Legal moves function Return all legal moves for the current player, as a list of dicts:
def compute_legal_moves(board, players, current_player_symbol):
player_index = next(i for i, p in enumerate(players)
if p["symbol"] == current_player_symbol)
moves = []
for r in range(len(board)):
for c in range(len(board[0])):
if board[r][c].strip() == "":
moves.append({
"id": f"PLACE_{r}_{c}", # string id is recommended
"player_index": player_index, # index in players[]
"type": "PLACE_MARK",
"position": {"row": r, "col": c},
})
return moves
- Apply-move function for AI (the only required “hook”) this is the only function that is needed from your game
from typing import Dict, Any, List
from game_sdk.state_utils import build_generic_state
State = Dict[str, Any]
Move = Dict[str, Any]
def apply_move_my_game(state: State, move: Move) -> State:
players: List[Dict[str, Any]] = state["players"]
# --- decode board from generic state ---
board_info = state["board"]
int_board = board_info["cells"] # 2D ints
symbol_map = {0: " "}
for idx, p in enumerate(players):
symbol_map[idx + 1] = p["symbol"]
board = [[symbol_map[v] for v in row] for row in int_board]
# --- apply the move ---
r = move["position"]["row"]
c = move["position"]["col"]
player_index = move["player_index"]
symbol = players[player_index]["symbol"]
board[r][c] = symbol
# --- next player ---
next_player_index = (player_index + 1) % len(players)
next_symbol = players[next_player_index]["symbol"]
# --- terminal & result: YOUR logic here ---
# is_terminal_and_winner() should be implemented by you and return:
# (finished: bool, winner_symbol: Optional[str])
finished, winner_symbol = is_terminal_and_winner(board)
result_map = None
if finished:
# winner_to_results() should return (result_str, result_map)
# e.g. result_map = {"P1": 1.0, "P2": -1.0}
_, result_map = winner_to_results(winner_symbol, players)
move_count = state["extra"].get("move_count", 0) + 1
legal_moves = [] if finished else compute_legal_moves(board, players, next_symbol)
# --- build and return the new state ---
return build_generic_state(
game_id=state["game_id"],
board=board,
players=players,
current_player_symbol=next_symbol,
move_count=move_count,
finished=finished,
legal_moves=legal_moves,
result=result_map,
)
Building a state for the AI / logging
On each turn, describe the current position using build_generic_state:
state = build_generic_state(
game_id="my_game_id",
board=board, # 2D list of symbols, e.g. [["X"," ","O"], ...]
players=players,
current_player_symbol=current_sym, # whose turn it is
move_count=move_count,
finished=False, # or True if you know it’s over
legal_moves=compute_legal_moves(board, players, current_sym),
result=None, # for terminal state: {player_id: score}
)
Using AIGameClient in your loop
Create client and start match:
client = AIGameClient(
game_id="my_game_id",
api_key="demo-key",
apply_move_fn=apply_move_my_game,
)
match_id = client.start_match(players=players, metadata={"mode": "casual"})
Each turn:
while not finished:
current_sym = ... # your turn logic
legal_moves = compute_legal_moves(board, players, current_sym)
state = build_generic_state(
game_id="my_game_id",
board=board,
players=players,
current_player_symbol=current_sym,
move_count=move_count,
finished=False,
legal_moves=legal_moves,
)
current_player = next(p for p in players if p["symbol"] == current_sym)
if current_player["type"] == "ai_mcts":
# ---- AI turn ----
client.send_state(match_id, state)
move = client.best_move(match_id, iterations=800)
else:
# ---- human turn ----
move = get_human_move_somehow(legal_moves)
# Apply move in your game
apply_move_on_real_board(board, move, current_sym)
# log the move
client.log_move(match_id=match_id, state=state, move=move)
# Update finished / winner using your own logic
finished, winner_symbol = is_terminal_and_winner(board)
move_count += 1
End the match:
result_str, result_map = winner_to_results(winner_symbol, players)
final_state = build_generic_state(
game_id="my_game_id",
board=board,
players=players,
current_player_symbol=current_sym,
move_count=move_count,
finished=True,
legal_moves=[],
result=result_map,
)
client.end_match(
match_id=match_id,
result=result_str,
final_state=final_state,
)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file game_ai_client-0.1.0.tar.gz.
File metadata
- Download URL: game_ai_client-0.1.0.tar.gz
- Upload date:
- Size: 7.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22dffe55f581b995cf9603aedde2a71b4ff2457c53e714d4c18c53e9e6095345
|
|
| MD5 |
79e672bb60415a4910f5b717cc27334f
|
|
| BLAKE2b-256 |
0ff681b45b84ff0caafd672c7db37247453598333a5b8f61de5e06c2a95b6e8b
|
File details
Details for the file game_ai_client-0.1.0-py3-none-any.whl.
File metadata
- Download URL: game_ai_client-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1d98f6a2ca257cabf2e05fa235e5c892af14d5c374f5949909c8501fb33d2fe
|
|
| MD5 |
23ce6430a114f4395c69115324c85e83
|
|
| BLAKE2b-256 |
6dbc01659ca334e50a9eb9bf203acae983f87565a16f7176f5df88f8fe9b4fbf
|