Skip to main content

Welcome Contributors!

Thank you for your interest in contributing to BeeHex! This guide will help you get started with contributing code, reporting issues, and improving documentation.

Getting Started

1

Set Up Development Environment

Follow the Development Setup guide to get BeeHex running locally.
2

Explore the Codebase

Familiarize yourself with:
  • Project structure in src/app/ and src/components/
  • TypeScript interfaces in src/app/definitions.ts
  • WebSocket handling in src/app/game_mode/WebsocketHandler.ts
3

Find an Issue

Look for issues labeled:
  • good first issue - Great for newcomers
  • help wanted - Need community assistance
  • bug - Bug fixes needed
  • enhancement - New features
4

Start Contributing

Follow the workflow below to make your contribution.

Development Workflow

1. Fork and Clone

1

Fork the Repository

Click the Fork button on GitHub to create your copy of BeeHex.
2

Clone Your Fork

git clone https://github.com/YOUR_USERNAME/beehex_v2.git
cd beehex_v2
3

Add Upstream Remote

git remote add upstream https://github.com/ORIGINAL_OWNER/beehex_v2.git

2. Create a Branch

Create a descriptive branch name:
# For new features
git checkout -b feature/add-game-timer

3. Make Changes

Write clean, well-documented code:
// Good: Clear function with JSDoc comment
/**
 * Validates a move on the Hex board
 * @param x - X coordinate (0 to boardSize-1)
 * @param y - Y coordinate (0 to boardSize-1)
 * @param grid - Current game grid state
 * @returns true if move is valid, false otherwise
 */
function validateMove(x: number, y: number, grid: number[][]): boolean {
  // Check bounds
  if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) {
    return false;
  }
  
  // Check if cell is empty
  return grid[y][x] === 0;
}

4. Follow Code Standards

TypeScript Style Guide

BeeHex uses TypeScript with strict mode. Always define proper types!
// ✅ Good: Proper typing
interface GameState {
  gameId: string;
  board: number[][];
  currentPlayer: number;
  status: GameStatus;
}

function updateGameState(state: GameState): GameState {
  return { ...state, currentPlayer: state.currentPlayer === 1 ? 2 : 1 };
}

// ❌ Bad: Using any
function updateGameState(state: any): any {
  return { ...state, currentPlayer: state.currentPlayer === 1 ? 2 : 1 };
}

React Component Best Practices

// ✅ Good: Functional component with proper types
import React from 'react';

interface GameBoardProps {
  size: number;
  onCellClick: (x: number, y: number) => void;
  grid: number[][];
}

export default function GameBoard({ size, onCellClick, grid }: GameBoardProps) {
  return (
    <div className="game-board">
      {/* Component implementation */}
    </div>
  );
}

// ❌ Bad: No prop types
export default function GameBoard(props) {
  return <div>{/* Implementation */}</div>;
}

Code Organization

  • Components: One component per file in src/components/
  • Pages: Use Next.js App Router structure in src/app/
  • Types: Define shared types in src/app/definitions.ts
  • Utilities: Helper functions in separate utility files

5. Test Your Changes

1

Run the Development Server

npm run dev
Test your changes at http://localhost:3000
2

Check TypeScript

# Verify no type errors
npx tsc --noEmit
3

Run Linter

npm run lint
Fix any linting errors:
npm run lint -- --fix
4

Test Different Scenarios

  • Test on different browsers (Chrome, Firefox, Safari)
  • Test different board sizes (5x5, 7x7, 9x9)
  • Test online and offline modes
  • Verify WebSocket reconnection handling
5

Build for Production

npm run build
Ensure the build completes without errors.

6. Commit Your Changes

Commit Message Format

Follow conventional commit standards:
git commit -m "feat: add game timer with countdown display"

Commit Message Guidelines

  • Type: feat, fix, docs, style, refactor, test, chore
  • Subject: Short description (max 72 characters)
  • Body (optional): Detailed explanation
  • Footer (optional): Breaking changes, issue references
Example:
feat: add move history replay feature

Implements a replay system that allows users to step through
previous moves in completed games. Adds navigation controls
and updates the board state for each move.

Closes #123

7. Push and Create Pull Request

1

Push Your Branch

git push origin feature/your-feature-name
2

Create Pull Request

  1. Go to your fork on GitHub
  2. Click “Compare & pull request”
  3. Fill out the PR template (see below)
  4. Click “Create pull request”

Pull Request Template

## Description
Brief description of what this PR does.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update

## Testing
Describe the tests you ran:
- [ ] Tested on Chrome, Firefox, Safari
- [ ] Tested different board sizes
- [ ] Tested online multiplayer
- [ ] Tested offline mode
- [ ] Verified no TypeScript errors
- [ ] Ran linter

## Screenshots (if applicable)
Add screenshots to demonstrate visual changes.

## Related Issues
Closes #issue_number

## Additional Notes
Any additional information reviewers should know.

Code Review Process

After submitting your PR:
  1. Automated Checks: CI/CD runs linter and builds
  2. Code Review: Maintainers review your code
  3. Feedback: Address any requested changes
  4. Approval: PR is approved by maintainers
  5. Merge: Your contribution is merged!
Be patient and responsive to feedback. Reviews may take a few days.

Contribution Areas

🐛 Bug Fixes

Found a bug? Great!
1

Check Existing Issues

Search GitHub issues to see if it’s already reported.
2

Create Bug Report

If not reported, create a detailed issue:
  • Title: Clear, descriptive title
  • Description: What happened vs. what should happen
  • Steps to Reproduce: Detailed steps
  • Environment: Browser, OS, Node.js version
  • Screenshots: Visual evidence if applicable
3

Fix the Bug

Follow the workflow above to create a fix.

✨ New Features

Want to add a feature?
  1. Discuss First: Open an issue to discuss the feature before implementing
  2. Get Approval: Wait for maintainer feedback
  3. Implement: Follow the development workflow
  4. Document: Update documentation for your feature

📚 Documentation

Improve or add documentation:
  • Fix typos and grammatical errors
  • Add code examples
  • Improve existing explanations
  • Add new guides or tutorials
  • Update outdated information

🎨 UI/UX Improvements

  • Improve component styling
  • Enhance responsive design
  • Add animations and transitions
  • Improve accessibility (WCAG compliance)
  • Better color contrast and themes

🚀 Performance Optimization

  • Reduce bundle size
  • Optimize component rendering
  • Improve WebSocket efficiency
  • Add caching strategies
  • Optimize images and assets

Project Architecture

Key Files and Directories

definitions.ts

src/app/definitions.tsCore TypeScript interfaces and enums for game logic, packets, and state management.

WebsocketHandler.ts

src/app/game_mode/WebsocketHandler.tsWebSocket client for real-time communication with game server.

layout.tsx

src/app/layout.tsxRoot layout component with metadata and global styles.

env.ts

src/env/env.tsEnvironment configuration (gitignored).

Technology Stack

TechnologyVersionPurpose
Next.js14.2.20React framework with App Router
React18.xUI library
TypeScript5.xType safety
Styled Components6.1.13CSS-in-JS styling
Socket.IO Client4.7.5Real-time communication
ECharts5.6.0Data visualization
Axios1.7.2HTTP client
SweetAlert2.1.2Modal dialogs

WebSocket Protocol

BeeHex uses custom WebSocket packets defined in definitions.ts:

Client-Bound Packets (Server → Client)

enum ClientBoundPacketType {
  ERROR_MESSAGE = 0,      // Error notifications
  GAME_SEARCH = 1,        // Game search status
  GAME_FOUND = 2,         // Game found notification
  JOIN_GAME = 3,          // Join game with initial state
  MOVE_PLAYED = 4,        // Move update
  GAME_END = 5            // Game end with result
}

Server-Bound Packets (Client → Server)

enum ServerBoundPacketType {
  GAME_SEARCH = 0,        // Start searching for game
  CANCEL_GAME_SEARCH = 1, // Cancel search
  JOIN_GAME = 2,          // Join specific game
  JOIN_ROOM = 3,          // Join room
  PLAY_MOVE = 4,          // Play a move
  FORFEIT_GAME = 5        // Forfeit the game
}

Game State Management

interface Game {
  game_id: string;
  game_parameters: GameParameters;
  grid: Array<Array<number>>;  // 0 = empty, 1 = player1, 2 = player2
  first_player_id: string;
  second_player_id: string;
  turn: number;  // Current turn number
}

interface GameParameters {
  ranked: boolean;      // Is this a ranked game?
  board_size: number;   // 5, 7, or 9
  time_limit: number;   // Time limit in seconds (0 = unlimited)
}

Common Tasks

Adding a New Component

1

Create Component Directory

mkdir src/components/my_component
2

Create Component Files

src/components/my_component/my_component.tsx
import React from 'react';
import styles from './my_component.module.css';

interface MyComponentProps {
  title: string;
  onClick?: () => void;
}

export default function MyComponent({ title, onClick }: MyComponentProps) {
  return (
    <div className={styles.container} onClick={onClick}>
      <h2>{title}</h2>
    </div>
  );
}
src/components/my_component/my_component.module.css
.container {
  padding: 1rem;
  background: #f5f5f5;
  border-radius: 8px;
}

.container:hover {
  background: #e0e0e0;
}
3

Use Component

src/app/page.tsx
import MyComponent from '@/components/my_component/my_component';

export default function Home() {
  return (
    <MyComponent 
      title="Hello BeeHex" 
      onClick={() => console.log('Clicked!')} 
    />
  );
}

Adding a New Page Route

1

Create Route Directory

mkdir src/app/new_page
2

Create Page Component

src/app/new_page/page.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'New Page - BeeHex',
  description: 'Description of the new page',
};

export default function NewPage() {
  return (
    <div>
      <h1>New Page</h1>
      <p>Content goes here</p>
    </div>
  );
}
3

Add Styling

src/app/new_page/page.module.css
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
}

Handling WebSocket Events

import { WebsocketHandler } from '@/app/game_mode/WebsocketHandler';
import * as packets from '@/app/definitions';

// Create WebSocket handler with callbacks
const ws = new WebsocketHandler({
  errorCallback: (message) => {
    console.error('Error:', message);
    // Show error to user
  },
  
  gameSearchCallback: (params, playerCount, eloRange) => {
    console.log(`Searching: ${playerCount} players`);
    // Update UI with search status
  },
  
  gameFoundCallback: (gameId) => {
    console.log('Game found:', gameId);
    // Navigate to game page
  },
  
  joinGameCallback: (game) => {
    console.log('Joined game:', game);
    // Initialize game board
  },
  
  movePlayedCallback: (x, y, turn, grid) => {
    console.log(`Move played at (${x}, ${y})`);
    // Update game board
  },
  
  connectionEndedCallback: () => {
    console.log('Connection lost');
    // Show reconnection UI
  }
});

// Send packet to server
const searchPacket: packets.ServerBoundGameSearchPacket = {
  type: packets.ServerBoundPacketType.GAME_SEARCH,
  game_parameters: {
    ranked: true,
    board_size: 7,
    time_limit: 0
  }
};

ws.sendPacket(searchPacket);

Style Guide

Component Structure

// 1. Imports
import React, { useState, useEffect } from 'react';
import styles from './component.module.css';
import { Game } from '@/app/definitions';

// 2. Interface definitions
interface ComponentProps {
  game: Game;
  onMove: (x: number, y: number) => void;
}

// 3. Component definition
export default function Component({ game, onMove }: ComponentProps) {
  // 4. State and hooks
  const [selectedCell, setSelectedCell] = useState<[number, number] | null>(null);
  
  // 5. Effects
  useEffect(() => {
    // Effect logic
  }, [game]);
  
  // 6. Event handlers
  const handleCellClick = (x: number, y: number) => {
    setSelectedCell([x, y]);
    onMove(x, y);
  };
  
  // 7. Render
  return (
    <div className={styles.container}>
      {/* JSX */}
    </div>
  );
}

Naming Conventions

  • Components: PascalCase (GameBoard, PlayerCard)
  • Files: snake_case for directories, PascalCase for React components
  • Functions: camelCase (handleMove, validateInput)
  • Constants: UPPER_SNAKE_CASE (BOARD_SIZES, TIME_LIMITS)
  • Types/Interfaces: PascalCase (GameState, PlayerInfo)

Getting Help

Need assistance?
  • GitHub Issues: Ask questions in issues
  • Discussions: Use GitHub Discussions for general questions
  • Documentation: Check technical documentation
  • Code Examples: Look at existing components for patterns

Recognition

Contributors are recognized in:
  • GitHub contributors page
  • Project README
  • Release notes for significant contributions
Thank you for contributing to BeeHex! 🐝

Next Steps

Setup Guide

Set up your development environment

Architecture Overview

Understand the system architecture

Deployment

Learn about deployment processes

WebSocket Protocol

Explore the WebSocket communication protocol