Hướng dẫn đọc mã nguồn
Engine (động cơ cờ) Pikafish gồm nhiều mô-đun với nhiệm vụ rõ ràng. Phần tìm kiếm quyết định nên tập trung tính những biến nào; phần đánh giá chấm điểm thế cờ; phần quản lý thế cờ xử lý việc đi quân, hoàn tác và luật chơi. Lớp điều phối kết nối các phần này, trao đổi lệnh và kết quả với giao diện.
Loạt bài này dựa trên commit 1c66b9b của official Pikafish. Mỗi trang đều dẫn tới mã nguồn tương ứng. Hai thuật toán có cùng tên không có nghĩa là cách triển khai trong các engine hoặc phiên bản khác cũng giống nhau.
Chọn bài theo điều bạn muốn biết
| Bạn muốn hiểu điều gì? | Bắt đầu từ đây |
|---|---|
| Làm sao tìm được nước hay giữa rất nhiều biến? | Thuật toán tìm kiếm: sắp thứ tự, lưu kết quả, cắt tỉa, giảm và tăng độ sâu. |
evaluate.cpp làm gì, NNUE chấm điểm ra sao? | Đánh giá thế cờ và NNUE: đặc trưng đầu vào, tính toán tăng dần, đầu ra của mạng và hiệu chỉnh đánh giá. |
| Bàn cờ được lưu thế nào, việc đi quân, hoàn tác và xử lý luật diễn ra ra sao? | Biểu diễn thế cờ và xử lý luật: bitboard, trạng thái, khóa hash, tính hợp lệ và lịch sử nước đi. |
| Giao diện khởi động engine thế nào, luồng và bộ nhớ hoạt động ra sao? | Vận hành và giao tiếp của engine: xử lý lệnh, xuất kết quả, phân bổ tài nguyên và chẩn đoán. |
Nếu chỉ muốn chỉnh cài đặt, hãy đọc Tùy chọn UCI trước. Nếu đang viết giao diện hoặc chương trình kết nối với engine, hãy đọc thêm Giao thức UCI.
Tài liệu NNUE của Stockfish cung cấp kiến thức nền tổng quát sâu hơn. Để hiểu cách triển khai cụ thể của Pikafish hiện tại, hãy bắt đầu từ bài về đánh giá ở trên.
Theo dõi một lần bắt đầu phân tích
- Chuẩn bị khi khởi động.
main.cppkhởi tạo các bảng tấn công và dữ liệu cần cho thế cờ, rồi vào vòng lặp nhận lệnh UCI. - Nhận thế cờ. Lớp UCI phân tích lệnh
position; lớp điều phối dựng thế cờ, rồi thực hiện lần lượt các nước trongmoveskèm theo và lưu trạng thái. Chỉ có FEN của bàn cờ hiện tại thì không thể khôi phục đầy đủ lịch sử nước đi trước đó. - Nhận yêu cầu tìm kiếm.
gomang theo các giới hạn như thời gian và độ sâu.Enginekiểm tra mạng rồi giao việc cho các luồng tìm kiếm. - Mở rộng các biến. Phần tìm kiếm gọi bộ sinh nước đi và kiểm tra tính hợp lệ, thực hiện nước đi rồi tìm tiếp bằng đệ quy. Khi cần ước lượng điểm của thế cờ, nó gọi phần đánh giá. Tính xong một nhánh, nó hoàn tác nước đi để trở về thế cờ trước.
- Trả kết quả. Lớp điều phối chuyển thông tin tìm kiếm qua các hàm gọi lại; lớp UCI xuất
infovà cuối cùng làbestmove. Giao diện quyết định cách hiển thị các thông tin đó.
Ở đây, Engine là lớp C++ làm nhiệm vụ điều phối, không phải một engine độc lập khác. go khởi chạy tìm kiếm ở nền; vòng lặp nhận lệnh vẫn phải tiếp nhận được các lệnh như stop.
Mã nguồn: điểm vào chương trình, giao diện của lớp Engine, khởi chạy tìm kiếm và thiết lập thế cờ. Quy trình lệnh đầy đủ hơn được trình bày trong Vận hành và giao tiếp của engine.
Bản đồ tệp: tìm ở đâu khi có câu hỏi
Không cần đọc lần lượt từ tệp đầu tiên đến tệp cuối cùng. Sẽ dễ hiểu hơn nếu xác định phần nào phụ trách công việc cần tìm, rồi lần theo các lời gọi hàm.
Tìm kiếm và đánh giá
| Tệp | Nhiệm vụ chính |
|---|---|
search.cpp | Tăng dần độ sâu, PVS, cắt tỉa, điều chỉnh độ sâu, tìm kiếm tĩnh và xử lý kết quả tìm kiếm. |
movepick.cpp, history.h | Thứ tự lấy các nước đi ứng viên và những thống kê lịch sử được tích lũy trong tìm kiếm. |
evaluate.cpp | Gọi NNUE và điều chỉnh kết quả của mạng thành đánh giá tĩnh dùng trong tìm kiếm. |
nnue/network.cpp và nnue/nnue_architecture.h | Đọc, ghi mạng, điểm vào của quá trình suy luận và cách kết nối các lớp mạng. |
nnue/features/, nnue/nnue_accumulator.cpp | Chuyển thế cờ thành đầu vào của mạng và tái sử dụng phần tính toán giữa các thế cờ liền nhau. |
Thế cờ, nước đi và luật chơi
| Tệp | Nhiệm vụ chính |
|---|---|
types.h, bitboard.h | Các định nghĩa cơ bản như vị trí trên bàn cờ, quân cờ, nước đi và các phép toán bitboard. |
attacks.cpp, attacks.h | Khởi tạo và tra cứu bảng tấn công, xử lý đường đi của Xe và Pháo, chân Mã, mắt Tượng cùng các quan hệ tấn công khác. |
movegen.cpp | Sinh nước đi ứng viên theo nhu cầu: ăn quân, không ăn quân, thoát chiếu... |
position.h, position.cpp | Lưu và cập nhật thế cờ, kiểm tra sự an toàn của Tướng, hoàn tác nước đi, ước lượng đổi quân và xử lý các luật phụ thuộc lịch sử. |
Vận hành, giao tiếp và công cụ
| Tệp | Nhiệm vụ chính |
|---|---|
main.cpp, engine.cpp | Điểm vào chương trình và việc điều phối các tài nguyên như thế cờ, mạng, luồng và tùy chọn. |
uci.cpp, ucioption.cpp | Nhận lệnh, phân tích tham số, xuất kết quả và xử lý tùy chọn. |
score.cpp | Chuyển điểm nội bộ sang dạng điểm xuất ra ngoài; không phụ trách chọn nước đi tốt nhất. |
thread.cpp, timeman.cpp, tt.cpp | Luồng, quỹ thời gian và bộ nhớ của bảng chuyển vị. |
numa.h, memory.cpp | Những công việc hỗ trợ như bố trí luồng và bộ nhớ theo phần cứng, cấp phát bộ nhớ. |
perft.h, benchmark.cpp | Liệt kê các chuỗi nước đi hợp lệ hoặc chạy các tác vụ kiểm tra định sẵn; hai công cụ kiểm tra những khía cạnh khác nhau. |
Makefile, universal/ | Cấu hình biên dịch và chọn cách triển khai phù hợp lúc chạy trong gói universal. |
Bản đồ này nhóm các tệp theo nhu cầu đọc, không có nghĩa các mô-đun chỉ gọi nhau theo một chiều. Chẳng hạn, phần tìm kiếm truy cập thế cờ, phần đánh giá, bảng chuyển vị và các bảng lịch sử; lớp điều phối chuẩn bị sẵn những tài nguyên đó.
Những điều cần phân biệt khi đọc mã
Điểm tìm kiếm, đánh giá tĩnh và điểm trên giao diện thuộc các bước khác nhau. Giá trị do evaluate() trả về chưa chắc là điểm cuối cùng giao diện hiển thị. Cũng không thể coi trực tiếp con số cp xuất ra là xác suất thắng hay hệ số Elo. Xem thêm Quy trình đánh giá và Xuất điểm số.
Thế cờ hiện tại và trạng thái lịch sử cũng khác nhau. Cách sắp quân có thể giống nhau nhưng bên đến lượt đi, bộ đếm giới hạn nước hoặc chu kỳ chiếu và đuổi bắt trước đó lại khác. Khi đọc mã về bộ nhớ đệm và luật chơi, cần xem chúng dùng những thông tin trạng thái nào.
Cần đối chiếu tên gọi và chú thích với cách gọi thực tế. Mã nguồn Pikafish vẫn giữ không gian tên Stockfish và một số thuật ngữ từ dự án gốc. Một tên gọi quen thuộc không có nghĩa phiên bản cờ tướng hiện tại sử dụng mọi cơ chế của cờ vua. Hãy xem tiếp hàm được gọi trong điều kiện nào và thực tế chạy vào nhánh nào.
Tìm thấy một phương pháp không có nghĩa tham số của nó là cố định. Các ngưỡng cắt tỉa, cấu trúc mạng và lựa chọn biên dịch đều có thể thay đổi. Các liên kết tới commit cố định trong bài giúp phần giải thích khớp với mã nguồn. Khi đọc mã của phiên bản mới, cần kiểm tra lại.
Sau khi sửa, làm sao biết sửa đúng
Mỗi loại kiểm tra trả lời một câu hỏi khác nhau:
- Biên dịch và khởi động được: cho thấy nhánh biên dịch đó về cơ bản hoạt động, chưa chứng minh luật chơi và tìm kiếm đều đúng.
- Perft: phù hợp để kiểm tra xem việc phối hợp sinh nước đi ứng viên, lọc tính hợp lệ, thực hiện và hoàn tác nước đi có cho kết quả mong đợi không; không thay thế được kiểm tra các luật dựa trên lịch sử như chiếu liên tục hay đuổi bắt liên tục.
- Ca kiểm tra luật và thế cờ: dùng bàn cờ xác định cùng lịch sử nước đi đầy đủ để kiểm tra kết thúc ván, điểm số hoặc cách xử lý luật.
- Benchmark: giúp quan sát số nút, thời gian và các số liệu khác của tác vụ định sẵn. Khi so sánh, ngoài thay đổi đang thử, cần giữ nhất quán các điều kiện như phiên bản engine, mạng, số luồng, phần cứng và tham số.
- Thử đấu: trực tiếp đo ảnh hưởng của thay đổi tới kết quả thi đấu; cần đủ mẫu và điều kiện kiểm tra nhất quán. Chạy nhanh hơn, tìm sâu hơn hoặc giải đúng một thế cờ đều không đủ để tự nó chứng minh sức cờ tổng thể đã tăng.
Xem thêm Vận hành và giao tiếp của engine. Để bắt đầu thử đấu, xem Cách kiểm tra engine.
