Tài liệu tham khảo chuyên ngành tin học Lập trình Game “Đánh bài tiến lên” chạy trong mạng LAN
Trang 1LỜI NÓI ĐẦU
Cùng với sự phát triển nhanh chóng của Internet, Game Online hiện nay đang cũngđang rất thịnh hành và trở thành một phần không thể thiếu của nhiều tầng lớp trong xãhội Tất nhiên, xét trên một phương diện nào đó có thể nhận thấy nhiều mặt tiêu cực củaGame Online nhưng có thể nhận thấy rõ ràng rằng để lập trình được một Game Online,ngoài việc lập trình viên phải có những kiến thức về đồ họa, về thuật toán … thì một phầnkhông thể thiếu đó là kiến thức về mạng và việc truyền thông tin trên mạng Vì vậy, nếucoi Game Online là công cụ để lập trình viên tìm hiểu và thực hành những kiến thức vềmạng thì đây thực sự là một công cụ hữu hiệu.
Sau thời gian học tập và nghiên cứu về môn học Mạng và Truyền số liệu, chúng tôi
nhận đã quyết định phải viết một ứng dụng nhỏ để áp dụng những gì đã biết về môn học
vào thực tiễn.Ứng dụng được cả nhóm lựa chọn là Viết game “Đánh bài tiến lên” dựa
trên Socket giới hạn trong mạng LAN.Lý do lựa chọn ứng dụng này:
Thứ nhất, đây là một trò chơi đơn giản, dễ chơi, thuật toán dễ xây dựng nên phùhợp với khoảng thời gian ngắn được cho phép để hoàn thành trò chơi này Hơn
nữa, đây là ứng dụng để thực hành về Mạng và Truyền số liệu nên không cần
thiết phải chú trọng vào thuật toán game.
Thứ hai, với trò chơi này đòi hỏi nhiều người chơi nên có thể thực hành đượcviệc xây dựng các Room, việc chat giữa hai hoặc nhiều người với nhau, việctruyền thông điệp point - point hay MultiCast …
Thứ ba: Ứng dụng được viết trên mạng LAN do hạn chế về cơ sở hạ tầng vàthời gian.
Thực hiện: Sử dụng ngôn ngữ lập trình Visual C# dựa trên nền tảng DotNetFX 1.1
Nhóm chúng tôi gồm có 5 người:1 Phan Anh Dũng
2 Thân Quốc Lâm3 Nguyễn Hồng Phương4 Ngô Đức Thuận5 Nguyễn Thành Trung
Đều là sinh viên lớp CNTT – KSTN – K48.
Do thời gian thực hiện ứng dụng rất ngắn nên chương chưa có nhiều thời gian kiểmthử, chắc chắn còn tiềm ẩn nhiều lỗi Chúng tôi sẽ cố gắng hoàn thiện thêm trong thờigian sắp tới.
Trang 2MỤC LỤC
LỜI NÓI ĐẦU 1
MỤC LỤC 2
1 Giới thiệu về trò chơi “Đánh bài tiến lên” 3
1.1 Giới thiệu trò chơi 3
3.1.1 Module Socket và quản lý phòng chơi 16
3.1.2 Module thực hiện và kiểm soát luật chơi 20
3.1.3 Kết hợp giữa hai module 22
Trang 31 Giới thiệu về trò chơi “Đánh bài tiến lên”
1.1 Giới thiệu trò chơi
Trò chơi đánh bài tiến lên là một trò chơi bài đơn giản, dễ chơi Trò chơiđược thực hiện trên bộ bài 52 quân bài Các quân bài có tên là A (át), 2, 3, 4, 5, 6,7, 8, 9, 10, J, Q, K, lần lượt với mỗi tên quân bài có 4 chất: rô, cơ, bích, nhép.Trong trò chơi đánh bài tiến lên thứ tự xếp hạng của các quân bài là:3<4<5<6<7<8<9<10<J<Q<K<A<2 Trên bộ bài 52 quân các quân bài có chất rôhoặc cơ có màu đỏ, còn các quân bài có chất bích và nhép có màu đen.
Mục tiêu là ai đánh được tất cả các quân bài trên tay xuống trước là ngườithắng cuộc.
1.2 Luật chơi
Đầu tiên, bộ bài 52 quân sẽ lần lượt được chia đều cho mỗi người chơi Ngườichơi có quân bài 3 bích sẽ được bắt đầu lượt đánh bài đầu tiên và quân bài đượcđánh phải là quân bài 3 bích hoặc một bộ ghép có chứa 3 bích Trong các lượt chơitiếp theo, người chơi có thể bắt đầu với một quân bài hoặc một bộ ghép các quânbài Một bộ ghép các quân bài có thể là một đôi (2 quân bài cùng giá trị, và cùngmàu, ví dụ đôi 7 đỏ (gồm 7 rô và 7 cơ), đôi 8 đen (gồm 8 nhép và 8 bích), một bộ 3(3 quân bài có cùng giá trị gồm một bộ đôi, và một quân khác màu), một bộ tứ (4quân bài có cùng giá trị), một bộ dọc gồm từ 3 quân bài trở lên có cùng chất, và cógiá trị liên tiếp nhau (ví dụ 3, 4, 5 nhép).
Tại mỗi lượt đánh bài, quyền đánh bài sẽ lần lượt được trao cho người tiếptheo theo vòng tròn (cùng chiều kim đồng hồ) Khi đến lượt đánh bài, người chơicó thể chọn một hoặc một số quân bài trên tay để đánh chặn quân bài của ngườichơi vừa đánh xuống hoặc là bỏ qua (người chơi sẽ mất quyền đánh bài trong lượtchơi này)
Để đánh chặn được các quân bài vừa đánh xuống, người chơi phải chọn cácquân bài thỏa mãn các yêu cầu sau:
Nếu trước đó chỉ có một quân bài được đánh xuống, người chơi phảichọn quân bài cùng chất với quân bài đó và có giá trị lớn hơn, hoặcchọn một quân 2 bất kỳ để đánh chặn.
Nếu trước đó là một bộ đôi, người chơi phải chọn một bộ đôi cùngmàu (đỏ hoặc đen) và có giá trị lớn hơn, hoặc chọn 2 quân 2 bất kỳ đểđánh chặn.
Nếu trước đó là một bộ 3, người chơi phải chọn một bộ 3 có một bộđôi cùng màu, một quân khác màu có cùng chất với bộ 3 được đánhtrước đó và phải có giá trị lớn hơn để đánh chặn.
Trang 4 Nếu trước đó là một bộ dọc, người chơi phải chọn một bộ dọc có cùngchất, có giá trị lớn hơn và có số quân bài bằng số quân bài của bộ dọctrước đó.
Người chơi đánh quân bài cuối cùng trong lượt chơi sẽ được quyền bắt đầulượt chơi tiếp theo Trừ lượt đánh đầu tiên (quân bài được đánh phải là quân bài 3bích hoặc một bộ ghép có chứa 3 bích), trong các lượt chơi tiếp theo người chơi cóthể bắt đầu với bất kỳ lá bài nào trên tay.
Người chơi đầu tiên đánh hết các quân bài đầu tiên sẽ xếp thứ nhất Hai ngườichơi đánh đánh hết các quân bài đầu tiên tiếp theo sẽ lần lượt xếp thứ 2 và thứ 3.Người chơi không đánh hết các quân bài là người xếp cuối cùng.
Kết nối socket tới 1 IPEndPoint cục bộ
Đặt socket trong trạng thái lắng nghe (Listen) Chấp nhận kết nối đến socket.
Tạo ra Sever:
Bước đầu tiên để xây dựng 1 TCP Sever là tạo ra một thể hiện của đối tượng Socket Các chức năng cần thiết khác cho các thao tác của Sever là việc sử dụng các phương thức của đối tượng Socket.
4 nhiệm vụ đó được thực hiện như sau:
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Socket client = newsock.Accept();
Đối tượng Socket được tạo bởi phương thức Accept() được sử dụng để truyềndữ liệu theo cả 2 hướng giữa Sever và Remote Client.
Trang 5Các bước cơ bản được thể hiện trong ví dụ sau:SimpleTcpSrvr.cs
using System;
using System.Net;
using System.Net.Sockets;using System.Text;
class SimpleTcpSrvr{
public static void Main() {
int recv;
byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp); newsock.Bind(ipep);
{
data = new byte[1024];
recv = client.Receive(data); if (recv == 0)
break;
Console.WriteLine(
Encoding.ASCII.GetString(data, 0, recv)); client.Send(data, recv, SocketFlags.None); }
Console.WriteLine("Disconnected from {0}", clientep.Address);
client.Close(); newsock.Close();
Trang 6Phương thức Receive() và Send() chỉ làm việc với dãy byte Tất cả dữ liệuđược truyền Socket đều phải được chuyển đổi sang chuỗi byte Trong ví dụ trên tasử dụng xâu văn bản, phương thức Encoding.ASCII được dùng để chuyển đổi cácxâu sang các chuỗi byte và ngược lại.
Một đối tượng IPEndPoint được định nghĩa cho Local Sever:
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Bằng cách sử dụng trường IPAddress.Any, thì Sever sẽ chấp nhận tất cả cácyêu cầu kết nối trên tất cả các mạng mà có thể được cấu hình trên hệ thống Nếu tamuốn chỉ chấp nhận đối với 1 gói các Interface riêng biệt, ta có thể sử dụng địa chỉIP
IPEndPoint ipep = new
IPEndPoint(IPAddress.Parse("192.168.1.6"), 9050);
Sau khi xác địn đối tượng IPEndPoint thích hợp, khởi tạo Socket() được gọi để tạo TCP socket Phương thức Bind() và Listen() được sử dụng để kết nối socket đến đối tưưọng IPEndPoint mới và lắng nghe các kết nối tới.
Cuối cùng, phương thức Accept() được sử dụng để chấp nhận các kết nối đếntừ client Phương thức Accept() trả về một đối tượng Socket mới, đối tượng này sẽđược sử dụng trong tấy cả các giao tiếp với client.
Sau khi phương thức Accept() chấp nhận kết nối, thông tin địa chỉ IP củaclient yêu cầu có thể lấy được nhờ thuộc tính RemotEndPoint:
IPEndPoint clientep =
(IPEndPoint)client.RemoteEndPoint;
Một khi IPEndPoint được tạo ra sử dụng thông tin client từ xa, bạn có thể truycập nó và sử dụng các thuộc tính Address và Port (địa chỉ và cổng) Đây là kỹ thuậtsử dụng cho việc định danh cho các client riêng biệt mà kết nối đến server.
Sau khi socket được thiết lập với client, sự kết hợp client/sever đồng bộtruyền dữ liệu Nếu cả 2 server và client cùng cố gắng nhận dữ liệu cùng 1 luc,hoặc cùng gửi dữ liệu trong cùng 1 thời điển gây ra hiện tượng deadlock
Ví dụ thông điệp Welcom gửi cho client, và sau đó chờ thông điệp từclient:
string welcome = "Welcome to my test server"; data = Encoding.ASCII.GetBytes(welcome); client.Send(data, data.Length,
SocketFlags.None); while(true)
{
data = new byte[1024];
recv = client.Receive(data); if (recv == 0)
break;
Trang 7Console.WriteLine(
Encoding.ASCII.GetString(data, 0, recv)); client.Send(data, recv, SocketFlags.None); }
Client muốn giao tiếp với sever phải chuận bị nhận thông điệp ngay khi màkết nối được tạo ra Sauk hi nhận dữ liệu, client phải luôn phiên giữa việc gửi vànhận dữ liệu.
Phưong thức Receive() thay thế dữ liệu trong buffer dữ liệu, kích thước củavùng dữ liệu đệm được đặt trước Nếu vùng đệm không được xác lập lại giá trịgốc, thì lần gọi Receive() tiếp theo, vùng đệm chỉ chứa được dữ liệu tối đa bằnglần gọi trước.
Trang 82.2 Sử dụng C# Streams với TCP
Việc quản lý các thông điệp trong kết nối TCP là thách thức cho các nhà lậptrình, .NET Framework cung cấp một số lớp giải quyết vấn đề này Là lớpNetWorkStream, cung cấp 2 stream interface cho socket là 2 lớp: StreamReader vàStreamWriter, được dùng để gửi và nhận thông điệp văn bản sử dụng TCP.
NetworkStream Class:Phương thức khởi tạo:
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
NetworkStream ns = new NetworkStream(newsock);
Sau khi đối tượng NetworkStream được khởi tạo, có 1 vài thuộc tính và phươngthức áp dụng vào đối tượng Socket
Table 5.1: NetworkStream Class Properties
DataAvailable Is true if there is data available to be readCác phương thức:
NetworkStream Class Methods
NetworkStream
Trang 9NetworkStream Class Methods
GetLifetimeService() Retrieves the lifetime service object for the NetworkStream
InitializeLifetimeService() Obtains a lifetime service object to control the lifetime policy for the NetworkStream
NetworkStream
NetworkStream
Sử dụng phương thức Read để đọc khối dữ liệu từ NetworkStream Đọc chuỗibyte vào buffer, offset là vị trí buffer nơi bắt đầu đặt dữ liệu, và size số byte phải đọc
int Read(byte[] buffer, int offset, int size)
Phương thức Read() trả về giá trị integer là số byte đọc được từ NetWorkStream và được đặt trong buffer.
Trang 103 Using The C# Sockets Helper Classes:
Các phương thức của TcpClient
TcpClient Methods
GetHashCode() Gets a hash code suitable for use in hash functions
receive data
TcpClient Object Properties
Trang 11TcpClient Object Properties
receiving TCP buffers that are not full
ReceiveBufferSize Gets or sets the size of the TCP receive bufferReceiveTimeout Gets or sets the receive timeout value of the socketSendBufferSize Gets or sets the size of the TCP send buffer
Trang 122.3 Socket không đồng bộ
Có 2 cách để tránh sử dụng blocking sockets trong các ứng dụng về mạng:- Sử dụng các socket không đồng bộ
- Sử dụng các phương thức non-blocking socket
2.3.1 Windows Event Programming:
Chương trình chạy ở chế độ Windows console sử dụng mô trình lập trình cócấu trúc truyền thống Trong chương trình có cấu trúc, luồng chương trình đượcđiều khiển ngay trong chính chương trình đó Người dùng không thể tuỳ biến thayđội việc thực hiện chương trình, mà chỉ tuân theo tuần tự của chương trình địnhtrước Trái lại các chương trình Windows sử dụng “ event programming model”.
Windows event programming dựa trên luồng chương trình vào các sự kiện.Khi các sự kiện xuất hiện trong chương trình, các phương thức được gọi và thựchiện dựa trên vào các sự kiện Phương pháp này không làm việc tốt đối với cáchàm dạng blocking Khi 1 ứng dụng cần trình bày giao diện với người dùng, nó sẽđợi cho sự kiện từ người dùng để xác định những hàm nào được thực thi Eventprogramming giả thiết rằng trong khi các hàm khác đang xử lý ( ví dụ như các truycập mạng) người dùng vẫn có thể điều khiển giao diện đồ hoạ Điều này cho phápngười dùng thực hiện nhiều chức năng khác trong khi chờ phản hồi từ mạng, ngaycả khi huỷ kết nối mạng nếu cần Nếu các hàm blocking được sử dụng, việc thựchiện chương trình sẽ chờ cho đến khi hàm này được thực hiện, và người dùngkhông thể điểu khiển cả giao diện lẫn chương trình.
Mô hình chương trình Window hướng sư kiện
2.3.2 Sử dụng Events và Delegates:
Một sự kiện là 1 thông điệp được gửi bởi đối tượng mà mô tả một hành động xảy xa.Thông điệp định danh hành động và truyền dữ liệu cần thiết có liên quan cho hành động.Các sự kiện là bất cứ việc gì, như việc nhấn nút, ( nơi mà thông điệp biểu diễn bởi têncủa button), hay là gói nhận được từ một socket ( nơi mà thông điệp thể hiện bằng socket
Trang 13nhận dữ liệu) Người gửi sự kiện không cần biết, đối tượng nào sẽ bắt thông điệp sự kiện,ngay cả khi nó đã được gửi đi qua hệ thống Windows Điều này phụ thuộc vào đối tượngnhận sự kiện mà đăng kí với hệ thông Window và cho biét kiểu của sự kiện và người nhậnmuốn nhận.
Windows event senders and receivers
Bên nhận sự kiện được định danh trong hệ thống Windows bởi một lớp pointer được gọilà delegate ( uỷ quyền) Delegate là lớp mà giữ tham chiếu đến phương thức mà có thể bắtsự kiện được nhận Khi Windows nhận được một sự kiện, nó kiểm tra xem xét nếu bất cứdelegate nào đăng kí để bắt sự kiện này, thông điệp sự kiện được thông qua phương thứcđược định ra bởi delegate Sauk hi phương thức này hoàn thành hệ thống Window xử lýsự kiện tiếp theo xuất hiện, cho đến khi có tín hiệu kết thúc chương trình.
2.3.3 The AsyncCallback Class:
Các sự kiện có thể kích hoạt delegate Lớp AsyncCallback cho phép phưonưg thức khởiđộng một hàm không đồng bộ và cung cấp phương thức uỷ quyền (delegate) để gọi khihàm không đồng bộ hoàn thành.
Quá trình này khác với chương trình hướng sự kiện chuẩn Phương thức này tự đăng kýmột AsyncCallback delegate để gọi khi phương thức hoàn thành chức năng Ngay khiphương thức này xuất hiện và phương thông báo công việc đã hoàn thành cho hệ thốngWindow, một sự kiện được kích hoạt và truyền điều khiển chương trình cho phương thứcđược định ra trong AsyncCallback delegate đã đăng ký.
Lớp Socket sử dụng phương thức được định ra trong AsyncCallback để cho phép cáchàm mạng có thể thực hiện các quá trình xử lý không đồng bộ Nó sẽ báo đến OS khi màcác hàm mạng được hoàn thành và chuyển điều khiển cho phương thức AsncCallback đểhoàn thành chức năng mạng Trong môi trường Window, những phương thức này giúptránh việc một ứng dụng phải dừng (lock-up) trong khi chờ các hàm về mạng hoàn thành.
Trang 14Using Asynchronous Sockets
.Net asynchronous Socket methods
EndAccept()
Trang 152.3.4 Sử dụng Thread:
Làm thế nào để các chương trình ứng dụng chạy trong Windows
Khi chạy các chương trình trong hệ thống Windows, phần mềm hệ điều hànhphải biết cách quản lý các chương trình đảm bảo rằng tất cả các chương trình đềucó cơ hội sử dụng thời gian của CPU và truy cập bộ nhớ và các thiết bị vào ra Đểthực hiện việc này, OS quản lý mỗi chương trình chứa trong 1tiến trình 1 tiến trìnhlà biểu diễn thể hiện của một chuương trình Nếu bất cứ chương trình sao khác nàochạy cùng 1 thời gian, mỗi thể hiện của chương trình tạo ra 1 tiến trình mới.
Thread Class
Sử dụng lớp Thread để tạo ra đối tương Thread mới Định dạng của việc khởitạo Thread, với start là phương thức uỷ quyền ThreadStart.
Thread(ThreadStart start)Và tạo một thread mới
Thread newThread = new Thread(new ThreadStart(newMethod));
void newMethod(){
Sau khi đối tượng được tạo ra, nó được điểu khiển bởi các phương thức chính sau:The Thread Class Methods
Trang 16The Thread Class Methods
Interrupt() Interrupts a thread that is in the Wait thread stateJoin() Blocks the calling thread until the thread terminates
Start() Causes the operating system to change the thread state to Running