Chương 2 CỜ TƯỚNG - PHIÊN BẢN ĐẦU TIÊN (VERY SIMPLE CHINESE CHESS PROGRAM - VSCCP 1.

0)
I. Giới thiệu
Trò chơi Cờ Tướng (tên phiên âm Trung Quốc XiangQi, tên tiếng Anh Chinese Chess) là một minh hoạ rất tốt cho bài toán tìm kiếm trên cây trò chơi và áp dụng thuật toán AlphaBeta trên cây này như thế nào. Đây là một trò chơi thú vị và tương đối phổ biến ở Việt nam, châu Á cũng như trên toàn thế giới. Nó tạo cảm giác dường như máy tính có thể suy nghĩ và đọ sức với con người (thực tế cho đến nay nó vẫn chỉ tính toán mà thôi). Cờ Tướng là loại cờ có độ phức tạp và rất nhiều mặt tương đương với cờ Vua. Trong phần này, chúng tôi sẽ giới thiệu với bạn những kiến thức cơ bản nhất về một chương trình chơi cờ phải như thế nào. Các chương trình mẫu VSCCP (Very Simple Chinese Chess Program Chương trình Cờ Tướng rất đơn giản) cũng hết sức đơn giản và có đầy đủ trong các phụ lục.

II. Viết chương trình chơi cờ VSCCP 1.0 1. Biểu diễn bàn cờ và quân cờ
Bàn cờ trong trò chơi cờ Tướng là một bảng chữ nhật bao gồm 9 đường dọc và 10 đường ngang. Các quân cờ chia làm hai bên đứng tại các giao điểm của các đường. Bàn cờ và vị trí khởi đầu các quân cờ như hình 2.1. Cách đơn giản nhất để biểu diễn một bàn cờ trong máy tính là ta dùng một mảng hai chiều, kích thước 9 x 10: piece: array[1..10, 1..9] of byte Mảng trên hoạt động tốt nhưng có cái bất tiện là ta phải dùng tới hai chỉ số để truy cập vào mảng (ví dụ vị trí quân Pháo góc trên bên trái (cột 2, dòng 3) là piece[3, 2]). Một cải tiến nhỏ là ta dùng mảng một chiều như sau:

piece: array[1..90] of byte Truy nhập đến vị trí quân Pháo góc trên bên trái lúc này là piece[20].

Các ô của mảng sẽ chứa những giá trị khác nhau cho biết đó là quân cờ gì. Mỗi quân cờ sẽ được gán một mã số khác nhau như bảng dưới đây. Các chỗ trống (không có quân cờ) sẽ được điền kí hiệu trống (EMPTY): Quân cờ Tốt (Chốt) Sĩ Tượng Mã Pháo Xe Tướng Trống Kí hiệu PAWN BISHOP ELEPHANT KNIGHT CANNON ROOK KING EMPTY Giá trị 1 2 3 4 5 6 7 0

Ngoài mục đích gán mỗi quân cờ một mã số để phân biệt, mã này còn giúp ta ước lượng sơ bộ tầm quan trọng của quân cờ đó. Như vậy, lúc khởi đầu, các ô trong mảng sẽ được gán các giá trị quân cờ nhờ khai báo const (trong Pascal) như dưới, trong đó BOARD_SIZE (kích thước bàn cờ = 90) là một hằng số đã được định nghĩa trước đó: piece: array[1..BOARD_SIZE] of byte = ( 6, 4, 3, 2, 7, 2, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,

Khi tới phiên đối phương đi. 1. 6. 0. 3. 0. 1. 0. 6). 0. 0. ta còn khai báo biến computerside cũng chỉ có một trong hai giá trị LIGHT và DARK nhằm cho biết bên nào là máy. 0. 1. 0. 0. 0. 0. 0. 0. 5. 0. cho phép theo dõi và thi đấu bình thường. 0. ta cần đảo ngược giá trị trong cả side và xside bằng các lệnh như sau (chú ý là LIGHT+DARK = 3): side := xside. 1. 0. 0. 0. Tuy cách này trông xấu và thường không dùng trong các chương trình có trên thị trường nhưng nó có các ưu điểm nổi trội sau: • • Vẫn hiện được bàn cờ rất đầy đủ. 2. 4. 0. 0. 2. xside := 3 . 0. 1. 0. 1. 5. 2. 0. 2. 2. 0. 0. 0. 1. 5. Chúng tôi xin giới thiệu một phương pháp đơn giản là dùng thêm một mảng nữa . 1. 0. 0. 0. 0. 0. 2.mảng color. 0. 0. Ngoài ra. 0. Hai bên được gán kí hiệu và mã như bảng dưới. 1. 0. 0. 1.làm chương trình chơi hay hơn. . 0. 0. Đến đây. 0. Bạn sẽ đỡ nhiều thời gian tìm hiểu và phát triển giao diện. 1. 0. 0. 1. 0. 2. bàn cờ còn chưa có thông tin để phân biệt một quân cờ là của bên nào. 0. 1. Để biết bên nào tới lượt đi. 0. Một biến toàn cục khác xside sẽ có sẵn giá trị ngược với side để tiện tính toán (ví dụ nếu side = LIGHT thì xside = DARK).0. 1. 1. 0. 0. 5. 1. 0. 7. ta dùng một biến toàn cục side chứa một trong hai giá trị LIGHT và DARK. 0. 0. 2). 0. 0. 0. 0. 0. Giao diện đồ hoạ chỉ cần thiết lúc hoàn chỉnh . 1. 0. 2. Rất đơn giản và tin cậy. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 2. 3. 0. 0. 0. 0. phương pháp đơn giản nhất là hiện ở chế độ text (văn bản). 0. 0. 2. 2. 1. 2. 0. Để hiện bàn cờ. 2. 0. Bên Đen Trắng Trống Kí hiệu DARK LIGHT EMPTY Giá trị 1 2 0 Ta lại có thông tin về bên được khai báo khởi đầu tương tự: color: array[1.. 2.BOARD_SIZE] of byte = (1.xside. 0. 0. 1. 0. 0. 0. 0.lúc bạn "đóng gói" chương trình. 0. 0. 0. 0. 0. dồn sức cho việc quan trọng hơn . 0. trực quan. 0. 0. 0. 1. Những chỗ trống sẽ dùng cùng kí hiệu trống EMPTY. 0. 1. 0. 0. 2. 0. 0. 0. dễ hiểu. để lưu các thông tin về bên. 1. 0. Ta có thể cải tiến thay cho kiểu byte của mảng piece là một bản ghi nhằm lưu thêm các thông tin này. 0. 4. 0. 0. 0. 0. 0. 0. 1. 2. 2. 0. 0. 0.

Nó chỉ có thể "tiếp chuyện" Tướng đối phương. NORMAL). tìm lỗi rất dễ dàng. Quân hai bên sẽ có mầu khác nhau. Quân cờ được biểu diễn bằng một chữ cái viết hoa đứng đầu tên của nó. R-Xe. Chú ý cách đánh kí hiệu các vị trí trong bàn cờ bằng chữ và số giống như bàn cờ Vua. để hiện toàn bộ bàn cờ ta chỉ cần gọi một vòng lặp for là xong: for i := 1 to BOARD_SIZE do DrawCell(i. Do quân Tướng và Tượng có cùng chữ cái đầu T nên để tránh nhầm lẫn ta dùng chữ V (Voi) biểu diễn quân Tượng. S-Sĩ.. Win98. C-Chốt). T-Tượng. K-Tướng. ta viết một thủ tục DrawCell với tham số là vị trí bàn cờ.• • • Việc hiện thêm các thông tin để kiểm tra. macintosh. System7. Ta sẽ hiện bàn cờ ở dạng như hình 2. Tuy quân Tốt và Tướng cũng có cùng chữ cái đầu nhưng ta không sợ nhầm do Tốt không thể nhập cung bên mình được. Có thể bạn sẽ muốn thử chạy trên những máy tính có nền tảng khác như máy mini. Để hiện một quân cờ. nó còn một tham số nữa cho biết sẽ hiện mầu nền của quân cờ với mầu bình thường NORMAL (sẽ có nền đen) hay mầu khác SELECT (sẽ có nền xanh sẫm) để thể hiện quân cờ đó được chọn (phục vụ cho chọn và đi quân của người chơi). Dễ chuyển đổi hệ máy và hệ điều hành. nhanh và đảm bảo hơn các hàm đồ hoạ. N-Mã. P-Pháo. hoặc theo qui định của Liên đoàn cờ Việt Nam: Tg-Tướng. Ngoài ra. C-Pháo. E-Tượng. Bạn có thể tự phát triển giao diện đồ hoạ và cài chuột theo ý mình. hệ điều hành Windows 3. B-Sĩ.3.1. Sinh nước đi . Như vậy. procedure DrawCell(pos. WinNT. UNIX. M-Mã. Rõ ràng việc dùng hai thủ tục có sẵn của Pascal là gotoxy và write thì dễ. 2. nhưng lúc này hai bên lại phân biệt được nhờ mầu (các phương pháp khác là dùng chữ cái đầu tên tiếng Anh: PTốt.. mode: byte). X-Xe. Win95.

Việc kiểm tra . Do chức năng này phải sinh được hàng triệu nước đi mỗi khi máy đến lượt nên giảm thời gian sinh nước đi có ý nghĩa rất lớn. Ô 81 tuy có trong bàn cờ nhưng khác hàng nên không được chấp nhận. Một nước đi sai sẽ làm hỏng mọi tính toán. Như vậy. end. Sinh nước đi là một thuật toán vét cạn. Nước đi sang trái một ô được chuyển thành ô số 83 là một vị trí cùng hàng với ô xuất phát nên được chấp nhận. Đầy đủ (rất quan trọng). Thủ tục này luôn sinh nước cho bên đang tới lượt chơi căn cứ vào nội dung của biến side. dest: byte. Máy sẽ tính toán để chọn nước đi có lợi nhất cho nó. Máy phải sinh được từ những nước đi rất hay cho đến những nước đi rất ngớ ngẩn (như đẩy Tướng vào vị trí khống chế của đối phương). Các yêu cầu chính đối với một thủ tục sinh nước đi là: • • • Chính xác (rất quan trọng).Một trong những việc quan trọng nhất để máy tính có thể chơi được cờ là phải chỉ cho nó biết mọi nước đi có thể đi được từ một thế cờ. type move = record { Định nghĩa cấu trúc nước đi } from. Một nước đi khác cần phải xem xét là sang trái ba ô . Ta hình dung ngay thủ tục sinh nước đi Gen sẽ có đầy những vòng lặp for. các câu lệnh kiểm tra if và rẽ nhánh case. muốn biết ô của nước đi sang trái có được phép không ta phải kiểm tra xem nó có cùng hàng với ô xuất phát không. trong đó các phép tính kiểm tra giới hạn chiếm một phần đáng kể. Nhanh.ô 81. Đó là điểm xuất phát (from) và điểm đến (dest). Đồng thời chương trình có thể bị trọng tài xử thua luôn. luật đi quân nhiều và phức tạp nên việc kiểm tra tính đúng đắn tương đối khó. Bây giờ ta sẽ sinh thử một nước đi sang trái một ô cho nó. Kiểm tra giới hạn bàn cờ Ví dụ có một quân Xe nằm ở ô số 84 (trong mảng piece). Ta sẽ khai báo một cấu trúc move như sau để dùng những nơi cần đến dữ liệu nước đi. Một nước đi có hai giá trị cần quan tâm. Do số lượng nước đi sinh ra lớn. Đây là một trong những thủ tục phức tạp và dễ sai nhất. Máy sẽ tìm mọi nước đi hợp lệ có thể có. Sinh được mọi nước đi có thể có từ một thế cờ.

Dưới đây là đoạn chương trình dùng để sinh những nước đi sang trái của một quân Xe.1.5). Như vậy. while Xét lần lượt tất cả các ô từ bên trên Xe cho đến lề trên bàn cờ do . { Nước sang trái đầu tiên } while ((i-1) div 9) = ((x-1) div 9) do begin if (ô thứ i là trống) or (ô thứ i có quân đối phương) then gen_push(vị trí của Xe.1.thực hiện bằng cách chia cho 9 (kích thước của một dòng) và lấy phần nguyên (trước đó lại phải chuyển thứ tự ô về gốc là 0 bằng cách trừ đi 1). if ô thứ i không trống then break.. Ta thấy ((83-1) div 9) = ((84-1) div 9) nên ô 83 được chấp nhận. vị trí ô đang xét) if ô đang xét không trống then break. Các nước đi vừa sinh ra sẽ được đưa vào danh sách nước đi nhờ gọi thủ tục Gen_push. while Xét lần lượt tất cả các ô từ bên dưới Xe cho đến lề dưới bàn cờ do . i := x .. Việc sinh những nước đi theo chiều khác cũng tương tự (ví dụ để sinh nước đi theo chiều đứng ta chỉ cần cộng hoặc trừ một bội số của 9) nhưng điều kiện kiểm tra sẽ khác nhau.. Thủ tục này có hai tham số là vị trí xuất phát của quân cờ sẽ đi và nơi đến dest của quân cờ đó. while Xét lần lượt tất cả các ô từ bên trái Xe cho đến lề phải bàn cờ do . { Kết thúc quá trình sinh nước đi sang trái } i := i . trong đó x là vị trí của quân Xe này .. trong khi đó do ((81-1) div 9) <> ((84-1) div 9) nên kết luận là nước đi đến ô 81 không hợp lệ (hình 2. end. vị trí ô đang xét). { Xét tiếp vị trí bên trái } end. chương trình sinh nước đi Gen có dạng như sau: for Xét lần lượt từng quân cờ bên hiện tại do case quâncờ of Xe: begin while Xét lần lượt tất cả các ô từ bên phải Xe cho đến lề trái bàn cờ do begin if (ô đang xét là ô trống) or (ô đang xét chứa quân đối phương) then gen_push(vị trí của Xe.

thì khi đổi sang bàn cờ mở rộng sẽ thành vị trí mailbox90[84] = 148. Mục đích chính của kĩ thuật này là nhằm giảm bớt các phép kiểm tra vượt giới hạn bàn cờ và làm đơn giản chương trình. Có nghĩa là nó ứng với ô thứ 148 của mảng mailbox182. việc sinh nước và kiểm tra hợp lệ đều dựa vào mảng mailbox182 nên giá trị 13 là kích thước một dòng của mảng này). ta dùng một bàn cờ mở rộng. khó tìm lỗi nhưng khá nhanh. Để sinh các nước đi chéo ta phải cộng trừ với một hằng số khác. phải bỏ đi và ngừng sinh nước về phía đó.1 = 147... tức là các giá trị báo vượt biên. Chương trình cũng cần kiểm tra số nước đi tối đa theo một hướng của quân cờ đang xét.do các bảng của nó có dạng các hộp phân thư).. Việc chuyển đổi toạ độ thực hiện nhờ hai mảng. Các ô ứng với các đường bao mở rộng đều có giá trị -1.mailbox182 dùng để kiểm tra các nước đi vượt quá đường biên và dữ liệu để chuyển trở lại toạ độ bình thường. Bây giờ ta sẽ sinh thử một nước đi sang trái một ô cho quân Xe này. nước đi mới là ô số 148 . mỗi chiều có thêm 2 đường nữa (bàn cờ mới có kích thước 13x14 = 182). end. Việc sinh và kiểm tra sẽ được thực hiện trong bàn cờ mở rộng. Mấu chốt là thay cho bàn cờ có kích thước bình thường 9x10 = 90. cồng kềnh. Do mailbox182[147] = 83 ¹ -1 nên nước đi này được chấp nhận. Ta nên lưu các hằng số này vào . Việc đi một quân sang vị trí mới thực chất là ta đã thay đổi toạ độ của nó bằng cách cộng với một hằng số (dương hoặc âm). Tuy nhiên. Mảng mailbox90 dùng để chuyển từ toạ độ bàn cờ thường sang toạ độ bàn cờ mới. Phương pháp này có nhược điểm là chương trình phải viết phức tạp.. Mảng ứng với bàn cờ mới . Để sinh một ô mới theo hàng dọc.. Chỉ có quân Pháo và Xe có thể đi đến 9 nước đi. Nếu một nước đi được sinh ra lại rơi vào một trong hai đường bao thì có nghĩa nó đã rơi ra ngoài bàn cờ rồi. Sở dĩ có đến hai đường biên (chứ không phải một) do quân Mã và Tượng có thể nhẩy xa đến hai ô (hình 2. Ví dụ. Ở trên ta đã thấy để quân Xe sang trái một nước ta đã phải cộng vị trí hiện tại với -1.5.6). ta phải cộng với +13 hoặc -13 (chú ý. nếu vị trí quân Xe nằm ở ô số 84 (trong mảng piece) như hình 2. còn các quân khác có nhiều nhất là 1. nước đi sang trái ba ô. chúng tôi giới thiệu một kĩ thuật khác có tên gọi là "hộp thư" (mail box . { Xong quân Xe } Pháo: . Số 83 cho biết kết quả sang trái một ô là vị trí 83 trong bàn cờ bình thường. có số 148 .3 = 145 và mailbox182[145] = -1 cho biết đó là một nước đi không hợp lệ. Các nước đi trên bàn cờ 9x10 được chuyển tương ứng sang bàn cờ này. Trong chương trình mẫu VSCCP.

ta còn phải kiểm tra nước bị cản (đối với Tượng và Mã) và xử lí cách ăn quân của quân Pháo như một trường hợp đặc biệt. x:=mailbox90[i]. ta dùng một mặt nạ bít Maskpiece mà tuỳ theo từng quân sẽ có giá trị khác nhau. Pháo. Như vậy. ô số 3 có giá trị legalmove[3] = 5 (chuyển thành số nhị phân là 00000101) cho biết các quân cờ được phép đi đến đó là Tượng. Nếu ở ô cần kiểm tra có bit trùng với mặt nạ này được đặt là 1 thì quân cờ đó được phép đến ô đấy. Để tiết kiệm ta chỉ lưu nước tiến của Tốt đen. Ví dụ. Các quân cờ có số hướng ít hơn sẽ được điền 0 vào phần thừa. Kiểm tra vị trí được phép đến Việc chuyển toạ độ từ bàn cờ thường sang bàn cờ mở rộng chỉ giúp ta loại bỏ được các nước vượt quá biên. Ngoài ra. Ta còn phải kiểm tra một số giới hạn khác. CANNON] then n := 9 else n := 1. Ví dụ như Tướng và Sĩ không thể đi ra ngoài cung. Mã. tổng thể sinh các nước đi cho một quân cờ có dạng như sau: p := piece[i]. còn nước tiến của Tốt trắng chỉ đơn giản lấy ngược dấu.. Xe. Một chiều dựa vào loại quân cờ nên chỉ số được đánh từ 1 đến 7. Tượng chỉ được phép ở 7 điểm cố định phía bên mình. { Chuyển sang bàn cờ mở rộng} if p in [ROOK.. còn phía bên mình cũng bị giới hạn ngặt nghèo một số ô.mảng offset có 2 chiều. Tốt chỉ được phép tung hoành trên đất đối phương. Để thực hiện những phép kiểm tra này. { Chuyển về toạ độ bình thường } if side = DARK then t := y else t := 91-y. for t:=1 to n do { Số nước có thể đi theo một hướng} begin if (p=PAWN) and (side=LIGHT) then x := x . { Sinh nước đi cho quân cờ p ở vị trí i } for j := 1 to 8 do begin { Số hướng đi tối đa } if offset[p.j] = 0 then break. Chú ý là nước đi quân Tốt của hai bên khác nhau hướng tiến. Chiều còn lại là số hướng đi tối đa của một quân cờ.offset[p. Quân cờ có nhiều hướng nhất là quân Mã có tới 8 hướng nên chiều này được khai báo chỉ số từ 1 đến 8. j] else x := x + offset[p. if (y=-1) or { Ra ngoài lề ? } ((legalmove[t] and maskpiece[p])=0) { Được phép ở vị trí này không ? } . { Nước đi mới } y := mailbox182[x]. j]. Để kiểm tra một quân cờ có được phép ở đó không. ta dùng một mảng là legalmove ứng với từng ô trên bàn cờ sẽ cung cấp các thông tin này.

một thế cờ này hơn thế cờ kia ở chỗ nó còn nhiều quân bên mình hơn. nó chỉ là con số mà mục đích chính là so sánh được với nhau. . Tuy nhiên phương pháp tính điểm này có những ưu điểm sau: • Là cách tính điểm cơ bản nhất. Nhưng cũng có lúc hai Mã đứng rời lại tốt hơn hai Mã giao chân khi Mã này lại cản Mã kia trong một thế trận nào đó. thế nhưng số hoá điều này qua hàm lượng giá quả là một điều khó quá sức. nếu hở mặt Tướng thủ tục này sẽ không đưa nước đi đó vào danh sách các nước đi. thế thủ của từng quân cờ cũng như cả cục diện trận đấu. gặp thời một Tốt cũng thành công". Đánh giá một thế cờ Đánh giá một thế cờ là một trong những nhiệm vụ quyết định chương trình chơi cờ của bạn có là "cao thủ" hay không. Hàm sẽ trả lại giá trị true nếu nước vừa đi gây hở mặt Tướng. Điều này tuy không thật đúng lắm. nhưng đối với đa số chương trình thì con số đó không có gì đặc biệt. có thể sát cánh tiến quân và tựa lưng phòng thủ thường có giá hơn hai Mã đứng rời. Mã và xử lí Pháo ở đây } . nhưng nó giúp cho thuật toán tìm kiếm của ta làm việc tốt và trong thực tế cũng khá gần với sự thật. 2. Ta cũng có thể nhận thấy trong phần lớn thời gian diễn ra trận đấu.xấu. 3. chiến lược thường chỉ được tính như những điểm cộng thêm vào và đóng góp một tỷ lệ nhỏ trong tổng số điểm của một thế cờ. Ta có thể biểu diễn chất lượng một thế cờ bằng một con số. Cách tính này sẽ lấy tổng giá trị các quân cờ hiện có của bên mình trừ đi tổng giá trị các quân cờ hiện có của đối phương. Nó là cơ sở của đa số hàm đánh giá. Do đó. Chúng ta bắt đầu với việc công nhận các giả thuyết sau: 1. ta đặt nó trong thủ tục gen_push. Các phương án. một cặp Mã giao chân. nếu ta coi là đạt được một thế tốt thì đối phương của ta phải thấy đó là thế xấu đối với họ và ngược lại). Việc kiểm tra được thực hiện nhờ hàm kingface. tính biến. Nhờ điểm này máy mới có thể so sánh các thế cờ với nhau và biết chọn nước đi tốt nhất. Trong chương này chúng ta chỉ cài đặt phương pháp đơn giản nhưng cơ bản nhất: lượng giá dựa trên cơ sở giá trị của từng quân cờ. Căn cứ vào một thế cờ máy sẽ gán cho nó một điểm số (lượng giá tĩnh) để đánh giá độ tốt . Cách làm này đã bỏ qua mất những nghệ thuật. nghĩa là chỉ nhằm nhằm sơ hở của đối phương là "ăn" ngay mà không cần biết đến hậu quả (điều này không hẳn đúng. Các quân cờ được triển khai không theo một chiến lược chung nào hết (vô định). đóng vai trò chủ đạo trong điểm của một thế cờ. Đây là một nhiệm vụ rất khó khăn và phức tạp do không tồn tại một thuật toán tổng quát và thống nhất nào để tính điểm cả. Tuy nhiên đơn giản hơn. chiến lược chơi cờ (mà đó là những điểm để đánh giá một người là giỏi cờ hay không).. nhiều quân giá trị cao hơn cũng như có bắt được nhiều quân và quân giá trị cao của đối phương hơn không. con số đó có thể là đánh giá của ta về xác suất chiến thắng. Một vấn đề nữa là luật hai Tướng không được đối mặt trực tiếp với nhau. Ví dụ.. thế công.then break. Nó còn tạo cảm giác như cờ "vồ". phụ thuộc vào tính hãm. Hàm này có thể được đặt trong thủ tục sinh nước Gen. Chúng ta nên đo chất lượng của một thế cờ tương tự như phép đo của đối phương (do đó. lí do sẽ trình bầy dưới). hai bên đều tìm cách tiêu diệt quân của nhau. Ví dụ. { Thoát nếu nước đi không hợp lệ } { Kiểm tra nước cản với Tượng. "lạc nước hai Xe đành bỏ phí. Ta cũng biết rằng. Điểm của một thế cờ dựa trên rất nhiều yếu tố mà khó có thể số hoá hết được như phụ thuộc vào số lượng và giá trị các quân cờ hiện tại.

cách biệt nhiều lần so với các quân khác. ta cho nó 1000 điểm. Trong chương trình cờ của chúng ta. end.Tướng đối phương.7]of integer=(10. điểm cụ thể của từng quân cờ là các số nguyên 10. 20. "thêm" đều không bằng được Tướng. Mã . 45 và 90. 20.1 (2 nếu đã qua sông). Tượng .20.20.piecevalue[piece[i]]. var i.1000). Do đó. const piecevalue:array[1.• Việc chỉ nhằm "vồ" quân của đối phương nhưng sau khi suy nghĩ sâu nhiều nước cũng đã trở thành "cao cờ" rồi đấy. Việc tăng thêm độ sâu lại giúp máy có cái nhìn xa hơn. mục đích chính của chúng ta cũng là "vồ" bằng được quân có giá trị cao nhất .2. Pháo .5. tức là những điểm căn cứ vào những yếu tố khác (ví dụ cùng là Pháo nhưng lúc chỉ được 40 điểm do ở vị trí "bí".90.5.45. đảm bảo điểm tổng của các quân còn lại cùng đủ mọi loại "thưởng".2. Trong chương trình.. Sau đây là điểm từng quân mà mọi người thường chấp nhận: Quân cờ Tốt Sĩ Tượng Mã Pháo Xe Kí hiệu PAWN BISHOP ELEPHANT KNIGHT CANNON ROOK Điểm 1 (2 nếu đã qua sông) 2 2 4 4.10.5. Hàm lượng giá thật đơn giản như sau (chú ý là ta chưa tăng điểm cho Tốt đã qua sông): function Eval: integer. Ta dùng một mảng piecevalue để lưu điểm từng quân cờ. Tướng thường được cho một điểm rất cao. begin s := 0. quân Tướng là quân quan trọng nhất. Eval := s. for i:=1 to BOARD_SIZE do begin if color[i] = side then s := s + piecevalue[piece[i]] else if color[i] = xside then s := s . Cho điểm như vậy không những vẫn giữ nguyên được tương quan và tránh dùng số thực (tính chậm) mà ta còn có một khoảng cách hợp lí giữa các điểm để sau này dễ dàng thêm những điểm bổ xung "thưởng" hoặc "phạt". Điểm các quân cờ được đánh giá theo kinh nghiệm và cho biết sự tương quan giữa các quân cờ. Vấn đề cải tiến hàm lượng giá sẽ được bàn riêng trong một chương phần sau. s: integer. "cao cờ" hơn và nhiều khi khắc phục được nhược điểm của cách tính đơn giản. ta có thể tăng thêm độ sâu tìm kiếm. Trong bàn cờ. 40. end. Do tính nhanh. Xe .4. Nói cho cùng. lúc khác lại có thể đến 85 do ở vị trí Pháo trống và đang đe doạ Pháo lồng). dù mất mọi quân hoặc đạt được thế cờ nào đi nữa đều không được mất Tướng vì nó dẫn đến thua cờ. Ví dụ như trong làng cờ Việt nam thường cho điểm các quân hơi khác (xem bài đọc dưới): Tốt . Là cách tính đơn giản nhất và nhanh nhất.5 9 Bạn cũng có thể theo bất kì thang điểm nào khác tuỳ theo hiểu biết và sở thích của mình. Tham khảo kiến thức cờ Tướng .40. Sĩ .

nó khống chế đến 2 hay 3 điểm trước mặt và bên cạnh (Tốt biên chỉ khống chế được 2). sức mạnh bằng nửa con Xe (nhất Tốt độ hà. hay chỉ phát huy ở mức rất hạn chế. Ngay bản thân mỗi quân cờ cũng không phải lúc nào sức mạnh cũng giữ nguyên như cũ. theo thời gian một ván cờ được chia thành ba giai đoạn: khai cuộc. bán Xa chi lực). Thế nhưng trong giai đoạn trung cuộc quân số của hai bên tương đối còn nhiều. có nhiệm vụ chính là bảo vệ an toàn cho Tướng. dọc tối đa đến 17 điểm. Tốt đối phương qua sông đổi được một Sĩ hoặc một Tượng. sức mạnh của chúng coi như tương đương nhau. không thể phát huy tính năng tác dụng được. còn đối với hai Pháo thì có thể coi là cân bằng. còn giá trị biến động được gọi là giá trị tương đối. Thế nhưng sự đánh giá nhận định này về sức mạnh của các quân không chính xác và cũng không đầy đủ. Nếu đem Xe đổi lấy một Pháo và một Mã của đối phương thì thấy có phần thiệt thòi một chút. còn Pháo thì nhanh nhẹn hơn. Trong ba giai đoạn này thì trung cuộc chiếm nhiều thời gian nhất và có ý nghĩa đến thắng thua nhiều nhất. nhiều khi Pháo bất lực. Pháo 7. đôi lúc chúng cũng trợ giúp các quân khác tấn công. Thông thường. không làm gì được để khống chế đối phương. Khi đứng ở vị trí tốt nó phát huy tính năng tác dụng tối đa khác hẳn với khi nó đứng ở vị trí xấu. Quân Mã trên bàn cờ có khả năng khống chế tối đa 8 điểm. Nên nhớ hai Tốt cặp kè nhau khi qua sông phải có giá trị hơn 4. Nếu so với hai Tốt đã qua sông cặp kè nhau thì năng lực của Mã không hơn bao nhiêu. Khi Tốt qua sông. Từ lâu. Còn Tướng. Mặt khác Pháo là quân có tính cơ động cao.5 là vừa. Do đó so với Mã thì phải thấy mỗi quân có một sở trường riêng. làng cờ đã có câu nhận định sức mạnh của các quân chủ lực là "Xe 10. nó có thể đi một bước vượt đến 9 tuyến đường. Do đó người ta đánh giá Sĩ hoặc Tượng có giá trị bằng một Tốt đã qua sông. Các nhà nghiên cứu lí luận về cờ đã trao đổi thống nhất với nhau bảng giá trị cơ bản của các quân như sau: Nếu lấy Tốt chưa qua sông làm chuẩn để xác định giá trị thì nó là quân kém năng lực nhất trên bàn cờ. nó khống chế ngang. Đối với Sĩ. Khi chưa qua sông nó chỉ kiểm soát hay khống chế mỗi một điểm trước mặt nó nên tạm cho nó có giá trị bằng 1. tính năng và tác dụng khác nhau nên sức mạnh của chúng cũng khác nhau. Trong giai đoạn trung cuộc. Vậy điều trước tiên chúng ta phải thấy mỗi quân cờ có hai giá trị: giá trị vốn có và giá trị biến động. giá trị của chúng cũng không giống nhau. Nhưng vì so sánh năng lực giữa Tượng và Sĩ thì thấy Tượng có phần đắc lực hơn nên định giá trị Sĩ là 2 thì Tượng phải là 2. Bây giờ nó mang giá trị tương đối.5 mới công bằng. dễ phát huy năng lực hơn nên giá trị cơ bản của Pháo phải hơn Mã một chút và bằng 5. Mã đi lại dễ bị cản trở. Pháo là binh chủng tác chiến tầm xa rất có hiệu quả. Giá trị vốn có thường được gọi là giá trị cơ bản (giá trị tĩnh). Đối với Xe thì rõ ràng là một binh chủng cơ động mạnh nhất. tức là bằng 2. Pháo khống chế tối đa chỉ 8 điểm nhưng nó còn khống chế được cả đường ngang. Do đó khái niệm giá trị của các quân cần được xác định rõ và hoàn chỉnh hơn.GIÁ TRỊ CỦA CÁC QUÂN CỜ Khái niệm giá trị của các quân cờ không phải là mới đối với những người chơi cờ từ các thế kỷ trước. Mã 3" hoặc đánh giá Xe là quân chủ lực mạnh nhất bằng câu "Nhất Xa sát vạn" hay đánh giá một Tốt qua hà. Tượng là loại binh chủng phòng ngự. Nhưng ở cự li gần. So ra thì Mã cũng mạnh đấy nhưng tác chiến ở tầm cự ly ngắn mà thôi. Mỗi bước nhẩy của nó vượt được hai tuyến đường ngang hay dọc. bản thân nó không có sức mạnh gì nhiều dù đôi khi nó cũng làm được nhiệm vụ trợ giúp . do đó định giá trị cơ bản của Mã 4. Trên đường dọc. Do đó các định giá quân cờ thường là cho giai đoạn này. phải thấy nó mạnh hơn lúc chưa qua sông nên giá trị lúc này của nó phải bằng 2. Chúng ta đã biết mỗi loại quân cờ hay mỗi binh chủng có đặc điểm. Do đó giá trị cơ bản của Xe là 10. trung cuộc và tàn cuộc.

Do đó cũng không nên định giá trị cơ bản của Tướng vì định cũng chẳng có ý nghĩa gì. ván cờ sẽ kết thúc ngay lập tức.9). UnMakemove. { phục hồi nước đi này} Lưu nước đi có điểm cao nhất. máy sẽ coi Tướng như một quân bình thường (dù có giá trị rất cao) và có thể đổi quân được . Để lưu các thông tin này. (Sưu tầm) 4. Ta buộc phải cắt nhánh ở những nơi Tướng đã bị mất (hình 2. do đó không cần phải đi thử tiếp và nhánh cây trò chơi bị cắt tại đây dù vẫn chưa đạt đến độ sâu tìm kiếm tối đa. . Tất cả các quân đều có thể đổi. end. Do đó hàm này cũng phải thay đổi giá trị các biến toàn cục về bên đi và đối thủ side và xside (việc thay đổi này là rất cần thiết để hàm Gen sinh nước đi cho đúng bên tới lượt). Việc khôi phục nước đi này nhờ gọi thủ tục UnMakemove. Cách đơn giản nhất . Cứ như vậy các hàm Makemove và UnMakemove có thể được gọi lồng nhau rất sâu. Khi đi thử. không dùng được do các thông tin phải lưu quá nhiều. Sau đó nó sẽ phục hồi lại nước đi này. Một vấn đề nẩy sinh ở đây là nếu một quân Tướng bị bắt thì có cần phải đi "thử" nữa không. Khi máy đi "thử" cho một bên một nước. có thể hi sinh nhưng riêng quân Tướng không thể đánh đổi mà cũng không thể hi sinh. Nó cũng tăng biến ply (độ sâu) thêm 1 do đã dấn sâu thêm một bước trên cây tìm kiếm.nó sẵn sàng để mất Tướng miễn là ngay sau đó ăn lại được Tướng đối phương (hình 2. Xử lí một nước đi "thử" Trong quá trình tính toán tìm nước đi tốt nhất cho mình. điểm đến và loại quân cờ mà nó bắt được (nếu có). Nếu mất một Tướng. Thực chất. hàm sử dụng một mảng hist_dat các bản ghi chứa thông tin đó và con trỏ hdp được tổ chức như một ngăn xếp.phục hồi diễn ra rất nhanh và không hiện ra màn hình nên máy có thể thử hàng chục triệu lần mỗi khi đến lượt.lưu lại cả bàn cờ. Để tính được điểm của nước đi vừa thực hiện. Còn nếu để máy đi thử tiếp. Thế nhưng giá trị cơ bản của nó thì vô cùng to lớn. chương trình thường xuyên thực hiện các câu lệnh dạng như sau: while tất cả các nước đi có thể có từ một thế cờ do begin Makemove(một_nước_đi). máy thường phải đi "thử" một nước rồi lại tính tiếp. ta chỉ cần lưu các thông tin bao gồm: điểm xuất phát của quân cờ. Như vậy. Do quá trình thử .8).cho các quân phe nó tấn công có kết quả. không một quân nào so sánh được. { Đi thử một nước } best := Tính điểm của nước đi thử đó. Hàm khôi phục nước đi thử UnMakemove có nhiệm vụ là làm mọi thứ ngược lại với hàm Makemove. nhiều khi máy lại phải thử đi tiếp một số nước nữa. Việc đi thử được thực hiện nhờ gọi hàm Makemove. thì nước sau sẽ đến lượt đối phương. vì mất Tướng là thua cờ. nghĩa là nước đi sau cùng sẽ được phục hồi trước nhất. hàm Makemove phải lưu lại các thông tin cần thiết cho việc khôi phục lại sau này.

hiển nhiên điểm sẽ rất cao (được 1000 điểm) và không cần phải tính điểm cho những nhánh con nữa. trả lại giá trị true nếu nước đi thử này lại ăn được Tướng đối phương. Phần chương trình đi thử có dạng như sau: . ta cần chuyển Makemove thành một hàm boolean. Nếu ăn được.Để xử lí những trường hợp này.

UnMakemove. i := gen_begin[ply]. Nước đi này có thể không hay do chấp nhận thua quá sớm như minh hoạ trên hình 2. Trong các câu lệnh trên có một chỗ hơi khó hiểu: tại sao ta lại cho nước ăn Tướng là 1000 . { Khởi đầu để lặp tất cả các nước } while (i < gen_end[ply]) and (best < beta) do begin if best > alpha then alpha := best. value: integer. var i. chỉ bỏ đi các tham số pos (thế cờ hiện tại . Hàm AlphaBeta của VSCCP về cơ bản là giống như vậy. Do đó máy sẽ chọn nhánh đầu tiên để đi. .ply mà không phải đúng 1000? Biến ply là biến chỉ độ sâu tìm kiếm. 5. Hàm trả về giá trị true nếu ăn được Tướng đối phương } best := 1000 . Nếu không có biến này và trong trường hợp Tướng (bên máy tính) sắp bị bắt dù đi bất cứ nước nào thì mọi nhánh cây đều trả về điểm -1000. best := -INFINITY.while tất cả các nước đi có thể có từ một thế cờ do begin if Makemove(một_nước_đi) then { Đi thử một nước.ply { Điểm của nước cờ nếu ăn được Tướng đối phương} else best := Tính điểm của nước đi thử. beta: integer. best. begin if depth = 0 then AlphaBeta := Eval else begin Gen. Tìm kiếm AlphaBeta Ta đã thấy chương trình mẫu của thủ tục AlphaBeta trong chương 1. { phục hồi nước đi này} Lưu nước đi có điểm cao nhất end. Máy nên chọn nước thua lâu nhất để hi vọng đối phương đi "nhầm" (còn nước còn tát) và đúng tinh thần chiến đấu hơn.10. depth: byte): integer. function AlphaBeta(alpha.ở đây là giá trị của hai mảng piece và color) ở một số hàm do đã biến đổi trực tiếp vào các mảng toàn cục piece và color (nhờ các hàm Makemove và UnMakemove mà ta đã đề cập đến ở trên) nên chúng chính là thế cờ đang xét.

m) then value := 1000-ply else value := -AlphaBeta(-beta. { Tìm nước đi tốt nhất cho máy } begin AlphaBeta (-INFINITY. . end. if value > best then begin best := value. Cách làm thì có nhiều. bấm phím Space hoặc Enter để chỉ mình sẽ đi quân này. ở đây chúng tôi giới thiệu phương pháp đơn giản nhất sử dụng hàm sẵn có: hàm Gen. function GetHumanMove: boolean. end. if ply = 0 then newmove := gen_dat[i]. y: byte = 5.đó chính là các nước đi hợp lệ. Nó gọi hàm này với các giá trị khởi đầu thích hợp để bắt đầu tìm kiếm. begin Gen. depth-1). máy sẽ chấp nhận nước đi đó và thoát khỏi hàm GetHumanMove. chuyển con trỏ đến chỗ mới và vẫn bấm phím Space hoặc Enter để chỉ đó là chỗ đến. t. 6. ta gọi nó để sinh ra tất cả những nước đi có thể có từ thế cờ hiện tại với bên đi là người . const x: byte = 5. Xử lí điều khiển của người chơi Người chơi dùng bàn phím để điều khiển các quân bên mình khi đến lượt: dùng các phím mũi tên điều khiển con trỏ màn hình chạy đến quân cờ thích hợp. var ch: char. end. Người chơi có thể ngừng chơi giữa chừng bằng cách bấm phím ESC. { while } AlphaBeta := best. Nếu không nó sẽ đòi người chơi phải chọn lại. Nước đi tốt nhất (điểm cao nhất) ở độ sâu 0 (ply = 0) được lưu vào một biến toàn cục newmove. y là toạ độ của con trỏ màn hình. Mỗi khi người chơi chọn một nước. end. i: integer. Máy sẽ chọn đi theo nước này. inc (i). Thủ tục gọi hàm AlphaBeta chỉ có các lệnh đơn giản như dưới. -alpha. Hàm cũng xử lí hai biến toàn cục x. máy sẽ kiểm tra xem nó có trong danh sách các nước đi hợp lệ không.MÁY TÍNH TÍNH NƯỚC ĐI *****) procedure ComputerThink.if MakeMove(gen_dat[i]. (***** THINK . { Dùng để kiểm tra nước đi hợp lệ của người chơi } GetHumanMove := false. Hàm xử lí điều khiển của người chơi GetHumanMove phải làm nhiệm vụ xử lí các phím điều khiển và trả về nước đi được chọn trong biến toàn cục newmove. selecting. Một chương trình chơi cờ tốt còn phải giúp đỡ người chơi không đi sai luật. Nếu có. Các nước đi không hợp lệ (dù cố ý hay vô tình) đều cần được phát hiện và ngăn cấm. end. from. Đầu tiên. selecting := 0. INFINITY.m. UnMakemove. MAX_PLY).

hiện lại quân xuất phát. Chương trình phải cập nhật tất cả các thông tin cần thiết liên quan đến nước đi và hiện hình những thay đổi. đã đến lúc máy hoặc người phải thực hiện một nước đi thực sự.m. 8. Vòng lặp chính xử lí trò chơi . Nó có khá nhiều chỗ giống như hàm Makemove nhưng đơn giản hơn.. if color[t]=side then begin from := t. Cập nhật một nước đi Sau khi "suy nghĩ" xong. end. y). newmove. ch := ReadKey. DrawCell(t. DrawCell(t. end else begin { Người chơi chỉ ra nước đi đến } if t <> from then DrawCell(from. ' ': begin { Đánh dấu/chọn một nước đi } t := x + (y-1)*SIZE_X. 7.SELECT). end. Nếu có sẽ thoát khỏi hàm này để đến lượt máy } for i := gen_begin[ply] to gen_end[ply]-1 do if (gen_dat[i].dest := t. NORMAL).from = from) and (gen_dat[i]. SELECT). if selecting=0 then begin { Người chơi đánh dấu } if color[t]=side then begin selecting:=1. để người chơi chọn nước đi khác } DrawCell(from..while true do begin MoveTo(x. from := t. { Nếu nước đó không hợp lệ. { Kiểm tra xem nước đi vừa chọn có trong danh sách các nước đi hợp lệ không. Việc này được thực hiện nhờ gọi hàm UpdateNewMove. Vòng lặp chính sẽ căn cứ vào giá trị chân lí này để ngừng chơi. SELECT). end.m. case ch of #13. end. Hàm cũng đồng thời sẽ kiểm tra tình trạng thắng cờ của bên vừa đi (ăn được Tướng đối phương) và trả về giá trị true nếu có một bên thắng. end else begin newmove. #27: { Xử lí các phím khác ở đây } .dest = t)then exit. Tham số quan trọng nhất mà nó quan tâm là nước đi được người hoặc máy chọn đi (đặt trong biến toàn cục newmove).from := from.

Từ đây ta tính được thời gian tính bằng giây nhờ chia cho 18. . Sau đây là những chỗ cần bổ xung để bạn có được các thông tin cần thiết.23 .xside. bạn có thể còn muốn hiện chú giải nước vừa đi của máy để dễ theo dõi và xem xét điểm đạt được.{ Thoát nếu ESC được bấm} side := xside...một thông tin vô cùng quan trọng. sửa từ nay về sau đều được đánh dấu bằng một mũi tên trắng cho dễ tìm. Cách lấy số liệu rất đơn giản. const . Hiện thông tin Đến đây. { Đổi bên tới lượt chơi } until UpdateNewMove. . số nhánh con được sinh ra mỗi lần gọi hàm Gen được đo bằng hiệu gen_end[ply]-gen_begin[ply]. Trong phần thân chương trình chính. Do đó hệ số phân nhánh được đo bằng cách lấy tổng các nhánh con này rồi chia cho số lần gọi hàm Gen (đo bằng biến gencount). ta sẽ bắt gặp vòng lặp repeat. hàm AlphaBeta. Var evalcount: longint.. Chú ý rằng. chuyển đối phương thành người đến lượt chơi. Khi biết được thời gian đã tiêu tốn có thể ước tính tốc độ lượng giá của máy bằng cách chia tổng số nút đã lượng giá cho thời gian tiêu tốn. Càng chơi lâu. đã đến lúc ta liên kết chúng lại thành một trò chơi hấp dẫn. Tuỳ theo bên tới lượt nó sẽ gọi hàm xử lí thích hợp cho người hoặc máy. ta khai báo một biến toàn cục evalcount và mỗi khi hàm Eval được gọi ta lại tăng giá trị của nó thêm một. kết quả sẽ càng chính xác. nên cách làm này chỉ cho biết con số ước lượng mà thôi. thêm một số lệnh và cuối cùng in chúng ra màn hình ở chỗ thích hợp. repeat if side=computerside then ComputerThink else if GetHumanMove then break. chương trình đã sẵn sàng đọ sức với bạn rồi đấy. Tất nhiên thời gian tiêu tốn này ngoài thời gian tiêu cho hàm lượng giá còn phải chi cho nhiều thứ khác như chạy các lệnh của hàm Gen.. sửa đổi về sau có thực sự hoạt động tốt không. Sau khi nhận kết quả nó lo cập nhật các số liệu cần thiết. hệ số phân nhánh của cây trò chơi. Để có được hệ số phân nhánh chính xác ta phải thống kê qua một số dữ liệu lớn. Hãy chú ý cách tính các con số này (chú ý số 65 là mã của chữ cái A.. gencount: longint = 0. khả năng xét thế cờ trung bình của máy là bao nhiêu cũng như nhiều số liệu khác. Một số thông tin rất khó lấy chính xác. Việc quan sát các số liệu này còn giúp ta biết các cải tiến.số xung trong mỗi giây.Sau khi có các hàm và thủ tục cần thiết. Ta cũng cần biết máy đã nghĩ trong bao lâu bằng cách khai báo các biến để tính thời gian. Vòng lặp này sẽ lặp mãi cho đến khi người chơi bấm phím ESC hoặc một bên thắng cờ.. Ngoài ra. ta sẽ tạm bằng lòng với những số liệu gần đúng vậy. xside := 3 . ta khai báo thêm một số biến toàn cục. { Các biến dùng để đo hệ số phân nhánh } brandtotal: longint = 0. 9 là số cột của một dòng bàn cờ). Để biết mỗi lần nghĩ. máy đã lượng giá bao nhiêu thế cờ . Những chỗ thêm. 9. Nhưng có thể bạn muốn biết thêm một vài số liệu như: thời gian mỗi lần máy nghĩ. số thế cờ nó phải tính giá trị. các dữ liệu đo được càng lớn. Thời gian được đo căn cứ vào số xung đồng hồ ở vị trí tuyệt đối $40:$6c trong bộ nhớ.

chr(((newmove. end. Với tốc độ trung bình xét được 55985 nút mỗi giây thì thời . 5). write('Thoi gian (giay): '..11). . gotoxy(50. function Eval: integer. best := AlphaBeta(-INFINITY. gotoxy(50. MAX_PLY). write('May tinh di : '. Nếu lấy tròn là 40 thì ta có bd = 404 = 2560000 (tức là số nút lá của cây trò chơi có khoảng trên hai triệu rưởi nút).gen_begin[ply]. ' '). SIZE_X-(newmove. evalcount*18. gotoxy(50..23:0:2. write('Toc do xet nut : '. tickstart. (tickend-tickstart)/18. 9). Hình 2. tickstart := systicks. procedure Gen. Nếu đi nhầm bạn không thể đi lại. begin inc(evalcount). ' '). Chạy thử Bây giờ bạn hãy dịch và chạy thử chương trình trên (toàn bộ nguồn trong Phụ lục A). 6)..systicks: longint absolute $40:$6C. Đó chính là số nút ta phải xét nếu dùng phương pháp tìm kiếm Minimax.from-1) mod SIZE_X)+65)..dest-1) mod SIZE_X)+65). Chương trình được chạy trên máy tính Pentium 166MHZ cài hệ điều hành Windows 98 và tìm kiếm đến độ sâu bằng 4. write('He so phan nhanh: '. tickend: longint. cẩn thận một chút thì có thể thắng được. gotoxy(50. evalcount.. begin evalcount := 0.11 là thông tin in ra màn hình sau khi máy đi nước đầu tiên. { Phục vụ hiện các thông tin theo dõi } tickend := systicks. write('Diem dat duoc : '. textcolor(7). end. best. write('So nut luong gia: '. { Biến để đo thời gian } . . { Tìm nước đi và hiện thông tin } var best: integer. Chương trình máy tính lại không "nhầm lẫn" và với độ sâu ngầm định 4 (tức là nghĩ trước tới 4 nước đi) nó có khả năng đánh bại những người chơi có trình độ trung bình và chơi ẩu. s: integer. Bạn hãy chịu khó suy nghĩ kĩ một chút. procedure ComputerThink. chr(((newmove. ' ').. inc(gencount). gotoxy(50.dest-1)div SIZE_X. 4). Các số liệu cho ta biết cây trò chơi có hệ số phân nhánh khoảng 41. write('Do sau : '. 7). brandtotal/gencount:3:2. INFINITY. gotoxy(50. SIZE_X -(newmove. Cẩn thận nhé. 8). brandtotal := brandtotal + gen_end[ply] . ' '). var i.from-1) div SIZE_X. 10. ' '). gotoxy(50.23/(tickend-tickstart+1):0:0.' '). MAX_PLY). ta chưa cài chức năng cho phép hoãn đi.

hãy thử tiếp với độ sâu 6. đây mới chỉ là phiên bản đầu tiên và độ sâu tìm kiếm còn hạn chế. số nút lượng giá. Độ sâu 1 2 3 4 5 6 7 Số nút lượng giá 44 877 24411 150480 5574794 62929742 723007130 Thời gian (giây) 0.00 0.0 11. các máy tính cỡ Pentium 166 MHZ sẽ mất đến. Ở đây.75 40. gần gấp 12 lần). Tuỳ theo sức mạnh máy tính (và cả khả năng chờ đợi).73 1031. thời gian máy "nghĩ" nước đầu tiên đã mất tới 1031 giây (hơn 17 phút. Còn độ sâu 7 tôi chỉ dám thử một lần vì thời gian tiêu tốn cho lần nghĩ nước đầu tiên mất đến.2 37.24 Hệ số phân nhánh 44. bạn sẽ thấy chương trình chơi càng bớt hớ hênh và càng khó thắng đấy. Còn số nút phải lượng giá là 62929742 (gấp 11 lần).. Các con số đều tăng khủng khiếp.. Ta hãy thử ước mơ nếu có được chiếc máy tính mạnh và chuyên dụng (cho cờ) như Deep Blue thì tính được đến đâu...15 39.. Bạn có thể thấy các con số cũng tăng rất nhiều nhưng dù sao chỉ tăng cỡ 11 lần. Số nút phải lượng giá là 5574794 nút (tăng 37 lần). Nếu ta cho rằng tăng một độ sâu thời gian tính toán chỉ dài thêm trung bình 10 lần thì chỉ cần đến độ sâu 11. ta đang dùng phương pháp tìm kiếm AlphaBeta.15. 4 năm để nghĩ nước đi đầu tiên (còn đến độ sâu 14 thì mất.00 43. bạn cần thận trọng kiểm tra chương trình. thời gian xét nước đầu tiên đã tăng từ 2.63 90.38 2.00 0. Trên máy tính của tôi.78 41.73 41. Nếu có điều gì khác có nghĩa là đã có sai sót trong khi nhập chương trình nguồn. Đừng ham tăng nhiều.86 Điểm Nước đi Số lần tăng số nút lượng giá 19. nghĩa là ít hơn 17 lần và tổng thời gian phải bỏ ra chỉ 2. máy tính đi (trừ những thông số liên quan đến thời gian do tuỳ thuộc loại máy bạn có) phải y hệt như hình 2.32 13355.40 40. Nếu máy tính của bạn là loại tốt. Loại máy lần đầu tiên thắng được Kasparov (năm 1997) có khả năng xét được . Bạn hãy thử kiểm nghiệm với độ sâu 5 xem sao. 3. 4223 năm).. Trên máy tính của tôi.63 giây thay cho 46 giây.gian để xét hết bằng phương pháp này sẽ mất khoảng 2560000/55985 » 46 giây. số nút phải xét chỉ còn 150480. điểm đạt được.7 giờ (trong khi qui định tổng cộng tất cả các nước của một đấu thủ chỉ được trong 1 giờ khi thi đấu). Nước đi Mã vào góc không được hay lắm . Nước được chọn đi hơi khác là B7C7 (có lẽ là nước tốt hơn). bạn hãy thử tăng độ sâu tìm kiếm bằng cách sửa khai báo hằng số MAX_PLY từ 4 lên 5 hoặc 6. hệ số phân nhánh. Càng tăng độ sâu. nếu tăng nhiều bạn sẽ không chờ nổi đâu.8 6.11.9 27.3 11.63 tới 88 giây (tăng 33 lần).5 40 -5 35 -5 10 -5 10 B7B0 B7B0 B7B0 B9A7 B7C7 B7C7 B7C7 Hình 2.nhưng không sao.11 Nếu bạn chưa hề thực hiện một cải tiến nào mà chỉ nhập nguyên vẹn chương trình mẫu từ Phụ lục A và dịch chạy thì các giá trị về độ sâu. Do bùng nổ tổ hợp. Hệ số phân nhánh được thống kê qua số lượng nhiều hơn đã tụt xuống còn 40.

Chú ý là các cấu trúc dữ liệu và các khai báo trong chương trình mẫu rất đơn giản nhưng cũng rất hiệu quả. 7. 3-5 hoặc 4-5) và thay đổi lượt đi trước của các bên. 3. Bạn cũng cần nắm vững cách lấy số liệu cũng như hiểu các số liệu này để tiếp tục theo dõi các cải tiến chương trình trong các chương sau. Toàn bộ mã nguồn chương trình cờ Tướng VSCCP phiên bản 1. Thay cho mảng một chiều biểu diễn bàn cờ. Chắc chắn trong quá trình chơi máy sẽ đi nhiều nước khá lạ lùng (trên quan điểm của người chơi bình thường). bạn hãy thử dùng mảng hai chiều (dạng piece[1. Bạn hãy tự cải tiến hàm lượng giá theo ý mình. Tuy nhiên.9] of byte). Nếu được nghĩ một nước trong 3 phút (thời gian được phép trung bình cho một nước) thì độ sâu có thể đến được sẽ khoảng từ 8 đến 10 (nếu không có cải tiến đặc biệt). Tại sao bạn lại phải chuyển sang bàn cờ mở rộng kích thước 13´14 (và để rồi lại phải chuyển về bàn cờ thường 9´10) để làm mỗi nhiệm vụ kiểm tra việc vượt quá biên cho nước cờ đang được sinh? Bạn có thể dùng trực tiếp bàn cờ mở rộng này để chơi được không (dùng trực tiếp bàn cờ 13x14)? 4. mảng color không thật cần thiết. Bên nào thắng? 6.. Tự mình khám phá các ưu. phức tạp và dễ sai nhất. nhược điểm của cách làm này. Một byte là quá đủ để lưu thông tin về quân cờ và bên chơi. 3-4. các chương trình cờ thuộc loại tốt nhất chạy trên các máy tính thông thường (loại PC và không gắn các mạch chuyên dụng) cũng chỉ tìm kiếm được đầy đủ đến độ sâu từ 5 đến 8 mà thôi (với giới hạn thời gian thi đấu bình thường). Nói cách khác.. Thử để máy đấu cả hai bên với độ sâu giống và khác nhau (ví dụ như 3-3. tỷ trọng thời gian không lớn so với các hàm khác và thời gian đo lại bao gồm tất cả mọi thứ nên các con số bị ảnh hưởng nhiều. GỢI Ý PHÁT TRIỂN 1. Tổng kết chương 2 Chương này đi sâu vào cách viết một chương trình cờ Tướng đơn giản. Dù vậy các cố gắng cải tiến bỏ ra cũng phải rất nhiều và rất lâu mới đạt được đến các độ sâu như vậy (bạn sẽ được nghiên cứu một vài cải tiến nhằm nâng độ sâu trong thời gian chơi hợp lí).. các thủ tục và hàm cơ bản. Thuật toán AlphaBeta tuy giống như đã đề cập đến trong chương 1 nhưng đến chương này bạn cần phải hiểu nó thật sự. nếu với cùng độ sâu thì tốc độ hiện tương đối ổn định. nhưng lại bỏ được mảng color.. Hãy thi đấu nhiều với máy. cách biểu diễn một bàn cờ. Bạn hãy sửa lại chương trình sao cho vẫn giữa nguyên khai báo của mảng piece. 3 giây. Hãy ghi lại những nước đi đó và hãy thử lí giải . Những điểm quan trọng trong chương là các khai báo các loại dữ liệu khác nhau. Nghĩa là cho máy có thể tự đấu.7 tiếng đồng hồ) sẽ được máy chiếu cố trong khoảng. Ví dụ hãy thử cho điểm các quân cờ tuỳ ý. 5. Chú ý là thông báo về tốc độ xét nước có thể rất khác nhau khi thay đổi độ sâu cực đại do hàm lượng giá dùng ở đây quá đơn giản. 2. Đến đây ta cũng có thể thấy.0 cho trong phần Phụ lục A. 4-4.trung bình 200 triệu (đôi khi lên đến 400 triệu) nước đi mỗi giây.10. Thủ tục Gen là lằng nhằng. 1. Với độ sâu 7 (mà máy tính của tôi phải ỳ ạch trong 3. Bạn hãy tự sửa chương trình cho phép cả hai bên đều là máy (hoặc đều là người) chơi.

12). M . B .Sĩ.Tướng. Bạn hãy cài thêm một chức năng nữa cho người chơi: chức năng "hoãn". sau hay ở giữa thì dùng các chữ viết tắt: t . Hãy cho phép người chơi ghi lại ván cờ đang chơi dở và lúc khác có thể nạp vào để chơi tiếp. Bình luận: Theo dõi một trận đấu bất kì. Ghi nước đi: Ta đã làm quen với cách ghi nước đi đơn giản ở trên. hoặc dẫn tới thắng hoặc tránh thua sau bao nhiêu nước. 11. 10. Ta cần ghi một ván cờ theo các qui định ghi chép biên bản ván đấu (do Liên đoàn cờ Việt nam đưa ra)... sẽ ăn được quân nào đó của đối phương hoặc tránh được mất quân. Nếu bạn đưa bản ghi một ván cờ như thế cho một người chơi cờ bình thường thì họ không hiểu. Bạn hãy cài chức năng "gợi ý" (hay "mách nước") cho người chơi biết nên đi nước nào bằng cách chỉ ra các nước đi chống cự tốt nhất. mỗi khi một đấu thủ đi một nước thì cố gắng giải thích lí do chọn nước đó.Mã. X . g .Xe. 9. s . 12.. S . Bầy xong mới thi đấu.trước.sau. phím gõ tắt.vì sao máy lại đi như vậy. thêm menu. Ví dụ như A1A6 nghĩa là quân cờ ở vị trí A1 sẽ chuyển đến vị trí A6. Kí hiệu đi quân: . T . Khảo sát kĩ và hãy thử cải tiến hàm lượng giá nếu máy phạm sai lầm nhiều.Pháo.Tốt (Binh). Các qui định này như sau: • Bàn cờ được tính theo cột (hình 2.giữa.. Hãy tham khảo các chương trình cờ có trên thị trường để làm cho tốt. • • • Kí hiệu quân cờ: Tg .Tượng. 13. đây không phải là cách ghi nước đi thường dùng trong "làng cờ". Bên nào tính theo cột bên đó. cài thêm chuột. nghĩa là bỏ nước vừa đi (hoặc một loạt nước vừa đi) và cho người chơi đi lại nước khác. P . Ví dụ giải thích nước đi đó dẫn tới thế cờ hay (nhờ điểm cao). 14. 8. Cách này xử lí thật đơn giản. Hãy tự cải tiến giao diện: dùng đồ hoạ. Tuy vậy. Để phân biệt các quân cùng loại đứng trước. Chú ý: hãy tận dụng tối đa những thủ tục có sẵn như UnMakemove. Bạn nên tận dụng những kết quả có sẵn trong lần máy nghĩ trước đó mà không cần phải tính lại. Cho phép người chơi được bầy cờ thế.

7 2. giao lưu đã hình thành hai loại cờ Tướng của phương Đông và cờ Vua của phương Tây cùng có những điểm giống nhau.trò chơi) có đặc điểm là quân cờ lập thể. Thi. Nhưng có thể khẳng định rằng cờ Tướng lúc đó mới có mô hình sơ khai chứ chưa phải loại cờ Tướng mà chúng ta chơi ngày nay. có một điều phần đông chuyên gia thừa nhận là: Cờ Tướng xuất xứ từ Trung Quốc. P2-5 M8. Vĩnh Xương. Tượng. khảo cứu. Pháo. Hai loại cờ này có nguồn gốc khác nhau nhưng lại có những điểm tương đồng như cách đi của một số quân.4 M2. +Thoái (lui): (/) gạch chéo. tượng trưng chứ không phải là quân voi (Tượng) trên bàn cờ. Cho tới đời Tống (năm 960 . trong khi cờ Vua lại có nguồn gốc từ một loại cờ cổ của Ấn Độ (xuất hiện vào khoảng thế kỉ thứ 6. Nhiều ý kiến cho rằng loại bàn cờ này rất hợp với các con số mà nhiều học thuyết thuộc nền văn minh Trung hoa thường đề cập đến như "Thái . +Bình (sang ngang): (-) dấu ngang (P2-5 nghĩa là Pháo 2 sang ngang đến cột 5). hiện nay vẫn còn đang tranh luận và tiếp tục được tìm tòi. M2. Tượng có nghĩa là hình tượng.6 có nghĩa Xe ở cột 2 tiến 6 ô. Ví dụ: Các nước đi dẫn đến thế cờ như hình 2. một loại cờ rất thịnh hành ở thời kì đó. Hoạ" thời Đường. Chú ý là nước tấn của Xe. Các nhà nghiên cứu đã bỏ ra nhiều công sức để tìm ra câu trả lời. Tại Uyên Ương trì. Ví dụ M7/8 nghĩa là Mã cột 7 thoái lui sang cột 8. bàn cờ có 8 ´ 8 = 64 ô vuông xen kẽ hai mầu trắng đen. Loại bàn cờ này đã được để lại trên các bức tranh dệt "Cầm. Dĩ nhiên không loại trừ trường hợp đã từng có những mô hình tượng trưng cho chiến trường cổ đại cùng được hình thành từ nhiều miền đất khác nhau và mang những đặc điểm riêng biệt.1 3. Còn M 2. được truyền bá sang Iran rồi sang châu Âu và phát triển thành cờ Vua như ngày nay). Tốt được ghi hơi khác với Mã. Đặc biệt cờ Tướng cổ đại không có quân Pháo. do sự hoà nhập.3 Bài đọc VÀI NÉT VỀ NGUỒN GỐC CỜ TƯỚNG Cờ Tướng ra đời ở đâu? Ở thời đại nào? Có liên quan gì đến cờ Vua? Đó là những câu hỏi mà người hâm mộ rất muốn tìm hiểu.12 (thế này có tên dài dòng là: Pháo đầu Xe tuần hà đối bình phong Mã cổ điển) được ghi như sau: 1. Ví dụ: X2. Các nhà nghiên cứu đều nhất trí là quân Pháo được bổ xung từ thời nhà Đường (sau năm 618) là quân cờ ra đời muộn nhất trên bàn cờ Tướng bởi cho tới lúc đó con người mới tìm ra vũ khí "Pháo" sử dụng trong chiến tranh . Tuy vậy cho tới nay. Điều lí thú nhất là theo các tài liệu lịch sử cờ Tướng ở thời Đường được gọi là Tượng hý (du hý . Trong một thời gian dài. Theo các văn kiện lịch sử và các văn vật được phát hiện thì cờ Tướng xuất hiện từ thời Chiến Quốc (từ năm 403 đến 221 trước công nguyên) mà tiền thân là "Lục bác kỳ".) dấu chấm. X2.+Tấn (tiến): (. Nếu đúng như vậy thì cờ Tướng đã có trên 2000 năm lịch sử. Trong quá trình tiến hoá của lịch sử. X1-2 X9-8 4. Kỳ. Cờ Tướng theo Hán văn gọi là Tượng kỳ. giống hệt bàn cờ Vua hiện nay. tỉnh Cam Túc (Trung Quốc) người ta cũng phát hiện dạng bàn cờ này trên các vật dụng bằng sứ cổ đại với 64 ô.3 có nghĩa là quân Mã đang ở cột 2 tiến sang cột 3).3 C3.đó là các loại máy móc thô sơ dùng để bắn các viên đá to. Ngược với tấn.1276) khi phát minh ra loại Pháo mới mang thuốc nổ thì quân Pháo mới được viết lại với bộ "hoả". Nguồn gốc cờ Tướng là vấn đề rất thú vị. quân Pháo trong chữ Hán viết với bộ "thạch".

Lục thập tứ ngao". theo Bách khoa toàn thư của nước Anh thì trước thế kỉ 13. Biết thế nhưng tại sao người châu Á chúng ta (và cả Việt nam) lại không thay đổi mà vẫn cứ dùng mãi những quân cờ chữ truyền thống đó cho tới tận hôm nay? Thực ra. Bởi xét cho cùng cờ Tướng cũng hay.Cực. (bạn có thể xem lại các quân cờ biểu tượng ở hình 2. cụ thể như ở cờ Vua.. người châu Âu sử dụng loại bàn cờ 64 ô cùng mầu. cũng hấp dẫn không kém cờ Vua. Bát quái... Trong khi đó. bài thơ "Tượng dịch" của thi sĩ Lưu Khắc Trang. dễ làm. Quân cờ truyền thống có cái tiện của nó: không cồng kềnh như cờ Vua.1. dễ mang đi. Nhưng cũng có thể đó là sự tự hào sâu xa của một nền văn hoá phương Đông: muốn có cái gì đó mang dấu ấn của riêng mình. Âm dương. Cũng có thể đối với người Á Đông cách suy nghĩ có phần sâu sắc hơn: đã cải cách thì ở nội dung là chính chứ không phải là hình thức. binh chốt có 5 quân đúng với luật "ngũ nhân vi ngũ" (năm người một đội ngũ). Điều này được ghi chép lại trong nhiều tác phẩm đời Tống như "Quảng tượng kì đồ" của Triều Vô Cửu.. Nếu chỉ cải cách hình thức mà không cải cách nội dung thì cải cách đó cũng không cần thiết lắm. CÂU CHUYỆN VỀ HÌNH CÁC QUÂN CỜ TƯỚNG Người châu Âu (và cả nhiều người châu Á) học chơi cờ Tướng rất khó khăn do không nhớ nổi mặt chữ của quân cờ.. "Đả mã đồ kinh" của Lý Thanh Chiếu. Cuốn kỳ phổ đầu tiên về cờ Tướng hiện đại là "Sự Lâm Quảng ký" của Trần Nguyên Tĩnh ở thời kì cuối đời Tống (cách đây hơn 700 năm) trong khi tác phẩm đầu tiên về cờ Vua được xuất bản tại Tây Ban Nha vào năm 1495.. Vì sao vậy? Có lẽ thói quen đã ăn sâu vào đầu óc những người chơi cờ. rất giản dị. không có hình tượng rõ ràng. (Trích từ báo Người Chơi Cờ ) . có vệ sĩ túc trực. cờ Vua ra sao? Cho tới nay các nhà nghiên cứu chưa có đồng luận điểm. Các chữ tượng hình này trông khá lằng nhằng rắc rối (tôi chắc bạn không thể đảm bảo vẽ lại được toàn bộ các quân cờ mà không nhìn mẫu). Như vậy lịch sử cờ Tướng. quân cờ lập thể ở hình dưới) và thậm chí đã có nơi sản xuất các quân cờ theo kiểu cờ Vua cải biên rồi. Lưỡng Nghi. đã nhiều lần người ta muốn đổi và thử đổi. hỏng dễ thay. khiến người ta khó tiếp nhận một sự cải tiến hoặc cải cách. Pháo có hình khẩu thần công. Tứ Tượng. Mấy năm gần đây ở một số giải đấu người ta đã công bố các quân cờ có dạng biểu tượng như Mã hình đầu ngựa. Đơn thuần chỉ là các chữ Hán. Nhưng có một điều có thể gọi là chung: Cờ Tướng hiện đại được hoàn chỉnh vào đời Tống. không muốn có sự lai căng bắt chước từ bên ngoài. Thế nhưng "đâu lại hoàn đấy": các giải cờ Tướng trong từng quốc gia cũng như các giải thế giới toàn chơi bằng các quân cờ truyền thống dùng các chữ Hán quen thuộc. quân cờ rất hợp với cơ chế quân sự thời đó: Tướng soái ở trong dinh chỉ huy. Các quy định về bàn cờ. bình dân và gần gũi với mọi người.

Sign up to vote on this title
UsefulNot useful