Vận hành và giao tiếp của engine
Giao diện phụ trách bàn cờ, nút bấm và đồng hồ; engine (động cơ cờ) nhận thế cờ, tính toán rồi trả kết quả. Hai bên trao đổi bằng từng dòng lệnh văn bản. Bấm “Phân tích” không chỉ là gọi một hàm tính cờ, mà còn gồm đặt thế cờ, khởi động luồng, xuất thông tin liên tục và thông báo khi kết thúc.
Bài viết được đối chiếu với bản official Pikafish cố định tại commit 1c66b9b. Phần này giải thích cách các bộ phận phối hợp. Cách thiết lập tham số nằm ở Tùy chọn UCI; phương pháp tính cờ nằm ở Thuật toán tìm kiếm.
Từ khởi động đến trả về nước đi
main.cpp khởi tạo bảng tấn công và dữ liệu cần cho thế cờ trước, rồi tạo UCIEngine và vào vòng lặp nhận lệnh. uci.cpp phụ trách đọc và ghi giao thức; Engine quản lý chung thế cờ, tùy chọn, luồng, bảng chuyển vị và mạng, còn công việc tìm kiếm thực tế được giao cho các luồng làm việc.
| Lệnh hoặc đầu ra | Vai trò trong lượt tính này |
|---|---|
uci → uciok | Báo tên engine và các tùy chọn được hỗ trợ, hoàn tất bước bắt tay. |
setoption | Thay đổi tùy chọn; một số thiết lập còn phân bổ lại luồng hoặc bộ nhớ. |
position ... | Đặt thế cờ bắt đầu, rồi thực hiện lần lượt các nước sau moves để tới vị trí cần tính. |
go ... | Đọc giới hạn thời gian, độ sâu hoặc số nút; kiểm tra mạng, chuẩn bị thế cờ gốc và bắt đầu tìm kiếm. |
info ... | Liên tục báo độ sâu, điểm số, số nút và biến chính trong quá trình tìm kiếm. |
stop | Yêu cầu kết thúc lượt tìm kiếm này; các luồng làm việc hoàn tất phần còn lại rồi trả kết quả. |
bestmove ... | Thông báo nước đi cuối cùng của lượt tìm kiếm, không có nghĩa giao diện đã đi quân. |
Sau khi bắt đầu tìm kiếm, vòng lặp lệnh vẫn có thể nhận stop. stop chỉ đặt cờ dừng, không cưỡng bức hủy luồng; luồng tìm kiếm chính chờ các luồng làm việc khác kết thúc rồi xuất bestmove qua hàm gọi lại. quit yêu cầu dừng và thoát chương trình.
Sau bestmove có thể kèm ponder, cho biết đáp trả dự kiến của đối thủ. Suy nghĩ nền thực sự được giao diện khởi động bằng go ponder; nếu đối thủ đúng là đi nước dự kiến, ponderhit chuyển sang suy nghĩ thông thường. Chỉ thấy một đáp trả dự kiến không có nghĩa engine đã tự bắt đầu tìm kiếm nền.
Mã nguồn: Điểm khởi động, Phân phối lệnh, Bắt đầu và kết thúc tìm kiếm.
Thế cờ, lịch sử và chờ sẵn sàng: không thể chỉ nhìn bàn cờ
Mỗi lệnh position đều dựng lại trạng thái thế cờ. Các nước đi kèm trong moves được kiểm tra rồi thực hiện lần lượt, để lại chuỗi lịch sử. Chỉ gửi FEN cuối cùng không khôi phục được ván cờ đã đi tới đó thế nào, nên có thể ảnh hưởng tới phán định lặp lại, chiếu liên tục và đuổi quân liên tục.
ucinewgame xóa bảng chuyển vị và lịch sử tìm kiếm, không đưa bàn cờ về khai cuộc. Vẫn phải dùng position để đặt thế cờ mới. Mã hiện tại xuất CRITICAL ERROR rồi thoát nếu gặp thế cờ không đọc được hoặc nước đi lịch sử không hợp lệ; chương trình kết nối cần giữ lại thông báo lỗi.
readyok không có nghĩa “tìm kiếm đã kết thúc”. isready hiện tại trả lời trực tiếp trong khi tìm kiếm vẫn có thể tiếp tục. Muốn kết thúc lượt tìm kiếm đang chạy, cần gửi stop và chờ bestmove. Cũng nên kết thúc tìm kiếm trước khi đổi tùy chọn, vì phần xử lý setoption sẽ chờ tìm kiếm hiện tại hoàn tất. Mã tùy chọn kiểm tra kiểu và khoảng giá trị trước khi thực hiện cập nhật tương ứng.
Mã nguồn: Dựng lại thế cờ và lịch sử, Chờ khi đặt tùy chọn và lỗi thế cờ, Kiểm tra tùy chọn.
Luồng và bộ nhớ: chia sẻ thông tin hữu ích, tự tính các nhánh
Mỗi luồng làm việc giữ riêng thế cờ và tiến độ tìm kiếm, chuẩn bị trạng thái gốc của mình trước khi chạy; lịch sử thế cờ trước đó được chia sẻ chỉ để đọc. Các luồng trong cùng một phiên engine dùng chung bảng chuyển vị. Một phần thống kê lịch sử được tổ chức theo nút NUMA, phần khác được giữ riêng trong từng luồng. Đây không phải “mỗi luồng chỉ phụ trách một quân”, cũng không phải cứ thêm một luồng là sao chép nguyên một vùng Hash.
Có thể hiểu NUMA là trên máy nhiều nhân, “truy cập bộ nhớ ở gần và ở xa có chi phí khác nhau”. Mã liên quan sắp xếp vị trí của luồng và các bản sao mạng. Mã bộ nhớ còn cung cấp cấp phát theo căn chỉnh, thử dùng trang lớn và cách dự phòng tương ứng. Các cơ chế này giúp truy cập dữ liệu hiệu quả hơn, không thay đổi luật đi quân cờ tướng.
Số luồng ảnh hưởng tới tổng bộ nhớ, nhưng Hash chỉ đặt ngân sách cho bảng chuyển vị, không phải toàn bộ bộ nhớ của tiến trình. Mạng, bảng lịch sử, ngăn xếp luồng và các dữ liệu khác cũng chiếm chỗ. Đa luồng có phần tính trùng, nên số luồng gấp đôi không có nghĩa sức cờ gấp đôi.
Quản lý thời gian cấp ngân sách tìm kiếm từ thời gian còn lại, thời gian cộng thêm và các thông tin khác, rồi điều chỉnh theo diễn biến tìm kiếm. Xem Đa luồng và phân bổ thời gian.
Mã nguồn: Chuẩn bị luồng và trạng thái gốc, Dữ liệu dùng chung và dữ liệu riêng của luồng, Cấp phát bộ nhớ.
cp, mate, wdl: ba ý nghĩa khác nhau
Điểm tìm kiếm UCI thô nhìn từ bên đang đến lượt đi ở thế cờ gốc; số dương có lợi cho bên đó. Giao diện có thể đổi sang góc nhìn của Đỏ, nên cần phân biệt khi đọc nhật ký.
| Đầu ra | Nên hiểu thế nào? |
|---|---|
score cp 100 | Điểm thế cờ thông thường đã đổi sang thang hiển thị; không phải xác suất thắng 100% hay hơn 100 Elo. |
score mate 3 | Báo khoảng cách tới kết quả thắng thua quyết định theo cách đếm nước của UCI; số dương là bên mình thắng, số âm là bên mình thua. |
wdl 300 600 100 | Ước lượng thắng / hòa / thua của mô hình, theo phần nghìn; ví dụ này tương ứng 30% / 60% / 10%. |
Cách đổi điểm thông thường hiện tại là round(100 × v / a), trong đó v là điểm nội bộ, còn a thay đổi theo thành phần lực lượng trên bàn. Vì vậy, không thể máy móc hiểu 100 cp là cố định “hơn một Tốt”. WDL được tính bằng mô hình đã khớp dữ liệu, ba số cộng lại bằng 1000; nó không phải ba đầu ra phân loại trực tiếp từ NNUE, cũng không phải kết quả chắc chắn sẽ xảy ra trong ván này.
Điểm quyết định thắng thua nội bộ được đổi thành số lớp còn lại trước, rồi thành số nước mate. Chẳng hạn, khoảng cách dương ba lớp được hiển thị là mate 2. Hết nước đi trong cờ tướng và một số trường hợp bị xử thua theo luật cũng dùng hệ điểm thắng thua này, nên thấy mate không có nghĩa ván “chỉ có thể kết thúc bằng chiếu bí”. Nếu còn kèm lowerbound hoặc upperbound, cần hiểu đó là cận điểm.
Xem Đánh giá thế cờ và NNUE để biết đánh giá được đưa vào tìm kiếm thế nào. Mã nguồn: Phân loại điểm, Chuyển đổi cp / mate / WDL, Đầu ra tìm kiếm gốc, Điểm thắng thua theo luật.
Kiểm tra hoạt động: mỗi công cụ đo gì?
Nên dùng các lệnh gỡ lỗi dưới đây sau khi đã dừng tìm kiếm. Một số giao diện có thể không cho nhập lệnh trực tiếp.
| Công cụ | Công dụng và giới hạn |
|---|---|
d | Xem thế cờ engine thực sự đang giữ để kiểm tra có khớp với giao diện không. |
eval | Xem chi tiết các phần đánh giá thế cờ hiện tại; không thực hiện lại một lượt tìm kiếm đầy đủ. |
compiler | Hiển thị thông tin như trình biên dịch, kiến trúc biên dịch và các tập lệnh được bật. |
bench | Chạy một nhóm bài thử với các thế cờ và giới hạn được chỉ định, thống kê nút và thời gian; phù hợp để so sánh hành vi chạy. |
speedtest | Sau bước làm nóng, chạy chuỗi đo tốc độ và báo các thông tin như số luồng, Hash, thời gian và tốc độ nút. |
go perft 3 | Đếm các đường đi hợp lệ tới số lớp được chỉ định, chủ yếu kiểm tra sinh nước đi, thực hiện và hoàn tác nước đi. |
Đệ quy perft không gọi đánh giá thế cờ, cũng không cắt nhánh theo toàn bộ quy tắc chiếu liên tục / đuổi quân liên tục; tuy nhiên, điểm vào Engine::perft() hiện tại vẫn kiểm tra mạng. Nó đếm đường đi, nên không thể gộp các đường khác nhau dẫn tới cùng một bàn cờ thành một, và cũng không thay thế được kiểm thử hồi quy luật cờ hay kiểm tra sức cờ.
NPS là số nút tìm kiếm mỗi giây. Khi so sánh các bản biên dịch, cần giữ cố định mã, mạng, thế cờ, luồng và điều kiện kiểm thử. Công việc ở mỗi nút có thể khác nhau giữa các chiến lược tìm kiếm, nên chỉ nhìn NPS không biết được bên nào mạnh hơn.
Mã nguồn: Lệnh chẩn đoán, Tham số bench, Chạy và báo cáo speedtest, perft.
Bản biên dịch và thư viện nén phụ trách việc gì?
Makefile quyết định kiến trúc đích, trình biên dịch và cách tối ưu. Bản thông thường chọn tập lệnh khi biên dịch; x86-64 universal gói nhiều bản vào cùng một tệp, kiểm tra đặc tính CPU lúc khởi động rồi chọn điểm vào. Mã phân phối hiện tại còn tránh nhánh BMI2 chậm trên một số CPU AMD cũ, không đơn thuần chọn tập lệnh có tên mới hơn.
Muốn biết lần chạy này dùng gì, có thể nhập compiler rồi xem Compilation architecture và Compilation settings. Sau khi universal vào bản đã chọn, chính bản đó trả về thông tin biên dịch; không phải lúc khởi động nó chạy thử tốc độ của mọi bản rồi chọn bản nhanh nhất.
Việc đọc mạng nén bên ngoài dùng Zstandard: misc.cpp giải nén luồng byte, sau đó mã nạp mạng phân tích và kiểm tra các tham số. Thư viện nén phụ trách đọc tệp, NNUE phụ trách đánh giá, còn mã tìm kiếm phụ trách chọn nước. Không thể coi thư viện nén là một thuật toán sức cờ khác chỉ vì nó có trong kho mã.
Mã nguồn: Mục tiêu biên dịch, Chọn bản x86 trong universal, Thông tin biên dịch, Đọc luồng nén, Nạp mạng bên ngoài. Xem toàn bộ phân công tệp ở Hướng dẫn đọc mã nguồn.
