Full‑Stack Gomoku Game with Vue 3, NestJS, AI Opponent, and Real‑Time Multiplayer
This article presents a complete Gomoku web application built with Vue 3 on the front end and NestJS on the back end, featuring customizable pieces, stylish UI, AI opponents of varying difficulty, and real‑time LAN multiplayer using Socket.io, along with detailed code examples and architectural explanations.
The author created a modern Gomoku (five‑in‑a‑row) game to replace ad‑filled mini‑programs, offering a pink‑themed UI, customizable pieces, and AI opponents, and provides a GitHub repository for local deployment.
Project Demo – The game runs locally at localhost:8888 after cloning the repository and executing pnpm -F front-end dev and pnpm -F back-end nest . It supports AI, local, and LAN matches.
Technology Stack Overview
Frontend : Vue 3 (Composition API), Tailwind CSS, Socket.io‑client, Vite.
Backend : NestJS, WebSocket (Socket.io), TypeScript.
Architecture : pnpm‑based monorepo with shared workspace configuration, unified dependency management, atomic commits, and a simplified workflow.
Project structure:
wuziqi-fullstack/
├── back-end/ # NestJS backend service
│ ├── src/
│ │ ├── app.controller.ts
│ │ ├── app.service.ts
│ │ ├── socketio/ # WebSocket communication module
│ │ └── main.ts # Application entry
├── front-end/ # Vue 3 frontend application
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── composables/ # Reusable logic
│ │ ├── plugins/ # Plugins
│ │ └── lang/ # Internationalization
├── package.json # Workspace config
└── pnpm-workspace.yaml # Workspace definitionCore Technical Implementations
1. Chessboard Component Design
The board state is stored in a Map for clear lookup, and the component uses Vue's Composition API to expose functions such as putDownPiece and validSuccess . Key excerpt:
import { ref, computed } from 'vue';
export function useChessboard(socket, active, disabled, resetGame) {
const rows = ref(10);
const cols = ref(10);
const boxMap = new Map();
// initialize board, handle piece placement, win detection, etc.
// ... (full implementation omitted for brevity)
return { rows, cols, boxMap, putDownPiece, validSuccess, resetChessboard };
}2. Multi‑Level AI Algorithm
The AI supports three difficulty levels: easy (random move), medium (better move), and hard (best move). Example snippet:
import { ref, watch } from 'vue';
export function useAI(boxMap, active, rows, cols, validSuccess, emitChessboard, resetGame) {
const isAIMode = ref(false);
const aiDifficulty = ref('medium');
function findRandomMove() { /* collect empty positions and pick one */ }
function findWinningMove(player) { /* simulate placement and test win */ }
function aiMove() {
if (!isAIMode.value || active.value !== 'blackPlayer') return;
setTimeout(() => {
let move;
if (aiDifficulty.value === 'easy') move = findRandomMove();
else if (aiDifficulty.value === 'medium') move = findBetterMove();
else move = findBestMove();
if (move) {
boxMap.set(`row${move.row}col${move.col}`, { empty: false, belongsTo: 'blackPlayer' });
emitChessboard({ row: move.row, col: move.col }, 'blackPlayer');
if (validSuccess(move.row, move.col, 'blackPlayer')) {
alert('AI获胜了!');
resetGame();
} else {
active.value = 'whitePlayer';
}
}
}, 800);
}
watch(active, (newValue) => { if (isAIMode.value && newValue === 'blackPlayer') aiMove(); });
return { isAIMode, aiDifficulty, toggleAIMode, setAIDifficulty, aiMove };
}3. Socket.io Real‑Time Communication
Frontend client creates a socket instance and registers it globally:
import { io } from "socket.io-client";
export default {
install(app, { connection, options }) {
const socket = io(connection, options);
app.config.globalProperties.$socket = socket;
app.provide('socket', socket);
socket.on('connect', () => console.log('Socket连接成功'));
socket.on('disconnect', () => console.log('Socket连接断开'));
}
};Backend gateway broadcasts board updates to all clients:
import { WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer } from '@nestjs/websockets';
import { SocketioService } from './socketio.service';
@WebSocketGateway({ cors: true })
export class SocketioGateway {
@WebSocketServer() server;
constructor(private readonly socketioService: SocketioService) {}
@SubscribeMessage('chessboard')
chessboard(@MessageBody() { location, belongsTo }, @ConnectedSocket() socket) {
const updatedChessboard = this.socketioService.chessboard(location, belongsTo);
this.server.sockets.sockets.forEach(s => s.emit('currentChessboard', updatedChessboard));
console.log('Sender socket ID:', socket.id);
return { event: 'currentChessboard', data: { ...updatedChessboard, socketId: socket.id } };
}
}Technical Challenges and Solutions
1. Win Detection Algorithm – Checks horizontal, vertical, and both diagonal directions from the last placed piece using a Map for O(1) cell access. Sample function:
function validSuccess(row, col, active) {
// check horizontal, vertical, diagonal‑left, diagonal‑right
// return true if five consecutive pieces belong to the same player
}Optimizations include limiting checks to the last move and early termination when a direction cannot reach five.
2. Real‑Time State Synchronization – Server acts as the authoritative source; after receiving a move it validates, updates the board, and broadcasts the new state to all clients, using Socket IDs to avoid duplicate handling.
function emitChessboard(location, belongsTo) {
socket.emit('chessboard', { location, belongsTo }, data => console.log('chessboard:', data));
}
@SubscribeMessage('chessboard')
chessboard(@MessageBody() { location, belongsTo }, @ConnectedSocket() socket) {
const updated = this.socketioService.chessboard(location, belongsTo);
this.server.sockets.sockets.forEach(s => s.emit('currentChessboard', updated));
return { event: 'currentChessboard', data: { ...updated, socketId: socket.id } };
}3. Internationalization – Simple i18n support with language packs (e.g., Chinese) registered in main.js .
Future Plans
Feature extensions: game replay, leaderboard, multi‑room support.
Technical optimizations: more intelligent AI (minimax, alpha‑beta pruning), rendering performance improvements, PWA offline support.
User experience enhancements: theme customization, sound effects, mobile‑friendly touch controls.
Conclusion – The project demonstrates a full‑stack implementation of Gomoku with Vue 3 and NestJS, covering UI design, AI logic, real‑time communication, and extensible architecture, offering a practical reference for developers interested in building interactive web games.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.