Nộp bot trong 4 bước
- Tải một mã mẫu. Chọn Python nếu bạn học Python, hoặc C++ nếu bạn học C++. Chỉ cần chọn một ngôn ngữ.
- Đăng nhập. Dùng tài khoản do ban tổ chức cấp. Lần đầu đăng nhập, hãy đặt mật khẩu riêng theo hướng dẫn trên màn hình.
- Nộp nguyên file vừa tải. Trong Bot của tôi, chọn file
main.pyhoặcmain.cpprồi bấm Nộp bot. Ô “File chính trong ZIP” để trống. - Chờ kết quả. Khi thấy Sẵn sàng thi đấu, mở Chi tiết & trận thử để xem bot chơi. Trang tự cập nhật, bạn không cần nộp lại.
Lần đầu chỉ cần chạy được. Mã mẫu đã làm sẵn phần nhận và gửi dữ liệu. Nó chọn một nước đi đúng luật nhưng chưa có chiến thuật hay. Sau khi nộp thử thành công, bạn hãy sửa cách chọn nước.
Mỗi lần nộp có thay thế bài cũ không?
Không. Mỗi lần nộp tạo một bản mới: v1 là bản đầu, v2 là bản thứ hai… Bản mới nhất vượt qua kiểm tra sẽ được chọn để thi đấu. Nếu bản mới bị lỗi, bản đang chọn trước đó vẫn được giữ.
Bạn có thể bấm “Chọn bản này thi đấu” để dùng lại bản cũ. Vòng đấu đã bắt đầu vẫn giữ bản được chọn lúc tạo vòng.
Luật cơ bản và luật của từng trận
Luật Classic mặc định: bàn cờ có 9 hàng, 9 cột. Mỗi bên có 10 quân: hai loại có 3 quân, loại còn lại có 4 quân. Loại có 4 quân được chọn ngẫu nhiên; cả hai bên có cùng số quân từng loại.
- Di chuyển trong Classic: mỗi lượt đi một quân sang ô bên cạnh, theo chiều ngang, dọc hoặc chéo.
- Ăn quân khi chưa nâng cấp: chỉ vào ô trống hoặc ô có quân đối thủ mà quân của bạn ăn được. Ví dụ: Búa được vào ô có Kéo, nhưng không được vào ô có Bao.
- Thắng: đưa quân vào nhà đối thủ (ô ★), ăn hết quân đối thủ, hoặc làm đối thủ không còn nước đi đúng luật.
- Hòa trong Classic: sau 500 nước của cả hai bên, hoặc cùng bàn cờ và bên đến lượt lặp lại 3 lần. Nếu có bên thắng ở nước đó thì vẫn tính thắng.
Khi dùng luật tùy chỉnh: kích thước, hình dạng bản đồ, số quân và hướng đi có thể thay đổi. Ô chặn và ô ngoài bản đồ nằm trong rules.blockedCells, rules.voidCells; chúng vẫn có thể là null trong bàn cờ nhưng không được đi vào. state.powerUps chứa vị trí vật phẩm còn lại. Quân nhặt được có powered: true, ăn thêm được quân cùng loại và vẫn bị loại khắc chế ăn. Luật cũ có thể không có các trường power-up.
Cách đọc vị trí quân trong code
Vị trí được ghi bằng (hàng, cột), bắt đầu từ 0. Hàng tăng từ trên xuống, cột tăng từ trái sang phải. Góc trên trái là (0, 0); góc dưới phải của bàn Classic 9 × 9 là (8, 8).
Trong dữ liệu: R là Búa, P là Bao, S là Kéo. Một ô trống có giá trị null. Hai bên được đánh số 0 và 1; bên 0 đi trước. Tọa độ không đảo theo bên chơi.
Đây là luật Classic hiện tại. Nếu ban tổ chức đổi luật, hãy đọc luật của trận thay vì luôn mặc định bàn 9 × 9.
Bạn cần sửa chỗ nào?
Mở file mẫu bằng trình soạn thảo bạn đang dùng. Tìm dòng “THAY CHIẾN THUẬT Ở ĐÂY”. Bạn chỉ cần thay cách chọn nước đi ở phần đó.
| Tên trong code | Hiểu đơn giản |
|---|---|
legal_moves | Danh sách những nước được phép đi. Chọn trong danh sách này sẽ tránh đi sai luật. |
state.board | Bàn cờ hiện tại. Dùng để biết quân nào đang ở ô nào. |
player_index | Bên của bạn, là 0 hoặc 1. Dùng để phân biệt quân mình và quân đối thủ. |
rules.homes | Vị trí nhà hai bên. Nhà đối thủ có chỉ số 1 - player_index. |
state.powerUps | Các ô còn vật phẩm nâng cấp, ghi bằng cặp [hàng, cột]. Nếu thiếu trường này thì xem như không có vật phẩm. |
powered | Nằm trong thông tin một quân. Giá trị true nghĩa là quân đã nâng cấp; thiếu trường này thì là quân thường. |
rules.movementByType | Kiểu đi riêng của Búa/Bao/Kéo. Nếu chỉ chọn trong legal_moves, bạn chưa cần tự đọc hay tính lại các hướng này. |
Mẫu mới chấm điểm từng nước: ưu tiên chiếm nhà đối thủ, ăn quân, nhặt power-up nếu quân chưa được nâng cấp, rồi tiến gần nhà đối thủ.
move = max(request['legal_moves'], key=score)
Hàm score trong file mẫu quyết định nước nào được ưu tiên. Bạn có thể sửa các điểm 40 (ăn quân), 15 (nhặt power-up) hoặc cách tính khoảng cách. C++ cũng có hàm score và vòng lặp chọn nước có điểm cao nhất.
Luôn chọn từ legal_moves: danh sách này đã xét các kiểu đi Xe, Tượng, Mã, hướng tự chỉnh và power-up. Đừng tự giới hạn mỗi lượt chỉ được đi một ô. Gợi ý tiến gần nhà trong mẫu chưa tự tìm đường vòng qua chướng ngại.
Đọc power-up trong code như thế nào?
Ví dụ dưới đây là các biến có thể thêm vào chiến thuật. Quân có dấu + trên bàn cờ chính là quân có powered = true. Quân nâng cấp giữ hiệu lực đến khi bị ăn; không cộng dồn khi nhặt thêm vật phẩm.
power_ups = request['state'].get('powerUps', [])
row, col = move['toRow'], move['toCol']
piece = board[move['fromRow']][move['fromCol']]
is_powered = piece.get('powered', False)
will_pick_up = [row, col] in power_ups
const auto powerUps = request.at("state").value("powerUps", json::array());
const auto& piece = board.at(move.at("fromRow").get<int>())
.at(move.at("fromCol").get<int>());
bool isPowered = piece.value("powered", false);
Đọc các biến này bên trong hàm score, nơi đã có biến move và board. Mẫu mới đã đọc sẵn; không cần dán lặp lại. Chỉ số hàng bắt đầu từ 0, tăng xuống dưới; cột tăng sang phải, không đảo tọa độ khi đổi bên.
Kiểu đi mới cần xử lý ra sao?
Quân hai bên đều chặn đường nhau: không được đi xuyên hay nhảy qua quân phía trước, kể cả khi có power-up. Chỉ được ăn quân đối thủ ở ô đích nếu đúng luật. Mã đi chữ L bị cản ở ô sát quân theo chiều dài chữ L. Hướng tự chỉnh dài phải có các ô trung gian trống; ô chặn và ô ngoài bản đồ cũng cản đường.
Ban tổ chức có thể cho từng loại quân đi như Vua, Xe, Tượng, Hậu, Mã, Tốt hoặc hướng tự đặt. Đây là cách di chuyển; luật ăn quân vẫn là Búa–Bao–Kéo và power-up. Không có chiếu vua, nhập thành hay phong cấp. Với kiểu Tốt, bên 0 tiến xuống, bên 1 tiến lên; đi thẳng một ô trống và ăn chéo một ô.
Cách dễ nhất: cứ chấm điểm các nước trong legal_moves. Danh sách này đã biết quân nào được đi xa, bị chặn đường hay ăn quân cùng loại. Một ô null chưa chắc đi được vì có thể là ô chặn hoặc ngoài bản đồ.
Muốn biết kích thước thật: Python dùng len(board), len(board[0]); C++ dùng board.size(), board.at(0).size(). Muốn đếm quân còn lại, đọc bàn cờ hiện tại thay vì cố định 10 quân.
Thử lại sau khi sửa: nộp phiên bản mới, xem hai ván kiểm tra rồi nhờ ban tổ chức chạy thêm Đấu thử với luật tùy chỉnh. Hai ván kiểm tra bài nộp và vòng điểm hiện dùng Classic; chúng chưa bảo đảm chiến thuật tốt trên mọi bản đồ. Bản đã nộp không tự đổi khi mã mẫu được cập nhật.
Bot được chạy lại từ đầu ở mỗi lượt. Biến bạn tạo ở lượt trước không tự được giữ lại. Khi mới bắt đầu, cứ chọn nước dựa trên bàn cờ hiện tại; chưa cần làm phần ghi nhớ.
Thấy thông báo này thì làm gì?
| Thông báo | Bạn cần làm gì? |
|---|---|
| Đang chờ / Đang kiểm tra / Đang đấu thử | Chờ trang tự cập nhật. Chưa cần nộp thêm. |
| Sẵn sàng thi đấu | Bot đã vượt qua hai ván kiểm tra. Bạn có thể xem lại trận hoặc cải thiện chiến thuật. Không bắt buộc thắng bot mẫu. |
| Cần sửa bài | Mở “Chi tiết & trận thử”, đọc gợi ý, sửa file rồi nộp lại. |
| Máy chủ gặp sự cố | Không cần sửa code vì thông báo này. Chờ hệ thống thử lại; nếu bị tạm dừng, báo ban tổ chức. |
Các lỗi thường gặp khi viết bot
- Viết sai cú pháp: kiểm tra dấu ngoặc, dấu nháy, dấu chấm phẩy và thụt dòng. Mở “Chi tiết kỹ thuật” để xem lỗi có nhắc đến file và dòng nào không.
- Chạy bị dừng: kiểm tra bạn có đọc phần tử ngoài danh sách, chia cho 0 hoặc gặp lỗi khi xử lý dữ liệu không.
- Tính quá lâu: kiểm tra vòng lặp có dừng không; thử cách chọn nước đơn giản hơn.
- Trả lời sai: giữ nguyên phần gửi dữ liệu trong mẫu. Đừng thêm lời chào hay thông báo bằng
printhoặccoutvào phần đó. - Đi sai luật: chọn một nước từ
legal_moves.
Những phần chưa cần học ngay
JSON, đầu vào và đầu ra là gì?
JSON là cách ghi dữ liệu theo tên và giá trị. Ví dụ {"player_index": 0} cho biết bên chơi là 0. Mẫu đã đọc dữ liệu này thành biến request để bạn dùng.
Đầu vào (stdin) là nơi chương trình nhận dữ liệu từ hệ thống, thay cho việc bạn gõ bàn phím. Đầu ra (stdout) là nơi gửi câu trả lời bằng print trong Python hoặc cout trong C++. Hệ thống nhận đúng một dòng JSON cho mỗi lượt.
Nhật ký (stderr) là chỗ viết thông tin để tự kiểm tra code mà không làm hỏng câu trả lời. Python dùng print("đang kiểm tra", file=sys.stderr); C++ dùng std::cerr << "đang kiểm tra";.
Chương trình gửi nước đi như thế nào?
Mẫu đã gửi sẵn request_id (mã của lượt hiện tại) và move (nước bạn chọn). Giữ đúng mã lượt để hệ thống biết bạn đang trả lời lượt nào.
Ví dụ dưới đây chuyển quân từ hàng 0, cột 6 sang hàng 0, cột 7. Chỉ được dùng nếu nước này có trong danh sách nước được phép của lượt đó:
{"request_id":"turn-0","move":{"fromRow":0,"fromCol":6,"toRow":0,"toCol":7},"userdata":null}fromRow, fromCol là hàng, cột xuất phát; toRow, toCol là hàng, cột đích.
Bot có thể nhớ thông tin giữa các lượt không?
Có. Trường userdata là dữ liệu ghi nhớ do bạn gửi lại. Hệ thống trả dữ liệu đó cho chính bot của bạn ở lượt tiếp theo. Dữ liệu này tối đa 16 KiB; có thể để null (Python dùng None) nếu chưa cần.
Nộp nhiều file và giới hạn dung lượng
Nếu chỉ có một file, nộp trực tiếp .py hoặc .cpp, tối đa 1 MiB (khoảng 1 MB). Nếu có nhiều file, nén thành ZIP: tối đa 5 MiB (khoảng 5 MB), 64 file mã nguồn cùng ngôn ngữ. Không đặt mật khẩu cho ZIP, không nộp file .exe hay thư viện cài thêm.
Nếu trong ZIP có main.py hoặc main.cpp rõ ràng, có thể để trống ô “File chính trong ZIP”. Nếu file chính có tên khác, nhập đường dẫn như src/play.py. C++ chỉ nên có một hàm main.
Giới hạn chạy và thư viện được dùng
Máy chủ tự chuẩn bị và chạy bot. Bạn không cần cài Docker để nộp bài trên web của ban tổ chức.
- Python 3.11, dùng các thư viện chuẩn được cho phép. C++17, có sẵn thư viện đọc JSON
nlohmann/json.hpp. - Mỗi nước có tối đa 0,5 giây tính toán; mỗi bot có tổng cộng 30 giây tính toán cho một trận. Bot cần phản hồi trong 3 giây mỗi lượt.
- Bộ nhớ được giới hạn ở 256 MiB không gian địa chỉ của chương trình. Tổng câu trả lời và nhật ký mỗi nước tối đa 64 KiB.
- Bot không được mở mạng, đọc file ngoài phạm vi cho phép hoặc tự chạy thêm chương trình.
“CPU” là thời gian chương trình thực sự tính toán. “Biên dịch” là bước chuyển code C++ thành chương trình chạy được; với Python, hệ thống kiểm tra cách viết code trước khi đấu thử.
Điểm xếp hạng và luật của từng trận
Elo là điểm xếp hạng, bắt đầu từ 1500 ở mỗi vòng. Điểm thay đổi theo kết quả và đối thủ, nên không chỉ phụ thuộc số trận thắng.
Seed là giá trị hệ thống dùng để chọn bố trí ban đầu của trận. Phiên bản luật cho biết bộ quy tắc nào được dùng. Khi mới viết bot, bạn chưa cần tự chọn hai giá trị này; hãy dùng luật và bàn cờ hệ thống gửi đến.
Khi cần tự viết phần nhận/gửi dữ liệu hoặc tra cứu đầy đủ: đọc đặc tả kỹ thuật.