MỤC LỤC
MỤC LỤC .........................................................................................................................................2
MỞ ĐẦU ...........................................................................................................................................4
1. Mục đích đề tài ..............................................................................................................................4
2. Nhiệm vụ của đề tài .......................................................................................................................4
2.1. Thiết kế giao diện người dùng ........................................................................................4
2.2. Thiết kế giao thức truyền thông ......................................................................................4
2.3. Phát triển logic trò chơi ..................................................................................................4
2.4. Phân công nhiệm vụ thành viên ......................................................................................4
CHƯƠNG 1: TỔNG QUAN VỀ HỆ THỐNG GAME BẮN TÀU TRỰC TUYẾN ........................5
1.1. Giới thiệu tổng quan về game bắn tàu ..................................................................................5
1.2. Các công nghệ sử dụng .........................................................................................................5
1.2.1. ASP.NET Core MVC ...................................................................................................5
1.2.2. WebSocket ...................................................................................................................6
1.2.3. HTML, CSS, JavaScript ..............................................................................................6
1.3. Kiến trúc hệ thống ................................................................................................................6
CHƯƠNG 2: PHÂN TÍCH THIẾT KẾ HỆ THỐNG ........................................................................6
2.1. Phân tích yêu cầu ..................................................................................................................6
2.2. Thiết kế cơ sở dữ liệu ...........................................................................................................7
2.3. Thiết kế luồng hoạt động chính ............................................................................................8
2.3.1. Luồng tạo và tham gia phòng chơi ..............................................................................8
2.3.2. Luồng đặt tàu ...............................................................................................................8
2.3.3. Luồng tấn công và xử lý kết quả .................................................................................9
2.4. Thiết kế giao thức truyền thông WebSocket .........................................................................9
2.4.1. Cấu trúc bản tin cơ bản (GameMessage) .....................................................................9
2.4.2. Các bản tin từ Client lên Server và từ Server xuống Client ........................................9
2.5. Lưu đồ thuật toán các chức năng chính ..............................................................................14
2.5.1. Lưu đồ Tạo/Tham gia trò chơi ...................................................................................14
2.5.2. Lưu đồ Đặt tàu ...........................................................................................................15
2.5.3. Lưu đồ Xử lý lượt bắn ...............................................................................................16
CHƯƠNG 3: TRIỂN KHAI HỆ THỐNG .......................................................................................16
3.1. Cấu trúc project...................................................................................................................16
3.2. Triển khai phía Server (ASP.NET Core) .............................................................................17
3.2.1. Khởi tạo WebSocket và Middleware .........................................................................17
3.2.2. Quản lý trạng thái trò chơi (GameManager) .............................................................17
3.2.3. Xử lý logic trò chơi (Game và Player) ......................................................................17
3.2.4. Các đối tượng tin nhắn (GameMessage) ...................................................................18
3.3. Triển khai phía Client (HTML, CSS, JavaScript)...............................................................18
3.3.1. Kết nối WebSocket và xử lý sự kiện ..........................................................................18
3.3.2. Xử lý giao diện và logic đặt tàu .................................................................................21
3.3.3. Xử lý giao diện và logic trong trận đấu .....................................................................22
3.4. Giao diện người dùng .........................................................................................................23
KẾT LUẬN .....................................................................................................................................25
1. Kết quả đạt được ..........................................................................................................................25
2. Hướng phát triển ..........................................................................................................................26
TÀI LIỆU THAM KHẢO ...............................................................................................................26
MỞ ĐẦU
Báo cáo này trình bày quá trình xây dựng một ứng dụng game bắn tàu trực tuyến đơn giản,
cho phép hai người chơi kết nối qua mạng để tham gia vào một trận đấu. Ứng dụng được phát triển
trên nền tảng ASP.NET Core MVC kết hợp với công nghệ WebSocket, mang lại trải nghiệm tương
tác thời gian thực.
1. Mục đích đề tài
Mục đích chính của đề tài xây dựng một ứng dụng game bắn tàu trực tuyến hoạt động
ổn định, cho phép hai người chơi kết nối và tương tác theo thời gian thực. Cụ thể, đề tài nhằm: -
Nghiên cứu ứng dụng công nghệ WebSocket trong việc phát triển game online, hiểu cách
thức thiết lập và duy trì kết nối song công.
- Thiết kế triển khai một kiến trúc ứng dụng sử dụng hình MVC (Model-View-
Controller) trên nền tảng ASP.NET Core để quản lý logic nghiệp vụ và giao diện người dùng.
- Phát triển giao diện trực quan và dễ sử dụng cho người chơi, bao gồm màn hình chờ, khu
vực đặt tàu và khu vực chiến đấu.
- Triển khai đầy đủ các quy tắc bản của game bắn tàu như đặt tàu, bắn, xác định trúng/trượt
và kết thúc game.
2. Nhiệm vụ của đề tài
2.1. Thiết kế giao diện người dùng
Thiết kế giao diện thân thiện, trực quan cho người chơi, bao gồm màn hình chính để
tạo/tham gia phòng, hai bảng chơi (một của người chơi, một của đối thủ) và các yếu tố điều khiển
(chọn tàu, xoay tàu, nút sẵn sàng). Giao diện cần đảm bảo hiển thị đúng trạng thái của trò chơi (đặt
tàu, đang chơi, kết thúc).
2.2. Thiết kế giao thức truyền thông
Xây dựng các cấu trúc bản tin ràng để hai bên (Client Server) thể giao tiếp hiệu
quả qua WebSocket. Các bản tin cần bao gồm thông tin về loại hành động (ví dụ: yêu cầu tạo
phòng, đặt tàu, bắn), dữ liệu liên quan (tọa độ, loại tàu) và kết quả của hành động.
2.3. Phát triển logic trò chơi
Triển khai các quy tắc nghiệp vụ của game bắn tàu, bao gồm:
- Quản lý phòng chơi: Tạo phòng, tham gia phòng, kiểm tra trạng thái phòng.
- Quản lý người chơi: Định danh người chơi, theo dõi trạng thái sẵn sàng.
- Logic đặt tàu: Kiểm tra vị trí đặt tàu hợp l(trong phạm vi, không chồng lấn), lưu trữ vị trí tàu
đã đặt.
- Logic bắn: Xác định ô bắn có tàu hay không (trúng/trượt), cập nhật trạng thái bảng chơi, đếm số
ô tàu bị phá hủy.
Logic kết thúc trò chơi: Xác định người thắng cuộc khi tất cả tàu của đối thủ bị phá hủy.
- Quản lý lượt chơi: Đảm bảo luân phiên lượt chơi giữa hai người.
2.4. Phân công nhiệm vụ thành viên
- Lê Chí Anh:
+ Phát triển phần Backend (Server-side) với ASP.NET Core: Xây dựng các lớp Model (Game,
Player, Game Manager) để quản lý logic trò chơi và trạng thái người chơi.
+ Thiết lập và quản lý kết nối WebSocket trên Server, xử lý các bản tin đến từ Client.
- Hà Hoàng Hiệp:
+ Phát triển phần Frontend (Client-side) với JavaScript: Xử kết nối WebSocket từ phía
Client, gửi và nhận bản tin.
+ Triển khai logic tương tác với giao diện người dùng (ví dụ: chọn tàu, đặt tàu, bắn).
+ Cập nhật trạng thái bảng chơi và thông báo cho người dùng dựa trên tin nhắn từ Server.
- Đoàn Nguyễn Nam:
+ Thiết kế giao diện người dùng (UI) bằng HTML CSS: Xây dựng cấu trúc HTML cho trang
game, tạo kiểu dáng cho bảng chơi, tàu và các thành phần UI khác.
+ Đảm bảo trải nghiệm người dùng mượt mà và trực quan.
+ Hỗ trợ kiểm thử và sửa lỗi tích hợp giữa Frontend và Backend.
CHƯƠNG 1: TỔNG QUAN VỀ HỆ THỐNG GAME BẮN TÀU
TRỰC TUYẾN
1.1. Giới thiệu tổng quan về game bắn tàu
Game bắn tàu (Battleship) một trò chơi chiến thuật cổ điển dành cho hai người chơi.
Mục tiêu của trò chơi là phá hủy toàn bộ đội tàu của đối thủ trước khi đối thủ phá hủy hết tàu của
mình. Mỗi người chơi có một bảng chơi (board) 10x10 ô vuông, nơi họ mật đặt các con tàu
kích thước khác nhau. Các tàu không được chồng lấn lên nhau không được đặt ra ngoài biên.
Trò chơi diễn ra theo lượt, mỗi lượt người chơi sẽ chọn một ô trên bảng của đối thủ để "bắn". Đối
thủ sẽ thông báo kết quả là "Trúng" (Hit) nếu ô đó có tàu, hoặc "Trượt" (Miss) nếu không có. Khi
một tàu bị bắn trúng tất cả các ô của nó, tàu đó sẽ "chìm". Trò chơi kết thúc khi tất cả các tàu của
một người chơi bị chìm.
1.2. Các công nghệ sử dụng
1.2.1. ASP.NET Core MVC
ASP.NET Core một framework nguồn mở đa nền tảng để xây dựng các ứng dụng
web hiện đại. hình MVC (Model-View-Controller) được sử dụng để tổ chức nguồn, giúp
phân tách rõ ràng các thành phần của ứng dụng:
- Model: Đại diện cho dữ liệu logic nghiệp vụ (ví dụ: lớp Game, Player, GameManager
các lớp tin nhắn).
- View: Đại diện cho giao diện người dùng (các file .cshtml các tài nguyên tĩnh như
CSS, JavaScript).
- Controller: Xử lý yêu cầu từ người dùng, tương tác với Model để lấy hoặc cập nhật dữ liệu,
sau đó chọn View phù hợp để hiển thị.
ASP.NET Core cung cấp một môi trường mạnh mẽ hiệu quả cho việc phát triển các ứng
dụng web. Các tệp cấu hình như appsettings.jsonappsettings.Development.json được
sử dụng để quản các thiết lập ứng dụng, ví dụ như cấu hình ghi log. Tệp launchSettings.json
định nghĩa các cấu hình khởi chạy cho dự án, bao gồm URL ứng dụng và biến môi trường.
1.2.2. WebSocket
WebSocket là một giao thức truyền thông cung cấp kênh giao tiếp song công (full-duplex)
qua một kết nối TCP duy nhất. Khác với HTTP truyền thống (duy trì kết nối ngắn và phải tạo yêu
cầu mới cho mỗi lần truyền dữ liệu), WebSocket cho phép máy chủ và máy khách gửi dữ liệu cho
nhau bất cứ lúc nào sau khi kết nối được thiết lập. Điều này làm cho WebSocket lý tưởng cho các
ứng dụng thời gian thực như game, chat, và giao dịch tài chính. Trong ứng dụng này, WebSocket
được sử dụng để:
- Duy trì kết nối liên tục giữa Client và Server.
- Gửi các bản tin sự kiện game (đặt tàu, bắn, kết quả, chuyển lượt) theo thời gian thực mà không
cần Client phải liên tục gửi yêu cầu.
1.2.3. HTML, CSS, JavaScript
- HTML: Được sử dụng để định nghĩa cấu trúc và nội dung của trang web, bao gồm c bảng
chơi, các nút điều khiển và thông báo.
- CSS: Định kiểu cho các thành phần HTML, làm cho giao diện trở nên trực quan và hấp
dẫn hơn, ví dụ như tạo lưới cho bảng chơi và định dạng các ô tàu bị bắn trúng/trượt.
- JavaScript: Là ngôn ngữ lập trình phía Client, xử lý tương tác người dùng, kết nối và giao
tiếp với WebSocket Server, cập nhật giao diện người dùng động dựa trên các sự kiện game.
1.3. Kiến trúc hệ thống
Hệ thống được thiết kế theo kiến trúc Client-Server, trong đó Client là trình duyệt web của
người chơi và Server là ứng dụng ASP.NET Core:
- Client (Trình duyệt Web):
+ Tải về trang web chứa game (HTML, CSS, JavaScript).
+ Sử dụng JavaScript để thiết lập kết nối WebSocket với Server.
+ Gửi các yêu cầu hành động của người chơi (tạo phòng, tham gia phòng, đặt tàu, bắn) dưới
dạng bản tin JSON qua WebSocket.
+ Nhận các bản tin từ Server, phân tích cập nhật giao diện người dùng tương ứng (vẽ tàu,
cập nhật trạng thái ô đã bắn, thông báo lượt chơi).
- Server (Ứng dụng ASP.NET Core):
+ Xử lý các yêu cầu HTTP ban đầu để cung cấp trang web.
+ Thiết lập một điểm cuối (endpoint) WebSocket (/ws) để chấp nhận các kết nối WebSocket
từ Client.
+ Sử dụng lớp GameManager để quản lý tất cả người chơi (Player) và các phòng game (Game)
đang hoạt động hoặc chờ người chơi.
+ Xử các bản tin nhận được từ Client, thực hiện logic game (kiểm tra luật chơi, cập nhật
trạng thái game).
+ Gửi các bản tin phản hồi cập nhật trạng thái game đến các Client liên quan thông qua
WebSocket.
+ Quản lý vòng đời kết nối WebSocket (mở, đóng, lỗi).
CHƯƠNG 2: PHÂN TÍCH THIẾT KẾ HỆ THỐNG
2.1. Phân tích yêu cầu
Hệ thống cần đáp ứng các yêu cầu chức năng và phi chức năng sau:
- Yêu cầu chức năng:
+ Tạo/Tham gia phòng chơi: Người chơi có thể tạo một phòng mới và nhận một ID phòng duy
nhất. Người chơi khác có thể sử dụng ID này để tham gia phòng.
+ Đặt tàu: Mỗi người chơi có 5 loại tàu với kích thước khác nhau (tàu sân bay - 5 ô, thiết giáp
hạm - 4 ô, tuần dương hạm - 3 ô, tàu ngầm - 3 ô, tàu khu trục - 2 ô). Người chơi phải đặt
tất cả các tàu lên bảng của mình trước khi bắt đầu trận đấu. Vị trí đặt tàu phải hợp lệ (không
ra ngoài biên, không chồng lấn).
+ Bắt đầu trận đấu: Trận đấu chỉ bắt đầu khi cả hai người chơi đã đặt tất cả các tàu và xác nhận
"Sẵn sàng".
+ Luân phiên lượt chơi: Sau khi trận đấu bắt đầu, hệ thống sẽ ngẫu nhiên chọn người chơi đi
trước. Hai người chơi luân phiên bắn.
+ Tấn công xử kết quả: Người chơi có thể chọn một ô trên bảng của đối thủ để bắn. Hệ
thống sẽ xác định kết quả (trúng/trượt) thông báo cho cả hai người chơi. Nếu trúng, ô
đó được đánh dấu là "Hit". Nếu trượt, ô đó được đánh dấu là "Miss".
+ Chìm tàu: Khi tất cả các ô của một tàu bị bắn trúng, tàu đó được coi là "chìm".
+ Kết thúc trò chơi: Tchơi kết thúc khi tất cả các tàu của một người chơi bị chìm. Người
chơi có tàu bị chìm hết sẽ thua, người còn lại thắng.
+ Thông báo trạng thái: Hệ thống cần thông báo ràng trạng thái hiện tại của trò chơi cho
người chơi (ví dụ: "Đang chờ đối thủ", "Chọn tàu để đặt", "Đến lượt bạn", "Bạn
thắng/thua").
- Yêu cầu phi chức năng:
+ Thời gian thực: Giao tiếp giữa Client và Server cần diễn ra gần như tức thời để đảm bảo trải
nghiệm chơi game mượt mà.
+ Độ tin cậy: Kết nối WebSocket cần ổn định, có cơ chế xử lý lỗi khi kết nối bị ngắt.
+ Dễ sử dụng: Giao diện người dùng cần đơn giản, trực quan, dễ hiểu.
2.2. Thiết kế cơ sở dữ liệu
Trong dự án này, không sở dữ liệu vật được sử dụng để lưu trữ trạng thái game.
Thay vào đó, tất cả dữ liệu về game, người chơi bảng chơi được quản trong bộ nhớ của Server
thông qua các lớp Model GameManager. Điều này phù hợp cho một game đơn giản, thời gian
thực và không yêu cầu lưu trữ lịch sử lâu dài.
- Lớp Player: Đại diện cho một người chơi cụ thể.
+ Id: Chuỗi định danh duy nhất cho người chơi.
+ Socket: Đối tượng duy trì kết nối với Client tương ứng.
+ Board: Mảng 2 chiều (int[][]) biểu diễn bảng chơi của người chơi, kích thước 10x10.
Các giá trị có thể là: 0 (ô trống), 1 (có tàu), 2 (ô tàu bị bắn trúng), 3 (ô trống bị bắn trượt).
+ ShipCellsRemaining: Số ô tàu còn lại chưa bị bắn trúng.
+ ShipsToPlace: Một Dictionary<string, int> lưu trữ các loại tàu và độ dài của chúng
cần được đặt.
+ IsReady: Trạng thái sẵn sàng của người chơi.
- Lớp Game: Đại diện cho một trận đấu giữa hai người chơi.
+ Player1: Đối tượng Player của người chơi thứ nhất (người tạo phòng).
+ Player2: Đối tượng Player của người chơi thứ hai (người tham gia phòng).
+ CurrentTurnPlayer: Đối tượng Player của người chơi đang có lượt đi.
WebSocket
+ State: Trạng thái hiện tại của game (ví dụ: PlacingShips, InProgress, Finished). - Lớp
GameManager: Lớp tĩnh quản lý tất cả các Player đang kết nối và các Game đang diễn ra hoặc chờ
đợi.
+ _players: ConcurrentDictionary lưu trữ tất cả các
Player đang kết nối.
+ _pendingGames: lưu trữ các game đang chờ người chơi thứ hai tham gia.
+ _activeGames: lưu trữ các game đang diễn ra (cả hai người chơi đã tham
gia và đang trong giai đoạn đặt tàu hoặc chiến đấu).
2.3. Thiết kế luồng hoạt động chính
2.3.1. Luồng tạo và tham gia phòng chơi
1. Client A (Host) gửi bản tin host_game đến Server.
2. Server nhận bản tin, tạo một Game mới với Client APlayer1, tạo một GameId duy nhất
và lưu vào _pendingGames.
3. Server gửi bản tin game_hosted kèm theo GameId về cho Client A.
4. Client B (Joiner) nhập GameId và gửi bản tin join_game kèm GameId đến Server.
5. Server kiểm tra GameId:
+ Nếu tìm thấy và Player2 còn trống, Server gán Client B Player2 cho Game đó,
chuyển game từ _pendingGames sang _activeGames.
+ Server gửi bản tin start_placing cho cả Client A và Client B.
+ Nếu không tìm thấy hoặc game đã đầy, Server gửi bản tin join_failed cho Client
B.
2.3.2. Luồng đặt tàu
1. Client nhận bản tin start_placing, hiển thị giao diện đặt tàu.
2. Người chơi chọn một loại tàu một ô trên bảng của mình, sau đó quyết định hướng đặt
tàu.
3. Client gửi bản tin place_ship (kèm loại tàu, tọa độ X, Y, IsHorizontal) đến Server.
4. Server (Game.PlaceShip) nhận bản tin, kiểm tra tính hợp lệ của vị trí đặt tàu (trong phạm
vi, không chồng lấn, đủ độ dài cho tàu).
5. Server cập nhật bảng chơi của người chơi.
6. Server gửi bản tin place_ship_result (kèm Success/Message, thông tin tàu đã đặt) về
cho Client.
7. Client nhận kết quả, nếu thành công thì vẽ tàu lên bảng và gạch tên tàu khỏi danh sách chờ
đặt.
8. Khi người chơi đặt hết tất cả tàu, nút "Ready" xuất hiện. Người chơi nhấn "Ready".
9. Client gửi bản tin player_ready đến Server.
10. Server (Game.SetPlayerReady) nhận bản tin, đánh dấu người chơi đó là sẵn sàng.
11. Nếu cả hai người chơi đã sẵn sàng, Server (Game.StartBattle) chọn ngẫu nhiên người đi
trước, cập nhật trạng thái game thành InProgress, và gửi bản tin game_start cho cả hai
Client (kèm thông tin lượt đi). Nếu chỉ một người sẵn sàng, Server gửi bản tin
waiting_for_opponent_ready cho người chơi đó.
ConcurrentDictionary
ConcurrentBag
2.3.3. Luồng tấn công và xử lý kết quả
1. Client nhận bản tin game_start hoặc shot_result/opponent_shot với IsYourTurn = true,
hiển thị "Đến lượt bạn".
2. Người chơi chọn một ô trên bảng của đối thủ để bắn.
3. Client gửi bản tin fire_shot (kèm tọa độ X, Y) đến Server.
4. Server (Game.ProcessShot) nhận bản tin, kiểm tra tính hợp lệ (đúng lượt, tọa độ hợp lệ).
5. Server kiểm tra ô bắn trên bảng của đối thủ:
+ Nếu ô đó tàu (cellState == 1): đánh dấu ô đó trúng (2), giảm
ShipCellsRemaining của đối thủ. Kết quả là "Hit".
+ Nếu ô đó trống (cellState == 0): đánh dấu ô đó là trượt (3). Kết quả là "Miss".
6. Server kiểm tra ShipCellsRemaining của đối thủ. Nếu <= 0, trò chơi kết thúc.
7. Server gửi bản tin shot_result cho người bắn (kèm X, Y, Result, IsYourTurn=false).
8. Server gửi bản tin opponent_shot cho người bị bắn (kèm X, Y, Result,
IsYourTurn=true).
9. Nếu game kết thúc, Server gửi bản tin game_over cho cả hai người chơi (kèm YouWon).
10. Client nhận bản tin, cập nhật giao diện (vẽ X/O lên ô đã bắn, cập nhật trạng thái lượt
chơi/kết thúc game).
2.4. Thiết kế giao thức truyền thông WebSocket
Tất cả các bản tin được gửi qua WebSocket đều các chuỗi JSON. Dưới đây cấu trúc
chung và các loại bản tin cụ thể được sử dụng trong hệ thống.
2.4.1. Cấu trúc bản tin cơ bản (GameMessage)
Mọi bản tin đều kế thừa từ lớp GameMessage và phải có trường Type để xác định loại bản
tin.
serialize/deserialize JSON.
2.4.2. Các bản tin từ Client lên Server và từ Server xuống Client
Để đảm bảo giao tiếp hiệu quả ràng giữa Client Server, chúng tôi đã định nghĩa
một tập hợp các bản tin JSON. Mỗi bản tin đều trường Type để xác định mục đích của nó,
các trường dữ liệu bổ sung tùy thuộc vào loại bản tin.
GameMessage.cs
public class GameMessage
{
public string Type { get; set; }
}
Việc sử dụng
JsonSerializable
trong
giúp tối ưu hóa quá trình
Dưới đây định nghĩa của tất cả các lớp tin nhắn được sử dụng trong hệ thống, bao gồm cả các
bản tin Client gửi lên Server và các bản tin Server gửi xuống Client:
GameMessage.cs
using System.Text.Json.Serialization;
namespace BattleshipGame.Models;
// Lp cơ s cho mi tin nhn
public class GameMessage
{
public string Type { get; set; }
}
// --- CÁC MESSAGE MI CHO LOBBY ---
// Client gi lên khi mun to phòng
public class HostGameMessage : GameMessage { }
// Client gi lên khi mun tham gia phòng
public class JoinGameMessage : GameMessage
{
public string GameId { get; set; }
}
// Server gi v cho Host sau khi to phòng thành công
public class GameHostedMessage : GameMessage
{
public string GameId { get; set; }
}
// Server gi v nếu tham gia phòng tht bi
public class JoinFailedMessage : GameMessage
{
public string Reason { get; set; }
}
// --- CÁC MESSAGE CŨ CHO GAMEPLAY ---
public class PlaceShipMessage : GameMessage
{
public string ShipType { get; set; }
public int X { get; set; }
public int Y { get; set; }
public bool IsHorizontal { get; set; }
}
public class PlaceShipResultMessage : GameMessage
{
public bool Success { get; set; }
public string Message { get; set; }
public string ShipType { get; set; }
public int X { get; set; } public
int Y { get; set; } public int Length
{ get; set; } public bool
IsHorizontal { get; set; }
}
public class StartPlacingMessage : GameMessage { }
public class PlayerReadyMessage : GameMessage { }
public class FireShotMessage : GameMessage
{
public int X { get; set; }
public int Y { get; set; }
}
public class GameStartMessage : GameMessage
{
public int[][] YourBoard { get; set; }
public bool IsYourTurn { get; set; }
public string OpponentName { get; set; }
}
public class ShotResultMessage : GameMessage
{
public int X { get; set; }
public int Y { get; set; } public
string Result { get; set; } public
bool IsYourTurn { get; set; }
}
public class OpponentShotMessage : GameMessage
{
public int X { get; set; }
public int Y { get; set; } public
string Result { get; set; } public
bool IsYourTurn { get; set; }
}
public class GameOverMessage : GameMessage
{
public bool YouWon { get; set; }
}
public class ErrorMessage : GameMessage
{
public string Message { get; set; }
}
Giải thích chi tiết về các loại bản tin:
+ HostGameMessage: Client yêu cầu tạo phòng chơi mới.
+ JoinGameMessage: Client yêu cầu tham gia phòng chơi với GameId được chỉ định.
+ GameHostedMessage: Server thông báo cho Host về việc tạo phòng thành công và cung cấp
GameId.
+ JoinFailedMessage: Server thông báo cho Client về lỗi khi tham gia phòng, kèm theo
Reason.
+ StartPlacingMessage: Server thông báo bắt đầu giai đoạn đặt tàu cho cả hai Client.
+ PlaceShipMessage: Client gửi yêu cầu đặt tàu với ShipType, tọa độ (X, Y) và
IsHorizontal.
+ PlaceShipResultMessage: Server thông báo kết quả đặt tàu (Success, Message) và thông
tin chi tiết về tàu đã đặt (ShipType, X, Y, Length, IsHorizontal).
+ PlayerReadyMessage: Client thông báo đã đặt xong tàu và sẵn sàng bắt đầu trận đấu.
+ GameMessage (waiting_for_opponent_ready): Server thông báo Client đã sẵn sàng đang
chờ đối thủ.
+ FireShotMessage: Client bắn vào ô tọa độ (X, Y) trên bảng đối thủ.
+ GameStartMessage: Server thông báo trận đấu bắt đầu, kèm theo YourBoard ban đầu,
IsYourTurnOpponentName.
+ ShotResultMessage: Server thông báo kết qu bắn của người chơi (X, Y, Result)
trạng thái lượt chơi (IsYourTurn).
+ OpponentShotMessage: Server thông báo cho người chơi bị bắn về bắn của đối thủ (X, Y,
Result) và trạng thái lượt chơi (IsYourTurn).
+ GameOverMessage: Server thông báo trò chơi kết thúc và kết quả (YouWon).
+ ErrorMessage: Server gửi thông báo lỗi chung, kèm theo Message chi tiết.
2.5. Lưu đồ thuật toán các chức năng chính
2.5.1. Lưu đồ Tạo/Tham gia trò chơi
2.5.2. Lưu đồ Đặt tàu
2.5.3. Lưu đồ Xử lý lượt bắn
CHƯƠNG 3: TRIỂN KHAI HỆ THỐNG
3.1. Cấu trúc project
Cấu trúc thư mục của project theo chuẩn ASP.NET Core MVC, với các thư mục chính như
sau:
- Controllers/: Chứa các lớp điều khiển xử lý các yêu cầu HTTP thông thường (ví dụ:
HomeController.cs cho trang chủ).
- Models/: Chứa các lớp mô hình dữ liệu và logic nghiệp vụ chính của trò chơi.
+ ErrorViewModel.cs
+ Game.cs
+ GameManager.cs
+ GameMessage.cs (và các lớp tin nhắn kế thừa)
+ Player.cs
- Properties/: Chứa cấu hình dự án, bao gồm launchSettings.json cho cấu hình khởi chạy.
- Views/: Chứa các file Razor View (.cshtml) định nghĩa giao diện người dùng.
+ Home/: Chứa Index.cshtml (trang chính của game).
+ Shared/: Chứa _Layout.cshtml (layout chung) và Error.cshtml.
+ _ViewImports.cshtml, _ViewStart.cshtml.
- wwwroot/: Chứa các tài nguyên tĩnh phục vụ Client.
+ css/: Chứa các file CSS (site.css, _Layout.cshtml.css).
+ js/: Chứa các file JavaScript (site.js chứa logic game).
- appsettings.json, appsettings.Development.json: Các file cấu hình ứng dụng.
- Program.cs: File cấu hình và khởi tạo ứng dụng ASP.NET Core, bao gồm thiết lập WebSocket.
3.2. Triển khai phía Server (ASP.NET Core)
3.2.1. Khởi tạo WebSocket và Middleware
Trong Program.cs, WebSocket được kích hoạt bằng phương thức UseWebSockets, cho
phép ứng dụng ASP.NET Core xử các kết nối thời gian thực. Một middleware tùy chỉnh được
thiết lập để lắng nghe các yêu cầu đến đường dẫn /ws, là nơi duy nhất nhận kết nối WebSocket.
Khi client kết nối thành công, phương thức HandleGameSession được gọi để tạo một đối
tượng Player, thêm vào GameManager, và duy trì vòng lặp nhận tin nhắn từ phía client. Khi client
đóng kết nối hoặc xảy ra lỗi, Player sẽ bị xóa khỏi hệ thống. - Cấu hình quan trọng:
+ KeepAliveInterval = 120s: đảm bảo giữ kết nối ổn định.
+ Phân tích và xử lý thông điệp trong một buffer kích thước 4KB.
+ AppJsonSerializerContext hỗ trợ tối ưu JSON (cho AOT và trimming).
3.2.2. Quản lý trạng thái trò chơi (GameManager)
GameManager một lớp tĩnh có vai trò trung tâm điều phối, quản toàn bộ luồng game
giữa các người chơi. Sử dụng các cấu trúc thread-safe như ConcurrentDictionary
ConcurrentBag để đảm bảo an toàn trong môi trường đa luồng.
- Chức năng chính:
+ AddPlayer / RemovePlayer: quản lý danh sách người chơi đang hoạt động.
+ HandleMessage: nhận và phân tích tin nhắn từ client, điều hướng xử lý dựa trên Type của tin
nhắn, ví dụ:
+ "host_game" → gọi HostGame() để tạo game mới với ID ngẫu nhiên.
+ "join_game" → gọi JoinGame() để ghép người chơi vào phòng chờ.
+ "place_ship", "fire_shot", "player_ready": xử lý tương tác trong game.
+ Tin nhắn phản hồi được gửi lại cho từng người chơi thông qua SendMessageAsync().
3.2.3. Xử lý logic trò chơi (Game và Player)
- Player.cs:
+ Đại diện cho mỗi người chơi đang kết nối:
+ Thuộc tính: Id, Socket, Board, ShipsToPlace, IsReady, ShipCellsRemaining.
+ Phương thức: SendMessageAsync() để gửi tin nhắn cho client.
+ Hỗ trợ kiểm tra trạng thái đặt tàu (HasPlacedAllShips()).
- Game.cs:
+ Quản lý trạng thái một ván đấu giữa hai người chơi:
+ Giai đoạn game: PlacingShips, InProgress, Finished.
+ Phân quyền lượt chơi qua CurrentTurnPlayer, xử lý hành động theo giai đoạn:
+ StartShipPlacement() → gửi lệnh bắt đầu đặt tàu cho cả hai.
+ PlaceShip() → kiểm tra hợp lệ, cập nhật bảng chơi.
+ SetPlayerReady() → chờ cả hai sẵn sàng trước khi bắt đầu trận đấu.
+ StartBattle() → chọn ngẫu nhiên người bắn trước.
+ ProcessShot() → kiểm tra trúng/tượt, kết thúc game khi tàu bị bắn hết.
3.2.4. Các đối tượng tin nhắn (GameMessage)
- Toàn bộ giao tiếp giữa client server đều dựa trên các tin nhắn JSON. Mỗi tin nhắn
một lớp con của GameMessage, ví dụ:
+ HostGameMessage, JoinGameMessage, PlaceShipMessage, FireShotMessage,
GameStartMessage, GameOverMessage, v.v.
+ Những lớp này được sử dụng để:
Giải mã dữ liệu khi nhận từ WebSocket.
Tự động tuần tự hóa khi gửi phản hồi cho client.
- Việc định nghĩa rõ từng loại tin nhắn giúp giảm lỗi, dễ mở rộng duy trì giao tiếp rõ ràng
trong toàn bộ game.
3.3. Triển khai phía Client (HTML, CSS, JavaScript)
Phía Client được xây dựng chủ yếu bằng JavaScript (site.js), HTML (trong
Views/Home/Index.cshtml) và CSS (site.css, _Layout.cshtml.css).
3.3.1. Kết nối WebSocket và xử lý sự kiện
File wwwroot/js/site.js chứa toàn bộ logic tương tác phía Client, bao gồm thiết lập kết
nối WebSocket và xử lý các sự kiện từ Server.
wwwroot/js/site.js
document.addEventListener("DOMContentLoaded", function () {
let socket;
let connectionPurpose = ''; // 'host' hoc 'join'
let gameIdToJoin = '';
function connectToServer() {
// To bng ngay khi kết ni để hin th lưi
createBoard(playerBoardEl); createBoard(opponentBoardEl);
const scheme = document.location.protocol === "https:" ? "wss"
:
"ws";
const port = document.location.port ? `:${document.location.port}`
: "";
const connectionUrl =
`${scheme}://${document.location.hostname}${port}/ws`;
socket = new WebSocket(connectionUrl); // To kết ni WebSocket
socket.onopen = () => { // Khi kết ni
m if (connectionPurpose === 'host')
{
socket.send(JSON.stringify({ Type: 'host_game' })); // Gi
yêu cu host
} else if (connectionPurpose === 'join') {
socket.send(JSON.stringify({ Type: 'join_game', GameId: gameIdToJoin }));
// Gi yêu cu join
}
};
socket.onmessage = (event) =>
handleServerMessage(JSON.parse(event.data)); // X lý tin nhn t Server
socket.onclose = () => { /* X lý khi kết ni đóng */ };
socket.onerror = (err) => { /* X lý li */ };
}
function handleServerMessage(message) {
switch (message.Type) {
case "game_hosted":
// Hin th Game ID cho người chơi host
document.getElementById("host-info").innerHTML = `<div
class="game-id-display">${message.GameId}</div>`;
break;
case "start_placing":
// Chuyn sang giao din đặt tàu
document.getElementById("lobby-screen").classList.add('d-none');
document.getElementById("game-container").classList.remove('d-none');
setupPlacementUI();
break;
case "place_ship_result":
if (message.Success) {
drawPlacedShip(message); // V tàu lên bng ca người
chơi
removeShipFromList(message.ShipType); // Xóa tàu
khi danh sách ch đặt } else {
alert(`Placement failed: ${message.Message}`);
}
break; case
"game_start": //
Bt đầu trn đấu
placementUiEl.classList.add('d-none');
myTurn = message.IsYourTurn;
updateStatus();
break;
case "shot_result":
updateOpponentBoard(message.X, message.Y, message.Result);
// Cp nht bng đối th
myTurn = message.IsYourTurn;
updateStatus();
break;
case "opponent_shot":
updatePlayerBoard(message.X, message.Y, message.Result); //
Cp nht bng ca người chơi
myTurn = message.IsYourTurn;
updateStatus();
break;
// ... (các case khác cho game_over, join_failed,
waiting_for_opponent_ready)
}
}
// ... (các hàm x lý UI và logic khác)
initialize(); // Khi to các s kin ban đầu
});
Đoạn mã JavaScript này cho thấy cách Client khởi tạo kết nối WebSocket đến endpoint
/ws, gửi tin nhắn host_game hoặc join_game khi kết nối được mở. Hàm handleServerMessage
trung tâm xlý, phân phối các tin nhắn nhận được từ Server tới các hàm chuyên biệt để cập
nhật giao diện người dùng và trạng thái game.

Preview text:

MỤC LỤC
MỤC LỤC .........................................................................................................................................2
MỞ ĐẦU ...........................................................................................................................................4
1. Mục đích đề tài ..............................................................................................................................4
2. Nhiệm vụ của đề tài .......................................................................................................................4
2.1. Thiết kế giao diện người dùng ........................................................................................4
2.2. Thiết kế giao thức truyền thông ......................................................................................4
2.3. Phát triển logic trò chơi ..................................................................................................4
2.4. Phân công nhiệm vụ thành viên ......................................................................................4
CHƯƠNG 1: TỔNG QUAN VỀ HỆ THỐNG GAME BẮN TÀU TRỰC TUYẾN ........................5
1.1. Giới thiệu tổng quan về game bắn tàu ..................................................................................5
1.2. Các công nghệ sử dụng .........................................................................................................5
1.2.1. ASP.NET Core MVC ...................................................................................................5
1.2.2. WebSocket ...................................................................................................................6
1.2.3. HTML, CSS, JavaScript ..............................................................................................6
1.3. Kiến trúc hệ thống ................................................................................................................6
CHƯƠNG 2: PHÂN TÍCH THIẾT KẾ HỆ THỐNG ........................................................................6
2.1. Phân tích yêu cầu ..................................................................................................................6
2.2. Thiết kế cơ sở dữ liệu ...........................................................................................................7
2.3. Thiết kế luồng hoạt động chính ............................................................................................8
2.3.1. Luồng tạo và tham gia phòng chơi ..............................................................................8
2.3.2. Luồng đặt tàu ...............................................................................................................8
2.3.3. Luồng tấn công và xử lý kết quả .................................................................................9
2.4. Thiết kế giao thức truyền thông WebSocket .........................................................................9
2.4.1. Cấu trúc bản tin cơ bản (GameMessage) .....................................................................9
2.4.2. Các bản tin từ Client lên Server và từ Server xuống Client ........................................9
2.5. Lưu đồ thuật toán các chức năng chính ..............................................................................14
2.5.1. Lưu đồ Tạo/Tham gia trò chơi ...................................................................................14
2.5.2. Lưu đồ Đặt tàu ...........................................................................................................15
2.5.3. Lưu đồ Xử lý lượt bắn ...............................................................................................16
CHƯƠNG 3: TRIỂN KHAI HỆ THỐNG .......................................................................................16
3.1. Cấu trúc project...................................................................................................................16
3.2. Triển khai phía Server (ASP.NET Core) .............................................................................17
3.2.1. Khởi tạo WebSocket và Middleware .........................................................................17
3.2.2. Quản lý trạng thái trò chơi (GameManager) .............................................................17
3.2.3. Xử lý logic trò chơi (Game và Player) ......................................................................17
3.2.4. Các đối tượng tin nhắn (GameMessage) ...................................................................18
3.3. Triển khai phía Client (HTML, CSS, JavaScript)...............................................................18
3.3.1. Kết nối WebSocket và xử lý sự kiện ..........................................................................18
3.3.2. Xử lý giao diện và logic đặt tàu .................................................................................21
3.3.3. Xử lý giao diện và logic trong trận đấu .....................................................................22
3.4. Giao diện người dùng .........................................................................................................23
KẾT LUẬN .....................................................................................................................................25
1. Kết quả đạt được ..........................................................................................................................25
2. Hướng phát triển ..........................................................................................................................26
TÀI LIỆU THAM KHẢO ...............................................................................................................26 MỞ ĐẦU
Báo cáo này trình bày quá trình xây dựng một ứng dụng game bắn tàu trực tuyến đơn giản,
cho phép hai người chơi kết nối qua mạng để tham gia vào một trận đấu. Ứng dụng được phát triển
trên nền tảng ASP.NET Core MVC kết hợp với công nghệ WebSocket, mang lại trải nghiệm tương tác thời gian thực.
1. Mục đích đề tài
Mục đích chính của đề tài là xây dựng một ứng dụng game bắn tàu trực tuyến hoạt động
ổn định, cho phép hai người chơi kết nối và tương tác theo thời gian thực. Cụ thể, đề tài nhằm: -
Nghiên cứu và ứng dụng công nghệ WebSocket trong việc phát triển game online, hiểu rõ cách
thức thiết lập và duy trì kết nối song công. -
Thiết kế và triển khai một kiến trúc ứng dụng sử dụng mô hình MVC (Model-View-
Controller) trên nền tảng ASP.NET Core để quản lý logic nghiệp vụ và giao diện người dùng. -
Phát triển giao diện trực quan và dễ sử dụng cho người chơi, bao gồm màn hình chờ, khu
vực đặt tàu và khu vực chiến đấu. -
Triển khai đầy đủ các quy tắc cơ bản của game bắn tàu như đặt tàu, bắn, xác định trúng/trượt và kết thúc game.
2. Nhiệm vụ của đề tài
2.1. Thiết kế giao diện người dùng

Thiết kế giao diện thân thiện, trực quan cho người chơi, bao gồm màn hình chính để
tạo/tham gia phòng, hai bảng chơi (một của người chơi, một của đối thủ) và các yếu tố điều khiển
(chọn tàu, xoay tàu, nút sẵn sàng). Giao diện cần đảm bảo hiển thị đúng trạng thái của trò chơi (đặt
tàu, đang chơi, kết thúc).
2.2. Thiết kế giao thức truyền thông
Xây dựng các cấu trúc bản tin rõ ràng để hai bên (Client và Server) có thể giao tiếp hiệu
quả qua WebSocket. Các bản tin cần bao gồm thông tin về loại hành động (ví dụ: yêu cầu tạo
phòng, đặt tàu, bắn), dữ liệu liên quan (tọa độ, loại tàu) và kết quả của hành động.
2.3. Phát triển logic trò chơi
Triển khai các quy tắc nghiệp vụ của game bắn tàu, bao gồm:
- Quản lý phòng chơi: Tạo phòng, tham gia phòng, kiểm tra trạng thái phòng.
- Quản lý người chơi: Định danh người chơi, theo dõi trạng thái sẵn sàng.
- Logic đặt tàu: Kiểm tra vị trí đặt tàu hợp lệ (trong phạm vi, không chồng lấn), lưu trữ vị trí tàu đã đặt.
- Logic bắn: Xác định ô bắn có tàu hay không (trúng/trượt), cập nhật trạng thái bảng chơi, đếm số ô tàu bị phá hủy.
Logic kết thúc trò chơi: Xác định người thắng cuộc khi tất cả tàu của đối thủ bị phá hủy.
- Quản lý lượt chơi: Đảm bảo luân phiên lượt chơi giữa hai người.
2.4. Phân công nhiệm vụ thành viên - Lê Chí Anh:
+ Phát triển phần Backend (Server-side) với ASP.NET Core: Xây dựng các lớp Model (Game,
Player, Game Manager) để quản lý logic trò chơi và trạng thái người chơi.
+ Thiết lập và quản lý kết nối WebSocket trên Server, xử lý các bản tin đến từ Client. - Hà Hoàng Hiệp:
+ Phát triển phần Frontend (Client-side) với JavaScript: Xử lý kết nối WebSocket từ phía
Client, gửi và nhận bản tin.
+ Triển khai logic tương tác với giao diện người dùng (ví dụ: chọn tàu, đặt tàu, bắn).
+ Cập nhật trạng thái bảng chơi và thông báo cho người dùng dựa trên tin nhắn từ Server. - Đoàn Nguyễn Nam:
+ Thiết kế giao diện người dùng (UI) bằng HTML và CSS: Xây dựng cấu trúc HTML cho trang
game, tạo kiểu dáng cho bảng chơi, tàu và các thành phần UI khác.
+ Đảm bảo trải nghiệm người dùng mượt mà và trực quan.
+ Hỗ trợ kiểm thử và sửa lỗi tích hợp giữa Frontend và Backend.
CHƯƠNG 1: TỔNG QUAN VỀ HỆ THỐNG GAME BẮN TÀU TRỰC TUYẾN
1.1. Giới thiệu tổng quan về game bắn tàu
Game bắn tàu (Battleship) là một trò chơi chiến thuật cổ điển dành cho hai người chơi.
Mục tiêu của trò chơi là phá hủy toàn bộ đội tàu của đối thủ trước khi đối thủ phá hủy hết tàu của
mình. Mỗi người chơi có một bảng chơi (board) 10x10 ô vuông, nơi họ bí mật đặt các con tàu có
kích thước khác nhau. Các tàu không được chồng lấn lên nhau và không được đặt ra ngoài biên.
Trò chơi diễn ra theo lượt, mỗi lượt người chơi sẽ chọn một ô trên bảng của đối thủ để "bắn". Đối
thủ sẽ thông báo kết quả là "Trúng" (Hit) nếu ô đó có tàu, hoặc "Trượt" (Miss) nếu không có. Khi
một tàu bị bắn trúng tất cả các ô của nó, tàu đó sẽ "chìm". Trò chơi kết thúc khi tất cả các tàu của
một người chơi bị chìm.
1.2. Các công nghệ sử dụng 1.2.1. ASP.NET Core MVC
ASP.NET Core là một framework mã nguồn mở đa nền tảng để xây dựng các ứng dụng
web hiện đại. Mô hình MVC (Model-View-Controller) được sử dụng để tổ chức mã nguồn, giúp
phân tách rõ ràng các thành phần của ứng dụng: -
Model: Đại diện cho dữ liệu và logic nghiệp vụ (ví dụ: lớp Game, Player, GameManager và các lớp tin nhắn). -
View: Đại diện cho giao diện người dùng (các file .cshtml và các tài nguyên tĩnh như CSS, JavaScript). -
Controller: Xử lý yêu cầu từ người dùng, tương tác với Model để lấy hoặc cập nhật dữ liệu,
sau đó chọn View phù hợp để hiển thị.
ASP.NET Core cung cấp một môi trường mạnh mẽ và hiệu quả cho việc phát triển các ứng
dụng web. Các tệp cấu hình như appsettings.jsonappsettings.Development.json được
sử dụng để quản lý các thiết lập ứng dụng, ví dụ như cấu hình ghi log. Tệp launchSettings.json
định nghĩa các cấu hình khởi chạy cho dự án, bao gồm URL ứng dụng và biến môi trường. 1.2.2. WebSocket
WebSocket là một giao thức truyền thông cung cấp kênh giao tiếp song công (full-duplex)
qua một kết nối TCP duy nhất. Khác với HTTP truyền thống (duy trì kết nối ngắn và phải tạo yêu
cầu mới cho mỗi lần truyền dữ liệu), WebSocket cho phép máy chủ và máy khách gửi dữ liệu cho
nhau bất cứ lúc nào sau khi kết nối được thiết lập. Điều này làm cho WebSocket lý tưởng cho các
ứng dụng thời gian thực như game, chat, và giao dịch tài chính. Trong ứng dụng này, WebSocket được sử dụng để:
- Duy trì kết nối liên tục giữa Client và Server.
- Gửi các bản tin sự kiện game (đặt tàu, bắn, kết quả, chuyển lượt) theo thời gian thực mà không
cần Client phải liên tục gửi yêu cầu.
1.2.3. HTML, CSS, JavaScript -
HTML: Được sử dụng để định nghĩa cấu trúc và nội dung của trang web, bao gồm các bảng
chơi, các nút điều khiển và thông báo. -
CSS: Định kiểu cho các thành phần HTML, làm cho giao diện trở nên trực quan và hấp
dẫn hơn, ví dụ như tạo lưới cho bảng chơi và định dạng các ô tàu bị bắn trúng/trượt. -
JavaScript: Là ngôn ngữ lập trình phía Client, xử lý tương tác người dùng, kết nối và giao
tiếp với WebSocket Server, cập nhật giao diện người dùng động dựa trên các sự kiện game.
1.3. Kiến trúc hệ thống
Hệ thống được thiết kế theo kiến trúc Client-Server, trong đó Client là trình duyệt web của
người chơi và Server là ứng dụng ASP.NET Core:
- Client (Trình duyệt Web):
+ Tải về trang web chứa game (HTML, CSS, JavaScript).
+ Sử dụng JavaScript để thiết lập kết nối WebSocket với Server.
+ Gửi các yêu cầu hành động của người chơi (tạo phòng, tham gia phòng, đặt tàu, bắn) dưới
dạng bản tin JSON qua WebSocket.
+ Nhận các bản tin từ Server, phân tích và cập nhật giao diện người dùng tương ứng (vẽ tàu,
cập nhật trạng thái ô đã bắn, thông báo lượt chơi).
- Server (Ứng dụng ASP.NET Core):
+ Xử lý các yêu cầu HTTP ban đầu để cung cấp trang web.
+ Thiết lập một điểm cuối (endpoint) WebSocket (/ws) để chấp nhận các kết nối WebSocket từ Client.
+ Sử dụng lớp GameManager để quản lý tất cả người chơi (Player) và các phòng game (Game)
đang hoạt động hoặc chờ người chơi.
+ Xử lý các bản tin nhận được từ Client, thực hiện logic game (kiểm tra luật chơi, cập nhật trạng thái game).
+ Gửi các bản tin phản hồi và cập nhật trạng thái game đến các Client liên quan thông qua WebSocket.
+ Quản lý vòng đời kết nối WebSocket (mở, đóng, lỗi).
CHƯƠNG 2: PHÂN TÍCH THIẾT KẾ HỆ THỐNG
2.1. Phân tích yêu cầu

Hệ thống cần đáp ứng các yêu cầu chức năng và phi chức năng sau: - Yêu cầu chức năng:
+ Tạo/Tham gia phòng chơi: Người chơi có thể tạo một phòng mới và nhận một ID phòng duy
nhất. Người chơi khác có thể sử dụng ID này để tham gia phòng.
+ Đặt tàu: Mỗi người chơi có 5 loại tàu với kích thước khác nhau (tàu sân bay - 5 ô, thiết giáp
hạm - 4 ô, tuần dương hạm - 3 ô, tàu ngầm - 3 ô, tàu khu trục - 2 ô). Người chơi phải đặt
tất cả các tàu lên bảng của mình trước khi bắt đầu trận đấu. Vị trí đặt tàu phải hợp lệ (không
ra ngoài biên, không chồng lấn).
+ Bắt đầu trận đấu: Trận đấu chỉ bắt đầu khi cả hai người chơi đã đặt tất cả các tàu và xác nhận "Sẵn sàng".
+ Luân phiên lượt chơi: Sau khi trận đấu bắt đầu, hệ thống sẽ ngẫu nhiên chọn người chơi đi
trước. Hai người chơi luân phiên bắn.
+ Tấn công và xử lý kết quả: Người chơi có thể chọn một ô trên bảng của đối thủ để bắn. Hệ
thống sẽ xác định kết quả (trúng/trượt) và thông báo cho cả hai người chơi. Nếu trúng, ô
đó được đánh dấu là "Hit". Nếu trượt, ô đó được đánh dấu là "Miss".
+ Chìm tàu: Khi tất cả các ô của một tàu bị bắn trúng, tàu đó được coi là "chìm".
+ Kết thúc trò chơi: Trò chơi kết thúc khi tất cả các tàu của một người chơi bị chìm. Người
chơi có tàu bị chìm hết sẽ thua, người còn lại thắng.
+ Thông báo trạng thái: Hệ thống cần thông báo rõ ràng trạng thái hiện tại của trò chơi cho
người chơi (ví dụ: "Đang chờ đối thủ", "Chọn tàu để đặt", "Đến lượt bạn", "Bạn thắng/thua").
- Yêu cầu phi chức năng:
+ Thời gian thực: Giao tiếp giữa Client và Server cần diễn ra gần như tức thời để đảm bảo trải
nghiệm chơi game mượt mà.
+ Độ tin cậy: Kết nối WebSocket cần ổn định, có cơ chế xử lý lỗi khi kết nối bị ngắt.
+ Dễ sử dụng: Giao diện người dùng cần đơn giản, trực quan, dễ hiểu.
2.2. Thiết kế cơ sở dữ liệu
Trong dự án này, không có cơ sở dữ liệu vật lý được sử dụng để lưu trữ trạng thái game.
Thay vào đó, tất cả dữ liệu về game, người chơi và bảng chơi được quản lý trong bộ nhớ của Server
thông qua các lớp Model và GameManager. Điều này phù hợp cho một game đơn giản, thời gian
thực và không yêu cầu lưu trữ lịch sử lâu dài.
- Lớp Player: Đại diện cho một người chơi cụ thể.
+ Id: Chuỗi định danh
duy nhất cho người chơi.
+ Socket: Đối tượng WebSocket duy trì kết nối với Client tương ứng.
+ Board: Mảng 2 chiều (int[][]) biểu diễn bảng chơi của người chơi, kích thước 10x10.
Các giá trị có thể là: 0 (ô trống), 1 (có tàu), 2 (ô tàu bị bắn trúng), 3 (ô trống bị bắn trượt).
+ ShipCellsRemaining: Số ô tàu còn lại chưa bị bắn trúng.
+ ShipsToPlace: Một Dictionary lưu trữ các loại tàu và độ dài của chúng cần được đặt.
+ IsReady: Trạng thái sẵn sàng của người chơi.
- Lớp Game: Đại diện cho một trận đấu giữa hai người chơi.
+ Player1: Đối tượng Player của người chơi thứ nhất (người tạo phòng).
+ Player2: Đối tượng Player của người chơi thứ hai (người tham gia phòng).
+ CurrentTurnPlayer: Đối tượng Player của người chơi đang có lượt đi.
+ State: Trạng thái hiện tại của game (ví dụ: PlacingShips, InProgress, Finished). - Lớp
GameManager: Lớp tĩnh quản lý tất cả các Player đang kết nối và các Game đang diễn ra hoặc chờ đợi. + _players:
ConcurrentDictionary lưu trữ tất cả các
Player đang kết nối. ConcurrentDictionary + _pendingGames:
lưu trữ các game đang chờ người chơi thứ hai tham gia. + _activeGames:
ConcurrentBag lưu trữ các game đang diễn ra (cả hai người chơi đã tham
gia và đang trong giai đoạn đặt tàu hoặc chiến đấu).
2.3. Thiết kế luồng hoạt động chính
2.3.1. Luồng tạo và tham gia phòng chơi

1. Client A (Host) gửi bản tin host_game đến Server.
2. Server nhận bản tin, tạo một Game mới với Client A là Player1, tạo một GameId duy nhất
và lưu vào _pendingGames.
3. Server gửi bản tin game_hosted kèm theo GameId về cho Client A.
4. Client B (Joiner) nhập GameId và gửi bản tin join_game kèm GameId đến Server.
5. Server kiểm tra GameId:
+ Nếu tìm thấy và Player2 còn trống, Server gán Client B là Player2 cho Game đó,
chuyển game từ _pendingGames sang _activeGames.
+ Server gửi bản tin start_placing cho cả Client A và Client B.
+ Nếu không tìm thấy hoặc game đã đầy, Server gửi bản tin join_failed cho Client B.
2.3.2. Luồng đặt tàu
1. Client nhận bản tin start_placing, hiển thị giao diện đặt tàu.
2. Người chơi chọn một loại tàu và một ô trên bảng của mình, sau đó quyết định hướng đặt tàu.
3. Client gửi bản tin place_ship (kèm loại tàu, tọa độ X, Y, IsHorizontal) đến Server.
4. Server (Game.PlaceShip) nhận bản tin, kiểm tra tính hợp lệ của vị trí đặt tàu (trong phạm
vi, không chồng lấn, đủ độ dài cho tàu).
5. Server cập nhật bảng chơi của người chơi.
6. Server gửi bản tin place_ship_result (kèm Success/Message, thông tin tàu đã đặt) về cho Client.
7. Client nhận kết quả, nếu thành công thì vẽ tàu lên bảng và gạch tên tàu khỏi danh sách chờ đặt.
8. Khi người chơi đặt hết tất cả tàu, nút "Ready" xuất hiện. Người chơi nhấn "Ready".
9. Client gửi bản tin player_ready đến Server.
10. Server (Game.SetPlayerReady) nhận bản tin, đánh dấu người chơi đó là sẵn sàng.
11. Nếu cả hai người chơi đã sẵn sàng, Server (Game.StartBattle) chọn ngẫu nhiên người đi
trước, cập nhật trạng thái game thành InProgress, và gửi bản tin game_start cho cả hai
Client (kèm thông tin lượt đi). Nếu chỉ một người sẵn sàng, Server gửi bản tin
waiting_for_opponent_ready cho người chơi đó.
2.3.3. Luồng tấn công và xử lý kết quả
1. Client nhận bản tin game_start hoặc shot_result/opponent_shot với IsYourTurn = true,
hiển thị "Đến lượt bạn".
2. Người chơi chọn một ô trên bảng của đối thủ để bắn.
3. Client gửi bản tin fire_shot (kèm tọa độ X, Y) đến Server.
4. Server (Game.ProcessShot) nhận bản tin, kiểm tra tính hợp lệ (đúng lượt, tọa độ hợp lệ).
5. Server kiểm tra ô bắn trên bảng của đối thủ:
+ Nếu ô đó có tàu (cellState == 1): đánh dấu ô đó là trúng (2), giảm
ShipCellsRemaining của đối thủ. Kết quả là "Hit".
+ Nếu ô đó trống (cellState == 0): đánh dấu ô đó là trượt (3). Kết quả là "Miss".
6. Server kiểm tra ShipCellsRemaining của đối thủ. Nếu <= 0, trò chơi kết thúc.
7. Server gửi bản tin shot_result cho người bắn (kèm X, Y, Result, IsYourTurn=false).
8. Server gửi bản tin opponent_shot cho người bị bắn (kèm X, Y, Result, IsYourTurn=true).
9. Nếu game kết thúc, Server gửi bản tin game_over cho cả hai người chơi (kèm YouWon).
10. Client nhận bản tin, cập nhật giao diện (vẽ X/O lên ô đã bắn, cập nhật trạng thái lượt
chơi/kết thúc game).
2.4. Thiết kế giao thức truyền thông WebSocket
Tất cả các bản tin được gửi qua WebSocket đều là các chuỗi JSON. Dưới đây là cấu trúc
chung và các loại bản tin cụ thể được sử dụng trong hệ thống.
2.4.1. Cấu trúc bản tin cơ bản (GameMessage)
Mọi bản tin đều kế thừa từ lớp GameMessage và phải có trường Type để xác định loại bản GameMessage.cs
public class GameMessage {
public string Type { get; set; } }
Việc sử dụng JsonSerializable trong Program.cs giúp tối ưu hóa quá trình tin. serialize/deserialize JSON.
2.4.2. Các bản tin từ Client lên Server và từ Server xuống Client
Để đảm bảo giao tiếp hiệu quả và rõ ràng giữa Client và Server, chúng tôi đã định nghĩa
một tập hợp các bản tin JSON. Mỗi bản tin đều có trường Type để xác định mục đích của nó, và
các trường dữ liệu bổ sung tùy thuộc vào loại bản tin.
Dưới đây là định nghĩa của tất cả các lớp tin nhắn được sử dụng trong hệ thống, bao gồm cả các
bản tin Client gửi lên Server và các bản tin Server gửi xuống Client: GameMessage.cs
using System.Text.Json.Serialization;
namespace BattleshipGame.Models;
// Lớp cơ sở cho mọi tin nhắn public class GameMessage {
public string Type { get; set; } }
// --- CÁC MESSAGE MỚI CHO LOBBY ---
// Client gửi lên khi muốn tạo phòng
public class HostGameMessage : GameMessage { }
// Client gửi lên khi muốn tham gia phòng
public class JoinGameMessage : GameMessage
{
public string GameId { get; set; } }
// Server gửi về cho Host sau khi tạo phòng thành công
public class GameHostedMessage : GameMessage
{
public string GameId { get; set; } }
// Server gửi về nếu tham gia phòng thất bại
public class JoinFailedMessage : GameMessage
{
public string Reason { get; set; } }
// --- CÁC MESSAGE CŨ CHO GAMEPLAY ---
public class PlaceShipMessage : GameMessage {
public string ShipType { get; set; }
public int X { get; set; }
public int Y { get; set; }

public bool IsHorizontal { get; set; } }
public class PlaceShipResultMessage : GameMessage {
public bool Success { get; set; }
public string Message { get; set; }
public string ShipType { get; set; }

public int X { get; set; } public
int Y { get; set; } public int Length { get; set; } public bool
IsHorizontal { get; set; }
}
public class StartPlacingMessage : GameMessage { }
public class PlayerReadyMessage : GameMessage { }
public class FireShotMessage : GameMessage {
public int X { get; set; }
public int Y { get; set; }
}
public class GameStartMessage : GameMessage {
public int[][] YourBoard { get; set; }
public bool IsYourTurn { get; set; }
public string OpponentName { get; set; }
}
public class ShotResultMessage : GameMessage {
public int X { get; set; }
public int Y { get; set; } public
string Result { get; set; } public
bool IsYourTurn { get; set; }
}
public class OpponentShotMessage : GameMessage {
public int X { get; set; }
public int Y { get; set; } public
string Result { get; set; } public
bool IsYourTurn { get; set; }
}
public class GameOverMessage : GameMessage {
public bool YouWon { get; set; } }
public class ErrorMessage : GameMessage {
public string Message { get; set; } }
Giải thích chi tiết về các loại bản tin:
+ HostGameMessage: Client yêu cầu tạo phòng chơi mới.
+ JoinGameMessage: Client yêu cầu tham gia phòng chơi với GameId được chỉ định.
+ GameHostedMessage: Server thông báo cho Host về việc tạo phòng thành công và cung cấp GameId.
+ JoinFailedMessage: Server thông báo cho Client về lỗi khi tham gia phòng, kèm theo Reason.
+ StartPlacingMessage: Server thông báo bắt đầu giai đoạn đặt tàu cho cả hai Client.
+ PlaceShipMessage: Client gửi yêu cầu đặt tàu với ShipType, tọa độ (X, Y) và IsHorizontal.
+ PlaceShipResultMessage: Server thông báo kết quả đặt tàu (Success, Message) và thông
tin chi tiết về tàu đã đặt (ShipType, X, Y, Length, IsHorizontal).
+ PlayerReadyMessage: Client thông báo đã đặt xong tàu và sẵn sàng bắt đầu trận đấu.
+ GameMessage (waiting_for_opponent_ready): Server thông báo Client đã sẵn sàng đang chờ đối thủ.
+ FireShotMessage: Client bắn vào ô tọa độ (X, Y) trên bảng đối thủ.
+ GameStartMessage: Server thông báo trận đấu bắt đầu, kèm theo YourBoard ban đầu,
IsYourTurnOpponentName.
+ ShotResultMessage: Server thông báo kết quả cú bắn của người chơi (X, Y, Result) và
trạng thái lượt chơi (IsYourTurn).
+ OpponentShotMessage: Server thông báo cho người chơi bị bắn về cú bắn của đối thủ (X, Y,
Result) và trạng thái lượt chơi (IsYourTurn).
+ GameOverMessage: Server thông báo trò chơi kết thúc và kết quả (YouWon).
+ ErrorMessage: Server gửi thông báo lỗi chung, kèm theo Message chi tiết.
2.5. Lưu đồ thuật toán các chức năng chính
2.5.1. Lưu đồ Tạo/Tham gia trò chơi

2.5.2. Lưu đồ Đặt tàu
2.5.3. Lưu đồ Xử lý lượt bắn
CHƯƠNG 3: TRIỂN KHAI HỆ THỐNG 3.1. Cấu trúc project
Cấu trúc thư mục của project theo chuẩn ASP.NET Core MVC, với các thư mục chính như sau:
- Controllers/: Chứa các lớp điều khiển xử lý các yêu cầu HTTP thông thường (ví dụ:
HomeController.cs cho trang chủ).
- Models/: Chứa các lớp mô hình dữ liệu và logic nghiệp vụ chính của trò chơi. + ErrorViewModel.cs + Game.cs + GameManager.cs
+ GameMessage.cs (và các lớp tin nhắn kế thừa) + Player.cs
- Properties/: Chứa cấu hình dự án, bao gồm launchSettings.json cho cấu hình khởi chạy.
- Views/: Chứa các file Razor View (.cshtml) định nghĩa giao diện người dùng.
+ Home/: Chứa Index.cshtml (trang chính của game).
+ Shared/: Chứa _Layout.cshtml (layout chung) và Error.cshtml.
+ _ViewImports.cshtml, _ViewStart.cshtml.
- wwwroot/: Chứa các tài nguyên tĩnh phục vụ Client.
+ css/: Chứa các file CSS (site.css, _Layout.cshtml.css).
+ js/: Chứa các file JavaScript (site.js chứa logic game).
- appsettings.json, appsettings.Development.json: Các file cấu hình ứng dụng.
- Program.cs: File cấu hình và khởi tạo ứng dụng ASP.NET Core, bao gồm thiết lập WebSocket.
3.2. Triển khai phía Server (ASP.NET Core)
3.2.1. Khởi tạo WebSocket và Middleware

Trong Program.cs, WebSocket được kích hoạt bằng phương thức UseWebSockets, cho
phép ứng dụng ASP.NET Core xử lý các kết nối thời gian thực. Một middleware tùy chỉnh được
thiết lập để lắng nghe các yêu cầu đến đường dẫn /ws, là nơi duy nhất nhận kết nối WebSocket.
Khi client kết nối thành công, phương thức HandleGameSession được gọi để tạo một đối
tượng Player, thêm vào GameManager, và duy trì vòng lặp nhận tin nhắn từ phía client. Khi client
đóng kết nối hoặc xảy ra lỗi, Player sẽ bị xóa khỏi hệ thống. - Cấu hình quan trọng:
+ KeepAliveInterval = 120s: đảm bảo giữ kết nối ổn định.
+ Phân tích và xử lý thông điệp trong một buffer kích thước 4KB.
+ AppJsonSerializerContext hỗ trợ tối ưu JSON (cho AOT và trimming).
3.2.2. Quản lý trạng thái trò chơi (GameManager)
GameManager là một lớp tĩnh có vai trò trung tâm điều phối, quản lý toàn bộ luồng game
giữa các người chơi. Sử dụng các cấu trúc thread-safe như ConcurrentDictionary và
ConcurrentBag để đảm bảo an toàn trong môi trường đa luồng. - Chức năng chính:
+ AddPlayer / RemovePlayer: quản lý danh sách người chơi đang hoạt động.
+ HandleMessage: nhận và phân tích tin nhắn từ client, điều hướng xử lý dựa trên Type của tin nhắn, ví dụ:
+ "host_game" → gọi HostGame() để tạo game mới với ID ngẫu nhiên.
+ "join_game" → gọi JoinGame() để ghép người chơi vào phòng chờ.
+ "place_ship", "fire_shot", "player_ready": xử lý tương tác trong game.
+ Tin nhắn phản hồi được gửi lại cho từng người chơi thông qua SendMessageAsync().
3.2.3. Xử lý logic trò chơi (Game và Player) - Player.cs:
+ Đại diện cho mỗi người chơi đang kết nối:
+ Thuộc tính: Id, Socket, Board, ShipsToPlace, IsReady, ShipCellsRemaining.
+ Phương thức: SendMessageAsync() để gửi tin nhắn cho client.
+ Hỗ trợ kiểm tra trạng thái đặt tàu (HasPlacedAllShips()). - Game.cs:
+ Quản lý trạng thái một ván đấu giữa hai người chơi:
+ Giai đoạn game: PlacingShips, InProgress, Finished.
+ Phân quyền lượt chơi qua CurrentTurnPlayer, xử lý hành động theo giai đoạn:
+ StartShipPlacement() → gửi lệnh bắt đầu đặt tàu cho cả hai.
+ PlaceShip() → kiểm tra hợp lệ, cập nhật bảng chơi.
+ SetPlayerReady() → chờ cả hai sẵn sàng trước khi bắt đầu trận đấu.
+ StartBattle() → chọn ngẫu nhiên người bắn trước.
+ ProcessShot() → kiểm tra trúng/tượt, kết thúc game khi tàu bị bắn hết.
3.2.4. Các đối tượng tin nhắn (GameMessage) -
Toàn bộ giao tiếp giữa client và server đều dựa trên các tin nhắn JSON. Mỗi tin nhắn là
một lớp con của GameMessage, ví dụ:
+ HostGameMessage, JoinGameMessage, PlaceShipMessage, FireShotMessage,
GameStartMessage, GameOverMessage, v.v.
+ Những lớp này được sử dụng để:
● Giải mã dữ liệu khi nhận từ WebSocket.
● Tự động tuần tự hóa khi gửi phản hồi cho client. -
Việc định nghĩa rõ từng loại tin nhắn giúp giảm lỗi, dễ mở rộng và duy trì giao tiếp rõ ràng trong toàn bộ game.
3.3. Triển khai phía Client (HTML, CSS, JavaScript)
Phía Client được xây dựng chủ yếu bằng JavaScript (site.js), HTML (trong
Views/Home/Index.cshtml) và CSS (site.css, _Layout.cshtml.css).
3.3.1. Kết nối WebSocket và xử lý sự kiện
File wwwroot/js/site.js chứa toàn bộ logic tương tác phía Client, bao gồm thiết lập kết
nối WebSocket và xử lý các sự kiện từ Server.
wwwroot/js/site.js
document.addEventListener("DOMContentLoaded", function () { let socket;
let connectionPurpose = ''; // 'host' hoặc 'join' let gameIdToJoin = '';
function connectToServer() {
// Tạo bảng ngay khi kết nối để hiển thị lưới
createBoard(playerBoardEl); createBoard(opponentBoardEl);

const scheme = document.location.protocol === "https:" ? "wss" : "ws";
const port = document.location.port ? `:${document.location.port}` : "";
const connectionUrl =
`${scheme}://${document.location.hostname}${port}/ws`;
socket = new WebSocket(connectionUrl); // Tạo kết nối WebSocket
socket.onopen = () => { // Khi kết nối
mở if (connectionPurpose === 'host') {

socket.send(JSON.stringify({ Type: 'host_game' })); // Gửi yêu cầu host
} else if (connectionPurpose === 'join') {
socket.send(JSON.stringify({ Type: 'join_game', GameId: gameIdToJoin })); // Gửi yêu cầu join
} };
socket.onmessage = (event) =>
handleServerMessage(JSON.parse(event.data)); // Xử lý tin nhắn từ Server
socket.onclose = () => { /* Xử lý khi kết nối đóng */ };
socket.onerror = (err) => { /* Xử lý lỗi */ };
}
function handleServerMessage(message) { switch (message.Type) { case "game_hosted":
// Hiển thị Game ID cho người chơi host
document.getElementById("host-info").innerHTML = `
class="game-id-display">${message.GameId}`;
break; case "start_placing":
// Chuyển sang giao diện đặt tàu
document.getElementById("lobby-screen").classList.add('d-none');
document.getElementById("game-container").classList.remove('d-none'); setupPlacementUI(); break; case "place_ship_result": if (message.Success) {
drawPlacedShip(message); // Vẽ tàu lên bảng của người chơi
removeShipFromList(message.ShipType); // Xóa tàu
khỏi danh sách chờ đặt } else {

alert(`Placement failed: ${message.Message}`); } break; case "game_start": //
Bắt đầu trận đấu

placementUiEl.classList.add('d-none');
myTurn = message.IsYourTurn; updateStatus(); break; case "shot_result":
updateOpponentBoard(message.X, message.Y, message.Result);
// Cập nhật bảng đối thủ
myTurn = message.IsYourTurn; updateStatus(); break; case "opponent_shot":
updatePlayerBoard(message.X, message.Y, message.Result); //
Cập nhật bảng của người chơi

myTurn = message.IsYourTurn; updateStatus(); break;
// ... (các case khác cho game_over, join_failed,
waiting_for_opponent_ready) } }
// ... (các hàm xử lý UI và logic khác)
initialize(); // Khởi tạo các sự kiện ban đầu });
Đoạn mã JavaScript này cho thấy cách Client khởi tạo kết nối WebSocket đến endpoint
/ws, gửi tin nhắn host_game hoặc join_game khi kết nối được mở. Hàm handleServerMessage
là trung tâm xử lý, phân phối các tin nhắn nhận được từ Server tới các hàm chuyên biệt để cập
nhật giao diện người dùng và trạng thái game.