0

Làm board game bằng AI: vì sao mỗi luật mới lại phá vỡ cấu trúc, và tôi đã tách Game với Referee thế nào?

Với tôi, tạo một trò chơi có bộ quy tắc cố định khác với tạo một trò chơi mà các quy tắc liên tục được thêm vào. Ban đầu, có vẻ chỉ cần bảo AI tạo một trò chơi là đủ, nhưng nếu mỗi lần thêm một quy tắc mới lại phải sửa lại toàn bộ cấu trúc thì mọi chuyện sẽ khác.

Những luật không có trò chơi hoàn chỉnh để tham khảo

INSIGNIA, trò chơi được nhắc đến trong video, được xây dựng dựa trên Stratego. Tôi đã thêm vào đó những luật không có trong trò chơi gốc.

Tên lửa bay theo đường thẳng đúng 2 hoặc 3 ô. Công binh biến chướng ngại vật bên cạnh thành đường đi rồi tiến lên. Đặc công chỉ được một lần nhảy đến vị trí cách 3 ô để tấn công; nếu có lính bắn tỉa ở gần điểm đáp, đặc công sẽ bị bắn hạ.

Với những luật như vậy, AI không có trò chơi hoàn chỉnh nào để tham khảo. Nhờ AI làm một trò chơi ngay từ đầu là một việc, còn liên tục bổ sung luật và thẻ mới như đặc công hay lính bắn tỉa lại là việc khác. Mỗi lần thêm luật, tôi lại phải sửa lại cấu trúc hiện có. Không phải AI làm sai, mà là cấu trúc cũ khiến việc triển khai luật mới trở nên khó khăn.

Tách luồng trò chơi khỏi luật chơi

Vì vậy, tôi chia các thành phần trong trò chơi thành ô, quân cờ, người chơi, thẻ và trọng tài. Game chỉ phụ trách luồng trò chơi, còn Referee phụ trách luật.

Game nhận một nước đi rồi chuyển cho trọng tài, sau đó hỏi trọng tài xem trò chơi đã kết thúc chưa. Tôi không đặt luật chơi bên trong Game.

Referee là lớp trừu tượng. Lớp này có legalActions để cho biết những nước đi hiện có thể thực hiện, và resolve để xử lý một nước đi rồi xác định lượt tiếp theo. Tôi tạo một lớp trọng tài riêng cho từng trò chơi.

Tôi cũng tách ô và quân cờ để chúng không cần biết luật của trò chơi. Ô không biết quân nào đang đứng trên đó.

Tạo chỗ cho từng loại luật

Khi chia cấu trúc như vậy, tôi có thể dành riêng chỗ để thêm luật.

Cấp bậc ẩn được biểu diễn bằng cách tách perceived của quân cờ — tức danh tính mà đối thủ nhìn thấy — khỏi cấp bậc thật. Tên lửa được xử lý bằng tầm bắn của quân cờ. Khả năng tạo đường đi của công binh là luật thay đổi việc một ô có thể đi qua được hay không trong lúc chơi. Khả năng nhảy của đặc công được đặt thành một năng lực chỉ dùng được một lần.

Việc thêm luật mới chuyển từ sửa lại cấu trúc sang tạo và bổ sung một chức năng. Thay vì gắn thẳng yêu cầu vào một chức năng, lập trình hướng đối tượng tổ chức các đối tượng phù hợp với yêu cầu, rồi để chúng tương tác với nhau nhằm tạo ra kết quả.

Hãy nghĩ xem trong nhà hàng có thường xuyên xảy ra chuyện khách gọi món rồi lại yêu cầu đổi sang món khác khi đầu bếp đang nấu hay không. Điều đó giúp ta thấy một đặc điểm của phần mềm: yêu cầu thường xuyên thay đổi. Lập trình hướng đối tượng là một trong những cách thiết kế phát triển để giúp phần mềm thích ứng với những thay đổi đó.

Engine dùng chung và luật riêng cho từng trò chơi

Tôi đã thử đưa nhiều trò chơi lên cùng một engine. Số dòng code như sau:

  • Engine dùng chung: khoảng 400 dòng
  • Trò chơi thang (Ladder): 80 dòng
  • Yut Nori (trò chơi dân gian Hàn Quốc): 127 dòng
  • Blue Marble (trò chơi kiểu Cờ tỷ phú của Hàn Quốc): 167 dòng
  • Dominion: 171 dòng
  • Janggi (cờ tướng Hàn Quốc): 257 dòng
  • INSIGNIA: 393 dòng

Thay vì làm lại trò chơi từ đầu, tôi kế thừa lớp trọng tài rồi bổ sung luật. Từ trò chơi thứ hai trở đi, tôi không sửa một dòng nào trong engine. Thời gian dành cho việc xây dựng lớp trừu tượng và làm sáu trò chơi còn ngắn hơn thời gian làm riêng INSIGNIA.

Tôi không khái quát hóa mọi thứ ngay từ đầu. Tôi chỉ đưa phần chung vào engine khi trò chơi thứ hai cần đến. Ngã rẽ của Yut Nori và lối tắt một chiều của trò chơi thang nối các ô theo những cách khác nhau. Với trò chơi bài không có bàn cờ như Dominion, cũng có thể không dùng ô mà chỉ vận hành dựa trên luồng trò chơi.

Trong video, tôi sẽ xem cách chia cấu trúc trò chơi khi luật liên tục được bổ sung, cũng như kết quả khi đưa nhiều trò chơi lên một engine dùng chung.

Nếu yêu cầu cứ tiếp tục thay đổi

Nếu bạn chỉ định làm xong một trò chơi có luật đã được xác định, như Janggi hay Gomoku, thì có thể không cần cấu trúc như vậy. Chỉ cần nhờ AI làm có thể đã đủ.

Nhưng nếu dự định liên tục bổ sung luật hoặc chức năng, thay vì áp dụng lập trình hướng đối tượng cho mọi thứ ngay từ đầu, bạn có thể cân nhắc áp dụng cấu trúc này cho những mô-đun thường xuyên thay đổi và hay phát sinh lỗi. Lúc này, điều quan trọng có thể không còn là thời gian viết code, mà là cần sửa ít đến mức nào khi có yêu cầu mới.

Trong chương trình thường xuyên nhận yêu cầu mới, bạn nghĩ nên tách phần nào trước?


Bài viết này tóm tắt video tiếng Hàn của tôi, được dịch sang tiếng Việt với sự hỗ trợ của AI và đã được tôi xem lại.

Video gốc (tiếng Hàn, có phụ đề tiếng Việt): https://youtu.be/4brdEOdyRAM
Video trước: https://youtu.be/B_EVfrK-ptk


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí