Skip to main content

Head-to-Head Game Integration Guide

Overview

MCAP provides three distinct pathways for providers to integrate Head-to-Head (H2H) multiplayer games with real-money on-chain mechanics. Choose the option that best fits your technical requirements and development timeline.

Integration Options

Embed MCAP's ready-to-use H2H game components directly into your platform. Get professional game interfaces with minimal code - just add an iframe or use our SDK components.

Option 2: MCAP Game Server Integration

Connect to MCAP's hosted game servers (Chess, Battleship) and create custom skins/UIs while we handle the game logic and real-time multiplayer Web3 infrastructure.

Option 3: Custom H2H Game Development (Under Development)

Build your own multiplayer games from scratch while leveraging MCAP's betting infrastructure for match creation, non-custodial fund management, and automated settlement.


Overview

The SDK/iframe integration is the fastest way to add H2H multiplayer games to your platform. MCAP provides ready-to-use UI components and complete game interfaces that you can embed directly into your website or application with minimal code.

Benefits

  • ⚡ Fastest Implementation: Get H2H games live in hours, not weeks
  • 🎨 Professional UI: Polished, battle-tested game interfaces
  • 🔧 Zero Maintenance: We handle all updates, bug fixes, and improvements
  • 📱 Responsive Design: Works seamlessly on desktop and mobile
  • 🎮 Multiple Games: Chess, Battleship, and more games available
  • 🎯 Plug & Play: No game logic development required

Implementation Methods

Method 1: iframe Embedding

The simplest integration method - just add an iframe to your page:

<!-- Basic iframe integration -->
<iframe
src="https://games.mcap.ai/chess?provider=YOUR_PROVIDER_ID&theme=dark"
width="100%"
height="600px"
frameborder="0"
allow="clipboard-write">
</iframe>

<!-- With custom styling -->
<div class="mcap-game-container">
<iframe
id="mcap-chess-game"
src="https://games.mcap.ai/chess?provider=YOUR_PROVIDER_ID&token=USDC&theme=light"
style="width: 100%; height: 80vh; border: none; border-radius: 8px;">
</iframe>
</div>

Supported Parameters:

  • provider: Your provider ID (required)
  • theme: light | dark | auto (default: auto)
  • token: Default token symbol (optional)
  • locale: en | es | fr | etc. (default: en)
  • autoConnect: true | false (default: false)

Method 2: SDK Integration

For more control and better integration with your existing UI:

npm install @mcap/platform-sdk
import { MCAPGames } from '@mcap/platform-sdk';

// Initialize MCAP Games
const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID',
theme: 'dark',
apiUrl: 'https://api.mcap.ai'
});

// Mount a chess game
const chessGame = await mcapGames.mountGame('chess', {
container: '#chess-container',
onMatchCreated: (matchData) => {
console.log('Match created:', matchData);
},
onGameEnd: (result) => {
console.log('Game ended:', result);
// Handle win/loss/draw
},
onError: (error) => {
console.error('Game error:', error);
}
});

// Mount a battleship game
const battleshipGame = await mcapGames.mountGame('battleship', {
container: '#battleship-container',
customStyles: {
primaryColor: '#your-brand-color',
backgroundColor: '#your-bg-color'
}
});

Method 3: React Component Integration

For React applications:

npm install @mcap/react-games
import { ChessGame, BattleshipGame } from '@mcap/react-games';

function MyGamesPage() {
const handleGameEnd = (result: GameResult) => {
if (result.isWinner) {
// Show celebration
showCelebration();
}
// Update user balance, show results, etc.
};

return (
<div className="games-container">
<h2>Play Chess</h2>
<ChessGame
providerId="YOUR_PROVIDER_ID"
theme="dark"
onGameEnd={handleGameEnd}
onMatchCreated={(match) => console.log('Chess match:', match)}
style={{ width: '100%', height: '600px' }}
/>

<h2>Play Battleship</h2>
<BattleshipGame
providerId="YOUR_PROVIDER_ID"
theme="light"
onGameEnd={handleGameEnd}
customStyles={{
primaryColor: '#your-brand-color',
secondaryColor: '#your-secondary-color'
}}
/>
</div>
);
}

Customization Options

Theme Customization

const customTheme = {
colors: {
primary: '#your-primary-color',
secondary: '#your-secondary-color',
background: '#your-bg-color',
text: '#your-text-color',
accent: '#your-accent-color'
},
fonts: {
primary: 'Your Primary Font',
secondary: 'Your Secondary Font'
},
borderRadius: '8px',
shadows: {
card: '0 4px 6px rgba(0, 0, 0, 0.1)',
button: '0 2px 4px rgba(0, 0, 0, 0.1)'
}
};

const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID',
customTheme
});

Localization Support

// Built-in language support
const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID',
locale: 'es', // Spanish
// Other supported: 'en', 'fr', 'de', 'pt', 'ja', 'ko'
});

// Custom translations
const customTranslations = {
'game.chess.title': 'Ajedrez',
'game.battleship.title': 'Batalla Naval',
'button.createMatch': 'Crear Partida',
'button.joinMatch': 'Unirse a Partida'
};

mcapGames.setTranslations('es', customTranslations);

Authentication Integration

The SDK automatically handles user authentication when integrated with your existing auth system:

// Option 1: Pass existing JWT token
const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID',
userToken: userJwtToken // Your existing user JWT
});

// Option 2: Use wallet connection
const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID',
walletProvider: 'metamask' // or 'walletconnect', 'coinbase'
});

// Option 3: Custom authentication
mcapGames.setAuthenticationHandler(async () => {
const walletAddress = await yourWalletProvider.getAddress();
const signature = await yourWalletProvider.signMessage('MCAP Auth');

return {
walletAddress,
signature,
timestamp: Date.now()
};
});

Event Handling

const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID'
});

// Global event listeners
mcapGames.on('matchCreated', (matchData) => {
// Track match creation in your analytics
analytics.track('Match Created', {
gameType: matchData.gameType,
wager: matchData.wager
});
});

mcapGames.on('gameEnd', (result) => {
// Update user stats, show notifications
if (result.isWinner) {
showWinNotification(result.winnings);
updateUserBalance(result.newBalance);
}
});

mcapGames.on('error', (error) => {
// Handle errors gracefully
if (error.code === 'INSUFFICIENT_FUNDS') {
showDepositModal();
} else {
showErrorMessage(error.message);
}
});

// Game-specific events
const chessGame = mcapGames.getGame('chess');
chessGame.on('moveMade', (move) => {
// Track moves for analysis
analytics.track('Chess Move', { move });
});

chessGame.on('checkmate', (winner) => {
// Special celebration for checkmate
showCheckmateAnimation();
});

Advanced Configuration

const mcapGames = new MCAPGames({
providerId: 'YOUR_PROVIDER_ID',

// Performance settings
enablePreloading: true,
cacheGames: true,
optimizeForMobile: true,

// Feature flags
features: {
spectatorMode: true,
gameRecording: true,
socialFeatures: true,
tournaments: true
},

// Analytics integration
analytics: {
provider: 'google-analytics', // or 'mixpanel', 'amplitude'
trackingId: 'YOUR_GA_ID'
},

// Custom API endpoints (for white-label solutions)
apiEndpoints: {
games: 'https://your-custom-domain.com/api/games',
auth: 'https://your-custom-domain.com/api/auth'
}
});

Best Practices

1. Responsive Design

/* Ensure games scale properly */
.mcap-game-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
aspect-ratio: 1; /* For chess */
}

@media (max-width: 768px) {
.mcap-game-container {
height: 70vh;
aspect-ratio: unset;
}
}

2. Loading States

mcapGames.on('gameLoading', (gameType) => {
showLoadingSpinner(`Loading ${gameType}...`);
});

mcapGames.on('gameReady', (gameType) => {
hideLoadingSpinner();
});

3. Error Boundaries

// React Error Boundary for game components
class GameErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error) {
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
console.error('Game Error:', error, errorInfo);
// Report to error tracking service
}

render() {
if (this.state.hasError) {
return (
<div className="game-error">
<h3>Game temporarily unavailable</h3>
<button onClick={() => window.location.reload()}>
Refresh Page
</button>
</div>
);
}

return this.props.children;
}
}

Getting Started with SDK/iframe

  1. Get Your Provider ID: Register at MCAP Provider Portal
  2. Choose Integration Method: iframe, SDK, or React components
  3. Add to Your Site: Copy the code examples above
  4. Customize: Apply your branding and theme
  5. Test: Verify games work in your environment
  6. Go Live: Deploy to production

Estimated Implementation Time: 1-4 hours


Option 2: MCAP Game Server Integration

Overview

Connect to MCAP's hosted game servers for Chess and Battleship. You create custom UIs while we handle game logic, anti-cheat, and real-time multiplayer infrastructure.

Benefits

  • 🎮 Pre-built Games: Chess and Battleship ready to use
  • 🔒 Anti-cheat: Built-in cheat prevention
  • ⚡ Real-time: WebSocket-based instant updates
  • 🎨 Custom UI: Full control over visual design
  • ⚙️ Low Maintenance: We handle game logic updates

Quick Start

  1. Connect via WebSocket:
const ws = new WebSocket(`${MCAP_GAME_SERVER_URL}/ws?playerWalletAddress=${address}`);
  1. Authenticate with Session:
ws.send(JSON.stringify({
type: 'authenticate',
content: { sessionId, gameType: 'chess' }
}));
  1. Handle Game Messages:
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
// Update your UI based on message.type
};
  1. Send Game Actions:
// Chess move example
ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: { action: 'make_move', from: 'e2', to: 'e4' }
}));

Estimated Implementation Time: 1-3 weeks


Option 3: Custom H2H Game Development (Under Development)

Overview

Build your own multiplayer games from scratch while leveraging MCAP's betting infrastructure for match creation, non-custodial fund management, and automated settlement.

Benefits

  • 🎯 Full Control: Build any game type with custom rules
  • 📈 Scalable: Handle unlimited concurrent players
  • 💰 Revenue Share: Keep more revenue vs pre-built options
  • 🔧 Flexible: Implement complex game mechanics

Quick Start

  1. Authenticate Users:
// Get nonce → User signs → Exchange for JWT
const { nonce } = await fetch('/v1/auth/nonce', { ... });
const signature = await wallet.signMessage(nonce);
const { access_token } = await fetch('/v1/auth/login', { ... });
  1. Create Sessions:
const { session_id } = await fetch('/v1/sessions', {
method: 'POST',
headers: { 'Authorization': `Bearer ${access_token}` },
body: JSON.stringify({ token_address, amount, provider_id, game_id })
});
  1. Manage Matches:
// Create match
const match = await fetch('/v1/user/multiplayer-bets/matches', {
method: 'POST',
body: JSON.stringify({ creator_session_id, wager })
});

// Join match
await fetch(`/v1/user/multiplayer-bets/matches/${matchId}/join`, {
method: 'POST',
body: JSON.stringify({ joiner_session_id })
});

// Settle match
await fetch(`/v1/user/multiplayer-bets/matches/${matchId}/settle`, {
method: 'POST',
body: JSON.stringify({ winner_session_id, is_draw })
});
  1. Implement Game Logic:
class GameManager {
async handlePlayerMove(matchId, playerId, moveData) {
// Validate move
// Update game state
// Check for win/draw
// Call settlement if game ended
}
}

Estimated Implementation Time: 2-8 weeks


Comparison Table

AspectSDK/iframeMCAP ServersCustom Development
Development Time1-4 hours1-3 weeks2-8 weeks
Game Logic ControlPre-builtPre-builtFull control
UI CustomizationThemes onlyFull custom UIUnlimited
Anti-cheatBuilt-inBuilt-inYou implement
MaintenanceZeroGame logic onlyFull maintenance
Technical ComplexityVery lowMediumHigh
Revenue ShareHigherMediumLower

Getting Started

  1. Register as Provider: Get your API keys and provider ID
  2. Choose Integration: Pick the option that fits your needs
  3. Follow Documentation: Use the specific implementation guide
  4. Test Integration: Verify everything works in staging
  5. Go Live: Deploy to production

Support


Core Integration Flow

1. Player Authentication & Session Creation

First, authenticate players and create gaming sessions:

// Step 1: Get authentication nonce
const nonceResponse = await fetch(`${MCAP_API_URL}/v1/auth/nonce`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
wallet_address: playerWallet,
provider_id: YOUR_PROVIDER_ID
})
});

// Step 2: Player signs the nonce with their wallet
const message = `Sign this message to authenticate with nonce: ${nonce}`;
const signature = await playerWallet.signMessage(message);

// Step 3: Login to get JWT tokens
const loginResponse = await fetch(`${MCAP_API_URL}/v1/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
wallet_address: playerWallet,
provider_id: YOUR_PROVIDER_ID,
signature,
nonce
})
});

const { access_token } = await loginResponse.json();

// Step 4: Create gaming session
const sessionResponse = await fetch(`${MCAP_API_URL}/v1/sessions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
token_id: SELECTED_TOKEN_ID,
game_id: YOUR_GAME_ID
})
});

const { session_id } = await sessionResponse.json();

2. Match Creation and Management

Use MCAP's multiplayer match endpoints to handle betting mechanics:

// Create a new H2H match
const createMatch = async (creatorSessionId: string, wager: string) => {
const response = await fetch(`${MCAP_API_URL}/v1/providers/multiplayer-bets/matches`, {
method: 'POST',
headers: {
'X-API-Key': YOUR_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
creator_id: playerWalletAddress,
session_id: creatorSessionId,
provider_id: YOUR_PROVIDER_ID,
game_type: 'your_custom_game',
game_id: YOUR_GAME_ID,
min_players: 2,
max_players: 2,
wager: wager, // Amount in wei
expiry_hours: 24
})
});

return await response.json();
};

// Join an existing match
const joinMatch = async (matchId: string, playerSessionId: string) => {
const response = await fetch(`${MCAP_API_URL}/v1/providers/multiplayer-bets/matches/${matchId}/join`, {
method: 'POST',
headers: {
'X-API-Key': YOUR_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
provider_id: YOUR_PROVIDER_ID,
session_id: playerSessionId
})
});

return await response.json();
};

// Start the match (locks funds)
const startMatch = async (matchId: string, creatorWalletId: string) => {
const response = await fetch(`${MCAP_API_URL}/v1/providers/multiplayer-bets/matches/${matchId}/start`, {
method: 'POST',
headers: {
'X-API-Key': YOUR_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
match_id: matchId,
wallet_id: creatorWalletId
})
});

return await response.json();
};

3. Game Logic Implementation

Implement your custom game mechanics. Here's a simplified example structure:

interface GameState {
matchId: string;
players: Player[];
currentTurn: string;
gameData: any; // Your custom game state
status: 'waiting' | 'in_progress' | 'finished';
}

class CustomH2HGame {
private gameState: GameState;

constructor(matchId: string) {
this.gameState = {
matchId,
players: [],
currentTurn: '',
gameData: {},
status: 'waiting'
};
}

// Handle player moves
async makeMove(playerId: string, moveData: any): Promise<boolean> {
// Validate move
if (!this.isValidMove(playerId, moveData)) {
return false;
}

// Apply move to game state
this.applyMove(playerId, moveData);

// Check for game end
const winner = this.checkGameEnd();
if (winner) {
await this.settleMatch(winner);
} else {
this.switchTurn();
}

// Broadcast state to all players
this.broadcastGameState();

return true;
}

// Your custom game logic methods
private isValidMove(playerId: string, moveData: any): boolean {
// Implement your validation logic
return true;
}

private applyMove(playerId: string, moveData: any): void {
// Apply the move to your game state
}

private checkGameEnd(): string | null {
// Return winner ID or null if game continues
return null;
}

private switchTurn(): void {
// Switch to next player's turn
}
}

4. Match Settlement

When the game ends, settle the match through MCAP smart contracts:

const settleMatch = async (matchId: string, winnerId: string, totalPot: string) => {
const payouts = [
{
player_id: winnerId,
amount: totalPot // Winner takes all (in wei)
},
{
player_id: loserId,
amount: "0" // Loser gets nothing (0 wei)
}
];

const response = await fetch(`${MCAP_API_URL}/v1/providers/multiplayer-bets/matches/${matchId}/settle`, {
method: 'POST',
headers: {
'X-API-Key': YOUR_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
match_id: matchId,
payouts
})
});

return await response.json();
};

// For draws, split the pot
const settleDraw = async (matchId: string, playerIds: string[], wagerAmount: string) => {
const payouts = playerIds.map(playerId => ({
player_id: playerId,
amount: wagerAmount // Each player gets their wager back
}));

// ... same settlement call with different payouts
};

Real-time Communication

For real-time gameplay, implement WebSocket connections:

// Server-side WebSocket handler (Node.js example)
import WebSocket from 'ws';

class GameWebSocketServer {
private wss: WebSocket.Server;
private games: Map<string, CustomH2HGame>;

constructor(port: number) {
this.wss = new WebSocket.Server({ port });
this.games = new Map();
this.setupHandlers();
}

private setupHandlers() {
this.wss.on('connection', (ws: WebSocket, req) => {
const playerId = req.url?.split('playerId=')[1];

ws.on('message', async (data) => {
const message = JSON.parse(data.toString());

switch (message.type) {
case 'join_game':
await this.handleJoinGame(ws, playerId, message.matchId);
break;
case 'make_move':
await this.handleMove(ws, playerId, message);
break;
}
});
});
}

private async handleMove(ws: WebSocket, playerId: string, message: any) {
const game = this.games.get(message.matchId);
if (game) {
const success = await game.makeMove(playerId, message.moveData);
ws.send(JSON.stringify({ type: 'move_result', success }));
}
}
}

Best Practices for Custom Development

1. State Synchronization

  • Maintain authoritative game state on your server
  • Validate all moves server-side
  • Use optimistic updates on client with rollback capability
  • Implement conflict resolution for simultaneous moves

2. Security Considerations

  • Never trust client-side game state
  • Validate session tokens on every game action
  • Implement anti-cheat mechanisms
  • Rate limit player actions

3. Error Handling

class GameErrorHandler {
static handleMatchError(error: any, matchId: string) {
if (error.code === 'INSUFFICIENT_FUNDS') {
// Handle insufficient balance
return { type: 'error', message: 'Insufficient funds to join match' };
}

if (error.code === 'MATCH_EXPIRED') {
// Handle expired match
return { type: 'error', message: 'Match has expired' };
}

// Log unexpected errors
console.error('Unexpected match error:', error);
return { type: 'error', message: 'An unexpected error occurred' };
}
}

4. Performance Optimization

  • Implement connection pooling for MCAP API calls
  • Cache session information with appropriate TTL
  • Use batch operations where possible
  • Implement proper database indexing for game state

Option 3: Custom H2H Game Development

Overview

MCAP hosts production-ready game servers for Chess and Battleship. You can connect to these servers via WebSocket and create custom UIs while we handle all game logic, anti-cheat, and real-time multiplayer infrastructure.

Supported Games

Chess

  • Full chess rule implementation including castling, en passant
  • Move validation and check/checkmate detection
  • PGN notation support
  • Tournament-ready

Battleship

  • Classic 10x10 grid battleship
  • Ship placement validation
  • Hit/miss tracking
  • Automatic win detection

Integration Architecture

graph TD
A[Your Custom UI] -->|WebSocket| B[MCAP Game Server]
B -->|Match Management| C[MCAP API]
B -->|Real-time Updates| A
C -->|Automated Settlement| D[Smart Contracts]

E[Player Wallets] -->|Deposits| D
D -->|Payouts| E

Connection Setup

1. Establish WebSocket Connection

// Connect to MCAP game server
const connectToGameServer = (playerWalletAddress: string) => {
const wsUrl = `${MCAP_GAME_SERVER_URL}/ws?playerWalletAddress=${playerWalletAddress}`;
const ws = new WebSocket(wsUrl);

ws.onopen = () => {
console.log('Connected to MCAP game server');
};

ws.onmessage = (event) => {
const message = JSON.parse(event.data);
handleGameMessage(message);
};

return ws;
};

2. Authentication and Session Creation

Players still need to authenticate and create sessions as in Option 1, but the session is used by the MCAP game server:

// After getting session_id from authentication flow
const joinGameServer = (ws: WebSocket, sessionId: string, gameType: 'chess' | 'battleship') => {
ws.send(JSON.stringify({
type: 'authenticate',
content: {
sessionId: sessionId,
gameType: gameType
}
}));
};

Game-Specific Integration

Chess Integration

interface ChessGameState {
gameId: string;
matchId: string;
board: ChessPiece[][];
currentTurn: string;
moveHistory: ChessMove[];
gameStatus: 'waiting_for_players' | 'waiting_for_start' | 'player_turn' | 'opponent_turn' | 'game_over';
players: {
white: string;
black: string;
};
}

class ChessGameClient {
private ws: WebSocket;
private gameState: ChessGameState | null = null;

constructor(ws: WebSocket) {
this.ws = ws;
this.setupMessageHandlers();
}

// Create a new chess match
createMatch(sessionId: string, wager: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
content: {
action: 'create_match',
sessionId,
wager
}
}));
}

// Join existing match
joinMatch(matchId: string, sessionId: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
content: {
action: 'join_match',
matchId,
sessionId
}
}));
}

// Start the match (creator only)
startMatch(matchId: string, sessionId: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
content: {
action: 'start_match',
matchId,
sessionId
}
}));
}

// Make a chess move
makeMove(gameId: string, from: string, to: string, promotion?: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: {
action: 'make_move',
from, // e.g., 'e2'
to, // e.g., 'e4'
promotion // 'Q', 'R', 'B', 'N' for pawn promotion
}
}));
}

// Resign from game
resign(gameId: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: {
action: 'resign'
}
}));
}

// Offer/accept/decline draw
offerDraw(gameId: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: {
action: 'offer_draw'
}
}));
}

private setupMessageHandlers() {
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);

switch (message.type) {
case 'match_created':
this.handleMatchCreated(message.content);
break;
case 'waiting_for_start':
this.handleWaitingForStart(message.content);
break;
case 'match_started':
this.handleMatchStarted(message.content);
break;
case 'move_made':
this.handleMoveMade(message.content);
break;
case 'game_ended':
this.handleGameEnded(message.content);
break;
case 'draw_offered':
this.handleDrawOffered(message.content);
break;
case 'error':
this.handleError(message.content);
break;
}
};
}

private handleMoveMade(content: any) {
// Update your UI with the new move
this.gameState = content;
this.renderChessBoard(content.board);
this.updateGameStatus(content.state);

if (content.isCheck) {
this.showCheckNotification();
}
}

private handleGameEnded(content: any) {
this.gameState = content;

if (content.isDraw) {
this.showDrawResult();
} else {
this.showWinnerResult(content.isWinner, content.settlement);
}
}

// Implement your UI update methods
private renderChessBoard(board: ChessPiece[][]) {
// Update your chess board UI
}

private updateGameStatus(status: string) {
// Update game status display
}
}

Battleship Integration

interface BattleshipGameState {
gameId: string;
matchId: string;
phase: 'placing_ships' | 'battle' | 'game_over';
currentTurn: string;
myBoard: BattleshipCell[][];
opponentBoard: BattleshipCell[][];
ships: Ship[];
gameStatus: string;
}

class BattleshipGameClient {
private ws: WebSocket;
private gameState: BattleshipGameState | null = null;

constructor(ws: WebSocket) {
this.ws = ws;
this.setupMessageHandlers();
}

// Place ships during setup phase
placeShip(gameId: string, shipType: string, positions: string[]) {
this.ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: {
action: 'place_ship',
shipType, // 'carrier', 'battleship', 'cruiser', 'submarine', 'destroyer'
positions // ['A1', 'A2', 'A3', 'A4', 'A5'] for carrier
}
}));
}

// Confirm ship placement and ready for battle
confirmPlacement(gameId: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: {
action: 'confirm_placement'
}
}));
}

// Fire at opponent's board
fireShot(gameId: string, target: string) {
this.ws.send(JSON.stringify({
type: 'game_action',
gameId,
content: {
action: 'fire_shot',
target // e.g., 'B5'
}
}));
}

private setupMessageHandlers() {
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);

switch (message.type) {
case 'ship_placed':
this.handleShipPlaced(message.content);
break;
case 'placement_confirmed':
this.handlePlacementConfirmed(message.content);
break;
case 'battle_started':
this.handleBattleStarted(message.content);
break;
case 'shot_result':
this.handleShotResult(message.content);
break;
case 'game_ended':
this.handleGameEnded(message.content);
break;
}
};
}

private handleShotResult(content: any) {
const { target, result, isYourTurn } = content;

// Update board with hit/miss/sunk
this.updateBoardWithShot(target, result);

// Update turn indicator
this.updateTurnStatus(isYourTurn);

if (result === 'sunk') {
this.showShipSunkAnimation(content.sunkShip);
}
}

private updateBoardWithShot(target: string, result: 'hit' | 'miss' | 'sunk') {
// Update your battleship board UI
}
}

UI Development Best Practices

1. Responsive Design

/* Example responsive chess board */
.chess-board {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
aspect-ratio: 1;
max-width: min(80vw, 80vh);
margin: 0 auto;
}

.chess-square {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.2s;
}

.chess-square.light {
background-color: #f0d9b5;
}

.chess-square.dark {
background-color: #b58863;
}

.chess-square.selected {
background-color: #646f40;
}

.chess-square.valid-move {
background-color: #fffacd;
}

2. Real-time Updates

// Handle real-time state synchronization
class GameStateManager {
private listeners: Map<string, Function[]> = new Map();

subscribe(event: string, callback: Function) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(callback);
}

emit(event: string, data: any) {
const callbacks = this.listeners.get(event) || [];
callbacks.forEach(callback => callback(data));
}

updateGameState(newState: any) {
// Merge new state with existing state
this.gameState = { ...this.gameState, ...newState };
this.emit('stateChanged', this.gameState);
}
}

3. Error Handling and Reconnection

class RobustWebSocketClient {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectInterval = 1000;

constructor(private url: string) {
this.connect();
}

private connect() {
try {
this.ws = new WebSocket(this.url);

this.ws.onopen = () => {
this.reconnectAttempts = 0;
this.onConnected();
};

this.ws.onclose = () => {
this.attemptReconnect();
};

this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};

} catch (error) {
this.attemptReconnect();
}
}

private attemptReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
setTimeout(() => {
this.reconnectAttempts++;
this.connect();
}, this.reconnectInterval * Math.pow(2, this.reconnectAttempts));
}
}

send(data: any) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
} else {
console.warn('WebSocket not connected, message queued');
// Queue message for when connection is restored
}
}
}

For questions about H2H integration or to discuss your specific use case, reach out to our team at support@mcap.ai