Cách thiết kế cơ sở dữ liệu đủ linh hoạt để chứa một số trò chơi bài rất khác nhau.
Gần đây, chúng tôi đã chỉ ra cách một cơ sở dữ liệu có thể được sử dụng để lưu trữ kết quả trò chơi board. Trò chơi trên bàn rất thú vị, nhưng chúng không phải là phiên bản trực tuyến duy nhất của trò chơi cổ điển. Trò chơi đánh bài cũng rất phổ biến. Họ giới thiệu một yếu tố may mắn vào trò chơi, và có nhiều điều hơn may mắn khi tham gia vào một trò chơi bài hay!
Trong bài viết này, chúng tôi sẽ tập trung vào việc xây dựng mô hình dữ liệu để lưu trữ các trận đấu, kết quả, người chơi và điểm số của trò chơi. Thách thức chính ở đây là lưu trữ dữ liệu liên quan đến nhiều trò chơi bài khác nhau. Chúng tôi cũng có thể xem xét phân tích dữ liệu này để xác định chiến lược giành chiến thắng, cải thiện kỹ năng chơi của riêng mình hoặc xây dựng một đối thủ AI tốt hơn.
Bốn trò chơi bài mà chúng tôi sẽ sử dụng trong cơ sở dữ liệu của mình
Bởi vì người chơi không thể kiểm soát ván bài mà họ được chia, các trò chơi bài kết hợp chiến lược, kỹ năng và may mắn. Yếu tố may mắn đó mang lại cho người mới bắt đầu cơ hội đánh bại một người chơi có kinh nghiệm, và nó làm cho các trò chơi bài trở nên gây nghiện. (Điều này khác với các trò chơi như cờ vua, dựa nhiều vào logic và chiến lược. Tôi đã nghe nhiều người chơi nói rằng họ không quan tâm đến việc chơi cờ vua vì họ không thể tìm thấy đối thủ ở cấp độ kỹ năng của mình.)
Chúng tôi sẽ tập trung vào bốn trò chơi bài nổi tiếng:poker, blackjack, belot (hoặc belote) và préférence. Mỗi người trong số họ có các quy tắc tương đối phức tạp và đòi hỏi một thời gian để nắm vững. Tỷ lệ may rủi và kiến thức cũng khác nhau ở mỗi trận đấu.
Chúng ta sẽ xem nhanh các quy tắc đơn giản và thông tin chi tiết cụ thể cho tất cả bốn trò chơi bên dưới. Mô tả trò chơi khá thưa thớt, nhưng chúng tôi đã đưa vào đủ để hiển thị các chế độ chơi khác nhau và các quy tắc đa dạng mà chúng tôi sẽ gặp phải trong quá trình thiết kế cơ sở dữ liệu.
Xì dách:
- Bộ bài: Một đến tám bộ bài, mỗi bộ 52 lá; không có thẻ joker
- Người chơi: Người chia bài và 1 hoặc nhiều đối thủ
- Đơn vị được sử dụng: Thường là tiền
- Quy tắc Cơ bản: Người chơi nhận được 2 thẻ mà chỉ họ có thể nhìn thấy; người chia bài nhận được hai lá bài, một lá ngửa và một lá úp xuống; mỗi người chơi quyết định rút thêm thẻ (hoặc không); nhà cái rút ra sau cùng. Các thẻ có giá trị điểm được chỉ định trong khoảng từ 1 đến 11.
- Hành động của người chơi có thể xảy ra: Đánh, đứng, chia cắt, đầu hàng
- Mục tiêu &Điều kiện Chiến thắng: Tổng số thẻ của người chơi lớn hơn của người chia bài; nếu bất kỳ người chơi nào vượt quá 21 tuổi, người chơi đó sẽ thua.
Xì tố (Texas Hold’Em):
- Bộ bài: Bộ bài tiêu chuẩn (còn được gọi là bộ đồ Pháp) 52 lá; không có thẻ joker. Thẻ thường có màu đỏ và đen.
- Người chơi: Hai đến chín; người chơi thay phiên nhau xử lý
- Đơn vị được sử dụng:Thường là chip
- Quy tắc Cơ bản: Mỗi người chơi bắt đầu bằng cách được chia hai lá bài; người chơi đặt cược của họ; ba quân bài được chia ngửa ở giữa bàn; người chơi lại đặt cược; một lá bài thứ tư được đặt ở giữa và người chơi đặt cược lại; sau đó quân bài thứ năm và quân bài cuối cùng được đặt và hoàn thành vòng cược cuối cùng.
- Hành động của người chơi có thể xảy ra: Gấp, Gọi, Tăng, Mù nhỏ, Mù lớn, Phục hồi
- Mục tiêu: Kết hợp 5 lá bài tốt nhất có thể (từ 2 lá trên tay người chơi và 5 lá ở giữa bàn)
- Điều kiện Chiến thắng:Thông thường để giành được tất cả các chip trên bàn
Belot (biến thể của Belote trong tiếng Croatia):
- Bộ bài: Thường là bộ bài 32 lá truyền thống của Đức hoặc Hungary; không có thẻ joker
- Người chơi: Hai đến bốn; thường là bốn người chơi trong cặp hai người
- Đơn vị được sử dụng: Điểm
- Quy tắc Cơ bản: Đối với trò chơi bốn người chơi, mỗi người chơi có sáu quân bài trên tay và hai quân bài úp xuống; người chơi đầu tiên đấu giá cho bộ đồ át chủ bài; sau khi át chủ bài được xác định, họ lấy hai quân bài úp xuống và đặt vào tay mình; một vòng khai báo sau đó, trong đó một số kết hợp thẻ nhất định được công bố để có thêm điểm; tiếp tục chơi cho đến khi tất cả các thẻ đã được sử dụng.
- Hành động của người chơi có thể xảy ra: Vượt qua, Phù hợp với giá thầu, Tuyên bố, Thẻ ném
- Bàn thắng cho bàn tay: Để giành được hơn một nửa số điểm
- Điều kiện Chiến thắng: Hãy là đội đầu tiên ghi được 1001 điểm trở lên
Préférence:
- Bộ bài: Thông thường nhất là bộ bài 32 lá truyền thống của Đức hoặc Hungary; không có thẻ joker
- Người chơi: Ba
- Đơn vị: Điểm
- Quy tắc Cơ bản: Tất cả người chơi được chia 10 lá bài; hai thẻ "kitty" hoặc "talon" được đặt ở giữa bàn; người chơi xác định xem họ có muốn đấu giá một bộ đồ hay không; người chơi quyết định chơi hay không.
- Hành động của người chơi có thể xảy ra: Vượt qua, Phù hợp với giá thầu, Chơi, Không chơi, Ném thẻ
- Mục tiêu: Phụ thuộc vào biến thể của Préférence được chơi; trong phiên bản tiêu chuẩn, người đặt giá thầu phải giành được tổng cộng sáu thủ thuật.
- Điều kiện Chiến thắng: Khi tổng điểm của cả ba người chơi bằng 0, người chơi có số điểm thấp nhất sẽ thắng.
Tại sao nên kết hợp Cơ sở dữ liệu và Trò chơi bài?
Mục tiêu của chúng tôi ở đây là thiết kế một mô hình cơ sở dữ liệu có thể lưu trữ tất cả dữ liệu liên quan cho bốn trò chơi bài này. Cơ sở dữ liệu có thể được ứng dụng web sử dụng làm nơi lưu trữ tất cả các dữ liệu có liên quan. Chúng tôi muốn lưu trữ cài đặt trò chơi ban đầu, người tham gia trò chơi, các hành động được thực hiện trong khi chơi và kết quả của một giao dịch, ván bài hoặc trò lừa. Chúng ta cũng phải lưu ý rằng một trận đấu có thể có một hoặc nhiều giao dịch được liên kết với nó.
Từ những gì chúng tôi lưu trữ trong cơ sở dữ liệu của mình, chúng tôi sẽ có thể tạo lại tất cả các hành động đã diễn ra trong trò chơi. Chúng tôi sẽ sử dụng các trường văn bản để mô tả điều kiện chiến thắng, hành động trong trò chơi và kết quả của chúng. Chúng dành riêng cho từng trò chơi và logic ứng dụng web sẽ diễn giải văn bản và chuyển đổi chúng khi cần thiết.
Giới thiệu nhanh về Mô hình
Mô hình này cho phép chúng tôi lưu trữ tất cả dữ liệu trò chơi có liên quan, bao gồm:
- Thuộc tính trò chơi
- Danh sách các trò chơi và trận đấu
- Người tham gia
- Hành động trong trò chơi
Vì các trò chơi khác nhau theo nhiều cách, chúng tôi sẽ thường xuyên sử dụng varchar (256) kiểu dữ liệu để mô tả thuộc tính, di chuyển và kết quả.
Người chơi, Trận đấu và Người tham gia
Phần này của mô hình bao gồm ba bảng và được sử dụng để lưu trữ dữ liệu về những người chơi đã đăng ký, các trận đấu đã chơi và những người chơi đã tham gia.
player
bảng lưu trữ dữ liệu về người chơi đã đăng ký. Tên người dùng username
và email
các thuộc tính là các giá trị duy nhất. nick_name
thuộc tính lưu trữ tên màn hình của người chơi.
match
bảng chứa tất cả dữ liệu đối sánh có liên quan. Nói chung, một trận đấu bao gồm một hoặc nhiều giao dịch thẻ (còn được gọi là vòng, ván bài hoặc thủ thuật). Tất cả các trận đấu đều có quy định trước khi trận đấu bắt đầu. Các thuộc tính như sau:
-
game_id
- tham chiếu bảng chứa danh sách trò chơi (poker, blackjack, belot và préférence, trong trường hợp này). -
start_time
vàend_time
là thời gian thực tế khi trận đấu bắt đầu và kết thúc. Lưu ý rằngend_time
có thể là NULL; chúng tôi sẽ không có giá trị của nó cho đến khi trò chơi kết thúc. Ngoài ra, nếu trận đấu bị hủy trước khi kết thúc,end_time
giá trị có thể vẫn là NULL. -
number_of_players
- là số người tham gia cần thiết để bắt đầu trò chơi -
deck_id
- tham chiếu bộ bài được sử dụng trong trò chơi. -
decks_used
- là số bộ bài được sử dụng để chơi trò chơi. Thông thường giá trị này sẽ là 1, nhưng một số trò chơi sử dụng nhiều bộ bài. -
unit_id
- là đơn vị (điểm, chip, tiền, v.v.) được sử dụng để ghi điểm trò chơi. -
entrance_fee
- là số đơn vị cần thiết để tham gia trò chơi; giá trị này có thể là KHÔNG ĐỦ nếu trò chơi không yêu cầu mỗi người chơi phải bắt đầu với một số đơn vị nhất định. -
victory_conditions
- xác định người chơi thắng trận đấu. Chúng tôi sẽ sử dụng varchar loại dữ liệu để mô tả điều kiện chiến thắng của mỗi trò chơi (tức là đội đầu tiên đạt 100 điểm) và để ứng dụng giải thích điều đó. Sự linh hoạt này giúp bạn có thêm nhiều trò chơi. -
match_result
- lưu trữ kết quả của trận đấu ở định dạng văn bản. Như vớivictory_conditions
, chúng tôi sẽ để ứng dụng diễn giải giá trị. Thuộc tính này có thể là NULL vì chúng tôi sẽ điền giá trị đó vào cùng lúc chúng tôi chènend_time
giá trị.
participant
bảng lưu trữ dữ liệu về tất cả những người tham gia trong một trận đấu. match_id
và player_id
thuộc tính là tham chiếu đến match
và player
những cái bàn. Cùng với nhau, các giá trị này tạo thành khóa thay thế của bảng.
Hầu hết các trò chơi xoay vòng mà người chơi đặt giá thầu hoặc chơi trước. Thông thường trong vòng đầu tiên, người chơi trước (người chơi mở đầu) được xác định bởi luật chơi. Trong vòng tiếp theo, người chơi ở bên trái (hoặc đôi khi ở bên phải) của người chơi mở đầu ban đầu sẽ đi trước. Chúng tôi sẽ sử dụng initial_player_order
thuộc tính để lưu trữ số thứ tự của người chơi mở đầu của vòng đầu tiên. match_id
và initial_player_order
các thuộc tính tạo thành một khóa thay thế khác vì hai người chơi không thể chơi cùng một lúc.
Điểm username
thuộc tính được cập nhật khi người chơi kết thúc trận đấu. Đôi khi điều này xảy ra vào cùng một thời điểm cho tất cả người chơi (ví dụ:belot hoặc préférence) và đôi khi trong khi trận đấu vẫn đang diễn ra (ví dụ:poker hoặc blackjack).
Hành động và Loại hành động
Khi chúng tôi nghĩ về các hành động mà người chơi có thể thực hiện trong một trò chơi bài, chúng tôi nhận ra rằng chúng tôi phải lưu trữ:
- Hành động là gì
- Ai đã thực hiện hành động đó
- Khi nào (trong giao dịch) hành động diễn ra
- (Các) thẻ nào đã được sử dụng trong hành động đó
action_type
bảng là một từ điển đơn giản chứa tên của các hành động của người chơi. Một số giá trị có thể có bao gồm thẻ rút, thẻ chơi, chuyển thẻ cho người chơi khác, kiểm tra và nâng cao.
Trong action
bảng, chúng tôi sẽ lưu trữ tất cả các sự kiện đã xảy ra trong một giao dịch. deal_id
, card_id
, participant_id
và action_type_id
là các tham chiếu đến các bảng có chứa các giá trị thỏa thuận, người tham gia thẻ và action_type. Lưu ý rằng participant_id
và card_id
có thể là giá trị NULL. Điều này là do một số hành động không được thực hiện bởi người chơi (ví dụ:người chia bài rút một quân bài và đặt nó ngửa lên), trong khi một số hành động không bao gồm các quân bài (ví dụ:tăng trong poker). Chúng tôi cần lưu trữ tất cả các thao tác này để có thể tạo lại toàn bộ trận đấu.
action_order
thuộc tính lưu trữ số thứ tự của một hành động trong trò chơi. Ví dụ:một giá thầu mở sẽ nhận được giá trị 1; giá thầu tiếp theo sẽ có giá trị 2, v.v. Không được có nhiều hành động xảy ra cùng một lúc. Do đó, deal_id
và action_order
các thuộc tính cùng nhau tạo thành khóa thay thế.
action_notation
thuộc tính chứa mô tả chi tiết của một hành động. Ví dụ:trong poker, chúng ta có thể lưu trữ một khoản tiền tăng hành động và một số tiền tùy ý. Một số hành động có thể phức tạp hơn, vì vậy, khôn ngoan hơn nếu lưu trữ các giá trị này dưới dạng văn bản và để ứng dụng diễn giải.
Giao dịch và Thứ tự giao dịch
Một trận đấu bao gồm một hoặc nhiều giao dịch thẻ. Chúng ta đã thảo luận về participant
và match
nhưng chúng tôi đã đưa chúng vào hình ảnh để hiển thị mối quan hệ của chúng với deal
và deal_order
bảng.
deal
bảng lưu trữ tất cả dữ liệu chúng ta cần về một phiên bản đối sánh duy nhất.
match_id
thuộc tính liên quan trường hợp đó với kết quả phù hợp, trong khi start_time
và end_time
biểu thị thời gian chính xác khi phiên bản đó bắt đầu và khi nó kết thúc.
move_time_limit
và deal_result
thuộc tính là cả trường văn bản được sử dụng để lưu trữ giới hạn thời gian (nếu có) và mô tả kết quả của giao dịch đó.
Trong participant
bảng, initial_player_order
thuộc tính lưu trữ thứ tự người chơi cho trường hợp trận đấu mở đầu. Việc lưu trữ đơn đặt hàng cho các lượt tiếp theo yêu cầu một bảng hoàn toàn mới - deal_order
bảng.
Rõ ràng, deal_id
và participant_id
là các tham chiếu đến một phiên bản đối sánh và một người tham gia. Cùng nhau, chúng tạo thành khóa thay thế đầu tiên trong deal_order
bàn. player_order
thuộc tính chứa các giá trị biểu thị đơn đặt hàng mà người chơi đã tham gia trong trường hợp đối sánh đó. Cùng với deal_id
, nó tạo thành khóa thay thế thứ hai trong bảng này. deal_result
thuộc tính là một trường văn bản mô tả kết quả của trận đấu cho từng người chơi. Điểm username
thuộc tính lưu trữ một giá trị số liên quan đến kết quả giao dịch.
Bộ quần áo, Xếp hạng và Thẻ
Phần này của mô hình mô tả các thẻ chúng tôi sẽ sử dụng trong tất cả các trò chơi được hỗ trợ. Mỗi thẻ có một bộ đồ và cấp bậc.
suit_type
table là một từ điển chứa tất cả các loại suit mà chúng tôi sẽ sử dụng. Đối với suit_type_name
, chúng tôi sẽ sử dụng các giá trị như “bộ quần áo kiểu Pháp”, “bộ quần áo của người Đức”, “bộ quần áo của người Đức-Thụy Sĩ” và “bộ quần áo của người Latinh”.
suit
bảng chứa tên của tất cả các bộ đồ có trong các loại bộ bài cụ thể. Ví dụ:bộ bài Pháp có các bộ đồ được gọi là “Spades”, “Hearts”, “Diamonds” và “Clubs”.
Trong rank
từ điển, chúng tôi sẽ tìm thấy các giá trị thẻ nổi tiếng như “Ace”, “King”, “Queen” và “Jack”.
card
bảng chứa danh sách mọi thẻ có thể. Mỗi thẻ sẽ chỉ xuất hiện trong bảng này một lần. Đó là lý do mà suit_id
và rank_id
các thuộc tính tạo thành khóa thay thế của bảng này. Giá trị của cả hai thuộc tính có thể là KHÔNG ĐỦ vì một số thẻ không có phù hợp hoặc cấp bậc (ví dụ:thẻ joker). is_joker_card
là một giá trị Boolean tự giải thích. card_name
thuộc tính mô tả một lá bài bằng văn bản:“Ace of Spades”.
Thẻ và Bộ bài
Các thẻ thuộc bộ bài. Bởi vì một thẻ có thể xuất hiện trong nhiều bộ bài, chúng tôi sẽ cần một n:n mối quan hệ giữa card
và deck
bảng.
Trong deck
bảng, chúng tôi sẽ lưu trữ tên của tất cả các bộ bài mà chúng tôi muốn sử dụng. Ví dụ về các giá trị được lưu trữ trong deck_name
thuộc tính:“Bộ bài tiêu chuẩn 52 lá (Pháp)” hoặc “Bộ bài 32 lá (Đức)”.
card_in_deck
quan hệ được sử dụng để gán thẻ cho các bộ bài thích hợp. card_id
- deck_id
cặp là khóa thay thế của deck
bàn.
Đối sánh thuộc tính, bộ bài và đơn vị được sử dụng
Phần này của mô hình chứa một số thông số cơ bản để bắt đầu một trò chơi mới.
Phần chính của phần này là game
bàn. Bảng này lưu trữ dữ liệu về các trò chơi được ứng dụng hỗ trợ. game_name
thuộc tính chứa các giá trị như “poker”, “blackjack”, “belot” và “préférence”.
min_number_of_players
và max_number_of_players
là số lượng người tham gia tối thiểu và tối đa trong một trận đấu. Các thuộc tính này đóng vai trò là ranh giới cho trò chơi và chúng được hiển thị trên màn hình khi bắt đầu trận đấu. Người bắt đầu trận đấu phải chọn một giá trị từ phạm vi này.
min_entrance_fee
và max_entrance_fee
thuộc tính biểu thị phạm vi phí vào cửa. Một lần nữa, điều này dựa trên trò chơi đang được chơi.
Trong possible_victory_condition
, chúng tôi sẽ lưu trữ tất cả các điều kiện chiến thắng có thể được chỉ định cho một trận đấu. Các giá trị được phân tách bằng dấu phân cách.
unit
từ điển được sử dụng để lưu trữ mọi đơn vị được sử dụng trong tất cả các trò chơi của chúng tôi. unit_name
thuộc tính sẽ chứa các giá trị như “point”, “dollar”, “euro” và “chip”.
game_deck
và game_unit
các bảng sử dụng cùng một logic. Chúng chứa danh sách tất cả các bộ bài và đơn vị có thể được sử dụng trong một trận đấu. Do đó, game_id
- deck_id
ghép nối và game_id
- unit_id
ghép các khóa thay thế hình thành trong các bảng tương ứng của chúng.
Điểm
Trong ứng dụng của mình, chúng tôi muốn lưu trữ điểm số của tất cả những người chơi đã tham gia vào các trò chơi bài của chúng tôi. Đối với mỗi trò chơi, một giá trị số duy nhất được tính toán và lưu trữ. (Tính toán dựa trên kết quả của người chơi trong tất cả các trò chơi của một loại duy nhất.) Điểm của người chơi này tương tự như xếp hạng; nó cho phép người dùng biết đại khái một người chơi giỏi như thế nào.
Quay lại quá trình tính toán. Chúng tôi sẽ tạo một n:n mối quan hệ giữa player
và game
những cái bàn. Đó là player_score
bảng trong mô hình của chúng tôi. player_id
và score_id
”Cùng nhau tạo thành khóa thay thế của bảng. “score
thuộc tính được sử dụng để lưu trữ giá trị số được đề cập trước đó.
Có rất nhiều trò chơi bài sử dụng các quy tắc, quân bài và bộ bài rất khác nhau. Để tạo cơ sở dữ liệu lưu trữ dữ liệu cho nhiều trò chơi bài, chúng ta cần thực hiện một số khái quát. Một cách chúng tôi làm điều này là sử dụng các trường văn bản mô tả và để ứng dụng xen kẽ chúng. Chúng tôi có thể nghĩ ra các cách để giải quyết hầu hết các tình huống phổ biến, nhưng điều đó sẽ làm phức tạp thêm thiết kế cơ sở dữ liệu theo cấp số nhân.
Như bài viết này đã trình bày, bạn có thể sử dụng một cơ sở dữ liệu cho nhiều trò chơi. Tại sao bạn sẽ làm điều này? Ba lý do:1) bạn có thể sử dụng lại cùng một cơ sở dữ liệu; 2) nó sẽ đơn giản hóa phân tích; và điều này sẽ dẫn đến 3) việc xây dựng các đối thủ AI tốt hơn.