You are on page 1of 39

TRƯỜNG ĐẠI HỌC ĐIỆN LỰC

KHOA CÔNG NGHỆ THÔNG TIN

BÁO CÁO CHUYÊN ĐỀ HỌC PHẦN


NHẬP MÔN TRÍ TUỆ NHÂN TẠO

ĐỀ TÀI:
Áp dụng thuật toán minimax và cắt tỉa alpha vào trò chơi cờ tướng

Sinh viên thực hiện : LƯƠNG NGỌC HẢI


NGUYỄN TIẾN ĐẠT

Giảng viên hướng dẫn : PHẠM ĐỨC HỒNG

Ngành : CÔNG NGHỆ THÔNG TIN

Chuyên ngành : CÔNG NGHỆ PHẦN MỀM

Lớp :D14CNPM6

Khóa : D14

Hà Nội, tháng 12 năm 2021


PHIẾU CHẤM ĐIỂM

STT Họ và tên Nội dung thực hiện Điểm Chữ


sinh viên ký

1 Nguyễn Tiến
Đạt-
19810310528

2 Lương Ngọc
Hải-
19810310309

Họ và tên giảng viên Chữ ký Ghi chú

Giảng viên chấm 1:

Giảng viên chấm 2:


MỤC LỤC
LỜI CẢM ƠN................................................................................................................... 1
LỜI NÓI ĐẦU.................................................................................................................. 2
MỞ ĐẦU........................................................................................................................... 3
I. Đặt vấn đề...................................................................................................................3
CHƯƠNG 1: THUẬT TOÁN MINIMAX VÀ CẮT TỈA ALPHA...............................4
1.1 Đặt ý tưởng..............................................................................................................4
1.2 Thuật toán MiniMax và AlphaBeta..........................................................................5
1.2.1 Thuật toán MiniMax..........................................................................................5
1.2.2 Thuật toán cắt tỉa AlphaBeta...........................................................................12
Chương 2: PHÂN TÍCH VÀ LẬP TRÌNH GAME.....................................................21
2.1 Mô tả ý tưởng.........................................................................................................21
2.2 Xây dựng chương trình...........................................................................................21
2.2.1 Lượng giá........................................................................................................21
2.2.2 Sinh nước đi....................................................................................................26
2.2.3 Giao diện chương trình....................................................................................32
2.3 Cài đặt chương trình...............................................................................................32
CHƯƠNG 3 : KẾT LUẬN.............................................................................................33
3.1 Các vấn đề khó khăn và cách thức giải quyết.........................................................33
3.2 Đánh giá chương trình............................................................................................33
3.3 Hướng phát triển....................................................................................................33
TÀI LIỆU THAM KHẢO.............................................................................................34
LỜI CẢM ƠN
Trong thời gian nghiên cứu và học tâ ̣p môn Nhập môn Trí tuệ nhân tạo(Với
ngôn ngữ lâ ̣p trình java), môn đồ họa máy tính cùng với viê ̣c tìm hiểu và đọc các
tài liê ̣u trên thư viê ̣n Trường và viê ̣c tìm hiểu thêm tài liê ̣u học tâ ̣p trên internet
nhóm em đã gă ̣p không ít khó khăn về viê ̣c xây dựng và thiết kế game Cờ tướng
sử dụng ngôn java ứng dụng đồ họa máy tính và kiến thức lập trình hướng đối
tượng. Với sự giúp đỡ quý báu của các thầy cô giáo và các bạn nhóm em đã hoàn
thành game và bài báo cáo môn học Xây game cờ tướng
Đồng thời em xin gửi lời cảm ơn đă ̣c biê ̣t về sự hướng dẫn và chỉ bảo nhiê ̣t tình
của thầy giáo Phạm Đức Hồng đã tâ ̣n tình giúp đỡ nhóm em trong suất quá trình
hoàn thành game cũng như bài báo cáo này.
Tuy nhiên, kinh nghiê ̣m còn thiếu nên trong game cũng như bài báo cáo này
chắc chắn sẽ không tránh khỏi những thiếu sót, hạn chế nhất định. Những ý kiến
nhâ ̣n xét và góp ý quý báu của thầy cô và các bạn là cơ sở để nhóm em học hỏi
thêm và hoàn thiê ̣n thêm kiến thức và củng cố thêm kinh nghiê ̣m của bản thân
mình. Nhóm em rất mong nhâ ̣n được sự nhâ ̣n xét và góp ý từ thầy cô và các
bạn.Mặc dù đã rất nỗ lực và cố gắng nhưng chắc chắn rằng đề tài vẫn còn nhiều
thiếu sót,nhóm em mong sẽ nhận đuợc sự góp ý, phê bình của các thầy cô, và các
bạn để đề tài hoàn thiện hơn.

Em xin chân thành cảm ơn!

1
LỜI NÓI ĐẦU
Các chiến lược tìm kiếm cơ bản chỉ sử dụng các thông tin chung của bài toán, nó
không phù hợp với nhiều bài toán thực tế trong cuộc sống vì chúng đòi hỏi quá
nhiều về thời gian và bộ nhớ. Bên cạnh đó, chúng ta ngày càng đặt ra những bài
toán trong thực tế, nhằm giải quyết được các vấn đề, nhu cầu của con người, với
một lượng thông tin, dữ liệu khổng lồ, cần phải đưa ra những chiến lượng giải
quyết tối ưu, thông mình để đạt hiệu quả hơn, vậy nên cần nghiên cứu, thiết lập và
cải thiện những chiến lược tìm kiếm với tri thức bổ sung (informed search
strategies) sử dụng các tri thức cụ thể của bài toán.Trong đồ án này, nhóm sinh
viên chúng em đã chọn ra một trong các giải thuật tìm kiếm để mô phỏng vào một
bài toán thực tế đó là chương trình “Cờ tướng”, một trò chơi rất quen thuộc với
chúng ta.Với mục tiêu đặt là có thể hiểu và vận dụng được giải thuật tìm kiếm tri
thức, và củng cố được kĩ năng lập trình để tạo nên bản demo hoàn chỉnh.
Quá trình thực hiện báo cáo gặp nhiều khó khăn vì ngôn ngữ lập trình mới, kinh
nghiệm thực tế còn hạn chế, em mong sẽ nhận được sự góp ý, phê bình của cô để
đề tài này hoàn thiện hơn.

Em xin chân thành cảm ơn sự giúp đỡ của Thầy!

2
MỞ ĐẦU
I. Đặt vấn đề
Game cờ tướng đã xuất hiện từ rất lâu đời, được nhiều các thế hệ yêu thích
bởi việc chơi cờ cũng như việc cần quân đánh trận. Hiện nay, Cờ Tướng trở thành
một trò chơi trí tuệ mang tầm cỡ quốc tế.
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.

3
CHƯƠNG 1: THUẬT TOÁN MINIMAX VÀ CẮT TỈA ALPHA
1.1 Đặt ý tưởng
Cờ tướng là trò chơi đối kháng, trong đó hai người luôn phiên nhau đi nước
đi của mình. Trạng thái bắt đầu là trạng thái khởi tạo bàn cờ, sau mỗi nước đi của
mô ̣t bên, trạng thái bàn cờ sẽ được thay đổi thành mô ̣t trạng thái mới hiê ̣n hành. Cờ
tướng có luâ ̣t của nó, và trò chơi sẽ kết thúc khi mô ̣t người có được trạng thái phản
ánh sự thắng cuô ̣c hoă ̣c hai người rơi vào trạng thái hòa cờ. Ta tìm cách phân tích
xem từ mô ̣t trạng thái nào đó sẽ dẫn đến đấu thủ nào sẽ thắng với điều kiê ̣n cả hai
có trình đô ̣ như nhau. Giải thuâ ̣t Minimax sẽ được áp dụng vào trong trò chơi cờ
tướng. Hai đấu thủ trong trò chơi sẽ được gọi là MIN và MAX và hai đấu thủ đều
biết rõ thông tin trên bàn cờ như nhau. 
- MAX đại diê ̣n cho đấu thủ quyết dành thắng lợi hoă ̣c tối đa hóa ưu thế của mình.
 - MIN ngược lại, cố gắng tối thiểu hóa điểm số của MAX.

Bàn cờ tướng cũng chính là mô ̣t không gian trạng thái với các mức và được
biểu diễn bằng cây trò chơi. Mỗi nút của cây biểu diễn cho mô ̣t trạng thái trên bàn
cờ. Nút gốc biểu diễn trạng thái bắt đầu ván cờ. Các nút lá thể hiê ̣n trạng thái kết
thúc của trò chơi (khi mô ̣t đấu thủ giành phần thắng, thua, hay hai đấu thủ hòa
nhau).
Hai đấu thủ được gọi là MAX và MIN và luân phiên đi nước cờ của mình nên
mỗi mức trên cây được biểu diễn luân phiên là MAX và MIN. Các nút ứng với
trạng thái mà từ đó đấu thủ MAX chọn nước đi ứng với lớp MAX, các nút mà đấu
thủ MIN chọn nước đi ứng với lớp MIN. Với mỗi nước đi trong bàn cờ, tương ứng
với các mức trên cây, giải thuâ ̣t minimax sẽ định giá trị cho các nút như sau:
-         Nếu nút là nút lá gán cho nút đó mô ̣t giá trị để phản ánh trạng thái
thắng, thua hay hòa của các đấu thủ
-         Sử dụng giá trị các nút lá để xác định giá trị của các nút ở mức trên
nó:
+ Nút thuô ̣c lớp MAX gán cho nó giá trị lớn nhất trong các nút lá.
+ Nút thuô ̣c lớp MIN gán cho nó giá trị nhỏ nhất trong các nút lá.
Gán giá trị cho từng thế cờ theo quy tắc trên chỉ rõ giá trị của trạng thái tốt
nhất mà mỗi đấu thủ hi vọng đạt được. Cũng giống như viê ̣c tính toán nước đi và
thực hiê ̣n mô ̣t nước cờ thực sự của đấu thủ. Giải thuâ ̣t Minimax thể hiê ̣n viê ̣c tính
toán nước đi tối ưu cho các đấu thủ thông quá giá trị của các nút được gán. Trong
mô ̣t nước cờ, khi đấu thủ MAX đến lượt đi, đấu thủ này sẽ chọn nước đi ứng với
trạng thái có giá trị cao nhất trong các trạng thái con, còn đấu thủ MIN sẽ chọn mô ̣t
nước đi ứng với trạng thái có giá trị nhỏ nhất trong các trạng thái con.
Tuy nhiên, cờ tướng là mô ̣t trò chơi có thể nói là phức tạp, viê ̣c mở rô ̣ng
không gian trạng thái khí áp dụng giải thuâ ̣t Minimax có thể gă ̣p khó khăn. Vì thế

4
mà chúng ta chỉ xét đến viê ̣c triển khai giải thuâ ̣t Minimax với  trò chơi cờ tướng
có mức đô ̣ sâu được định trước (khoảng 5 mức). Viê ̣c định trước mức hay đô ̣ sâu
sẽ làm giảm thời gian tính toán cho giải thuâ ̣t và AI (máy) sẽ đưa ra được mô ̣t
nước đi nhanh hơn và chính xác hơn. Phần sau đây nhóm em sẽ trình bày về quá
trình xây dựng giải thuâ ̣t Minimax.

1.2 Thuật toán MiniMax và AlphaBeta


1.2.1 Thuật toán MiniMax
1.2.1.1 Mô tả

5
Giả sử chúng ta có một bộ phân tích thế cờ có thể áp dụng tất cả các luật,
các phương pháp đánh cờ khác nhau vào từng thế cờ và chuyển đổi chúng thành
một con số đại diện (cho điểm thế cờ). Mặt khác, ta giả sử con số đó là dương
khi áp dụng cho thế cờ của một đấu thủ (được gọi là người chơi cực đại -
maximizer), và là âm khi áp dụng cho đấu thủ bên kia (được gọi là người chơi
cực tiểu - minimizer). Quá trình tính toán cho điểm thế cờ được gọi là lượng
giá tĩnh (static evaluation). Hàm thực hiện việc tính toán được gọi là một bộ
lượng giá tĩnh, và giá trị nhận được gọi là điểm lượng giá tĩnh. Cả hai đấu thủ
đều cố gắng đi như thế nào đó để đạt được điểm tuyệt đối lớn nhất. Người chơi

cực đại sẽ tìm những nước đi dẫn đến điểm của mình trở nên lớn hơn (hay cao
nhất có thể được) hay điểm của đối thủ bớt âm hơn (nhỏ hơn về giá trị tuyệt
đối). Còn đấu thủ của anh ta, người chơi cực tiểu, lại ra sức phản kháng lại, để
dẫn tới điểm âm của anh ta bé hơn hay điểm dương của đối thủ nhỏ đi (hình 1).

6
Ví dụ một phần cây trò chơi trong hình 2

Người chơi cực đại hi vọng chọn nước đi bên phải để đạt được điểm 8.
Thế nhưng nếu đi như vậy thì khi đến lượt đi của người chơi cực tiểu, anh ta sẽ
cố gắng không cho người chơi cực đại đạt được điểm này bằng cách chọn nước
đi nhánh bên trái và như vậy, người chơi cực đại chỉ được có 1 điểm thay vì 8.
Ngược lại, nếu người chơi cực đại chọn nước đi bên trái, thì trong tình huống
xấu nhất anh ta vẫn còn được 2 điểm, lớn hơn là chọn nước đi bên phải. Nói
chung, người chơi cực đại sẽ phải tìm cách nhận ra các nước đi của đối phương
tiếp theo làm cho điểm giảm xuống. Và tương tự như vậy, người chơi cực tiểu
phải nhận biết được nước đi của người chơi cực đại cố gắng làm tăng điểm lên.
Thủ tục tìm nước đi tốt nhất trên cây trò chơi như trên được gọi là thủ tục
Minimax do điểm ở mỗi nút có thể là điểm cực đại hoặc có thể là điểm cực tiểu
và có thuật toán như sau:

7
Thuật toán Minimax
Nếu như đạt đến giới hạn tìm kiếm (đến tầng dưới cùng của cây tìm kiếm),
tính giá trị tĩnh của thế cờ hiện tại ứng với người chơi ở đó. Ghi nhớ kết quả
Nếu như mức đang xét là của người chơi cực tiểu, áp dụng thủ tục Minimax
này cho các con của nó. Ghi nhớ kết quả nhỏ nhất
Nếu như mức đang xét là của người chơi cực đại, áp dụng thủ tục Minimax
này cho các con của nó. Ghi nhớ kết quả lớn nhất.

1.2.1.2 Xây dựng chương trình cho thuật toán Minimax


Dựa vào phát biểu trên để viết chương trình cho thuật toán này bằng ngôn
ngữ tựa Java. Đây là một phương thức có tên là Minimax và sẽ là loại đệ qui.
Trước hết, để phương thức này biết đã đạt đến giới hạn tìm kiếm chưa, ta cần
cung cấp cho nó một tham số về độ sâu tìm kiếm depth (để biết phải tìm đến
đâu). Giá trị trả về của hàm chính là điểm của thế cờ (bàn cờ) pos.
Mỗi khi Minmax được gọi, nó sẽ càng gần đến giới hạn tìm kiếm, do đó
ta sẽ gọi hàm này với độ sâu bằng độ sâu cũ trừ đi một. Đạt đến độ sâu giới hạn
chính là khi depth = 0. Khi đạt độ sâu này ta sẽ gọi phương thức lượng giá Eval
để đánh giá chất lượng của thế cờ pos hiện tại (thực hiện điều một của thuật
toán). Như vậy bước đầu phương này có dạng sau:
Public int MinMax (int pos, int depth){
   if (depth == 0) //Đã đạt đến giới hạn
Return Eval (pos)  //Tính giá trị thế cờ pos
else{
      ... 
      MinMax (pos, depth - 1); //Gọi đệ qui với độ sâu giản
dần
       ... 
  
}

Ở trên, Minmax được gọi với độ sâu giảm đi một. Đó là độ sâu của các
thế cờ là con. Các thế cờ con pos' đó là các thế cờ được tạo ra từ pos bằng cách
đi một nước đi hợp lệ m nào đó. Do đó ta phải có các lệnh thực hiện đi quân để
đến các thế cờ mới. Để biết từ thế cờ pos có thể đi được những nước nào, ta
dùng một thủ tục Gen có tham số là thế cờ cha pos. Thủ tục này sẽ cất các thế
cờ con pos' đó vào bộ nhớ (dạng danh sách). Việc tiếp theo là ta lấy từng thế cờ
đó ra và áp dụng tiếp thủ tục Minimax cho nó để tính điểm value của nó.
8
Vậy phương thức MinMax có dạng:
public int MinMax (int pos, int depth){
   if (depth == 0)
      return Eval (pos) // Tính giá trị thế cờ pos
   else{
      Gen (pos); //Sinh ra mọi nước đi từ thế cờ pos
      while (còn lấy được một nước đi m){
         pos = Tính thế cờ mới nhờ đi m;
         value = Minimax (depth-1); //Tính điểm của pos
          ...
      } 
      ... 
   } 
}
Theo phát biểu của thuật toán, ta thấy các điều 2 và 3 chỉ khác nhau ở
cách chọn kết quả tốt nhất best phụ thuộc vào người chơi đang là người chơi
cực đại hay cực tiểu. Cuối cùng thuật toán sẽ trả về điểm tốt nhất đạt được. Vậy
hàm này được phát triển tiếp thành:
public int MinMax (int pos, int depth){ 
   if (depth == 0){
      return Eval (pos) //Trả về giá trị thế cờ pos
}
   else{ 
      Gen (pos); //Sinh ra mọi nước đi từ thế cờ pos
      while (còn lấy được một nước đi m)
      {
          pos = Tính thế cờ mới nhờ đi m;
          value = Minimax (pos, depth-1); //Tính điểm của pos
// Chọn điểm tốt nhất tuỳ thuộc theo người chơi
         if (người chơi là người cực đại) {
             if (value > best) best = value; 
        }

return best; // Trả về giá trị tốt nhất

Thông thường để cho tiện (và cũng rất gần sự thực) ta coi cả hai người
chơi (hai bên) có cùng cách đánh giá về một thế cờ. Có điều thế cờ này là tốt
với một người thì phải được đánh giá là tồi với người kia và ngược lại. Trong
máy tính cách thể hiện tốt nhất là ta cho điểm một thế cờ có thêm dấu âm

9
dương: dấu dương dành cho người chơi cực đại và dấu âm cho người chơi cực
tiểu. Với người chơi cực đại sẽ mong muốn điểm này càng dương càng tốt, còn
người chơi cực tiểu lại mong muốn điểm này càng âm càng tốt. Do đó để dễ xử
lí ta sẽ tuỳ theo mức người chơi mà đổi dấu giá trị đánh giá thế cờ pos. Chú ý
rằng, thay đổi độ sâu là chuyển sang đối phương nên phải đổi dấu. Chương
trình thực hiện đổi dấu như sau:

value = -Minimax (depth-1); //Tính điểm của pos

Cũng do dùng cùng hàm lượng giá nên khi đến lượt người chơi cực đại
và cực tiểu có cùng cái nhìn như nhau về một thế cờ. Điều này dẫn đến có thể
dùng cùng cách chọn nước đi tốt nhất cho họ (gộp được điều 2 và 3 lại với nhau
được). Giá trị best cần được khởi đầu rất nhỏ để đảm bảo không vượt mọi giá trị
value, tốt nhất là giá trị - vô cùng:
Public int MinMax (int pos, int depth){ 
   if depth = 0 then 
   return Eval (pos); //Trả về giá trị thế cờ pos
   else{ 
      best = -INFINITY; 
      Gen (pos); //Sinh ra mọi nước đi từ thế cờ pos
      while (còn lấy được một nước đi m)
      {
         pos = Tính thế cờ mới nhờ đi m;
         value = -MinMax (pos, depth - 1);
         if (value > best) best = value; 
      } 
      return best; 
   } 

Thông thường, bàn cờ được biểu diễn bằng các biến toàn cục. Do đó thay
cho truyền tham số là một bàn cờ mới pos vào thủ thục Minimax thì người ta
biến đổi luôn biến toàn cục này nhờ thực hiện nước đi "thử" (nước đi dẫn đến
bàn cờ mới pos). Sau khi Minimax thực hiện việc tính toán dựa vào bàn cờ lưu
ở biến toàn cục thì thuật toán sẽ dùng một số thủ tục để loại bỏ nước đi này.
Minimax bỏ các tham số pos và được xây dựng hoàn chỉnh như sau:
public int MinMax(int depth) {
if (!run) {
return -1;
}

10
int best, i, value;
if (depth == 0) {
return Eval();
} else {
best = -INFINITY;
Gen();
i = gen_begin[ply];
while (i < gen_end[ply]) {
makeMove(gen_dat[i].m);
value = MinMax(depth - 1);
unMakeMove();
if (value > best) {
best = value;
if (ply == 0) {
newMove = gen_dat[i].m;
}
}
++i;
}
return best;
}
}
1.2.1.3 Đánh giá
Nếu hệ số nhánh trung bình của cây là b và ta thực hiện tìm kiếm đến độ
sâu d thì số nút phải lượng giá ở đáy cây như ta đã biết là bd. Đây chính là số đo
độ phức tạp của thuật toán. Nếu b = 40, d = 4 (các con số thường gặp trong trò
chơi cờ) thì số nút phải lượng giá là 404 = 2560000 (trên 2 triệu rưỡi nút). Còn
với b = 40, d = 5 thì số nút phải lượng giá sẽ tăng 40 lần nữa thành 40 5 =
102400000 (trên 102 triệu nút).
Lưu ý: toàn bộ ý tưởng của thuật toán này là dựa trên việc chuyển đổi
mỗi thế cờ thành một con số để đánh giá. Rất tiếc là các con số này thường
không tốt và không đủ để đánh giá hết mọi điều. Mặt khác, thuật toán này có
thể rất tốn kém (chạy chậm) do việc sinh các nước đi và lượng giá rất tốn thời
gian tính toán, do vậy độ sâu của cây trò chơi cũng bị hạn chế nhiều. Ta cần có
thêm những cải tiến để cải thiện tình hình.
1.2.2 Thuật toán cắt tỉa AlphaBeta
1.2.2.1 Mô tả
Thủ tục AlphaBeta là một cải tiến thuật toán Minimax nhằm tỉa bớt
nhánh của cây trò chơi, làm giảm số lượng nút phải sinh và lượng giá, do đó có
11
thể tăng độ sâu của cây tìm kiếm. Giả sử hình 3 là một thế cờ mà hai nút đầu
tiên đã được lượng giá. Nếu thực hiện thủ tục Minimax đối với các nút đó sẽ
cho thấy người chơi cực đại đã được đảm bảo nếu đi nước bên trái sẽ được ít
nhất là 2 điểm dù là các lượng giá của các nút khác cho kết quả như thế nào đi
nữa. 

12
13
Bây giờ, ta lại giả sử nút tiếp theo được lượng giá và cho kết quả là 1. Nếu đi
vào nhánh này thì đối phương sẽ đảm bảo làm điểm của người chơi cực đại không
thể vượt quá được giá trị 1 dù là các lượng giá của các nút khác cho kết quả như
thế nào đi nữa. Do đó đến đây, nước đi tốt nhất là chọn nước đi bên trái với đảm
bảo là ít nhất đạt được 2 điểm. Và do đó, hoàn toàn không cần thiết phải lượng giá
nút còn lại.

14
Nguyên tắc Alpha-Beta
Nếu biết điều đó thật sự tồi thì đừng mất thời gian tìm hiểu nó sẽ tồi tệ đến
đâu
Ý tưởng này được gọi là nguyên tắc Alpha-Beta do nó dùng trong thủ tục
AlphaBeta (ta sẽ xét dưới đây). Hai tham số của thủ tục này (theo các đặt tên
truyền thống) được gọi là alpha và beta và dùng để theo dõi các triển vọng -
chúng cho biết các giá trị nằm ngoài khoảng [alpha, beta] là các điểm "thật sự
tồi" và không cần phải xem xét nữa. Khoảng [alpha, beta] còn được gọi là cửa
sổ alpha, beta. Trong ngữ cảnh của các trò chơi, nguyên tắc Alpha-Beta nói
rằng, mỗi khi xem xét một nút bất kì, nên kiểm tra các thông tin đã biết về các
nút cha, ông của nó. Rất có thể do có đủ thông tin từ cha, ông nên không cần
phải làm bất cứ việc gì nữa cho nút này. Cũng vậy, nguyên tắc này cũng giúp
chỉnh sửa hoặc xác định chính xác giá trị tại nút cha, ông nó. Như trên nói, một
cách để tiện theo dõi quá trình tính toán là dùng các tham số alpha và beta để
ghi lại các thông tin theo dõi cần thiết. Thủ tục AlphaBeta được bắt đầu tại nút
gốc với giá trị của alpha là -vôcùng và beta là +vôcùng. Thủ tục sẽ tự gọi đệ
quy chính nó với khoảng cách giữa các giá trị alpha và beta ngày càng hẹp hơn.

15
Thuật toán AlphaBeta
 Nếu mức đang xét là đỉnh (gốc cây), đặt giá trị của alpha là
-vôcùng và beta là +vôcùng

*  Nếu như đạt đến giới hạn tìm kiếm (đến tầng dưới cùng của cây
tìm kiếm), tính giá trị tĩnh của thế cờ hiện tại ứng với người chơi
ở đó. Ghi lại kết quả

*  Nếu như mức đang xét là của người chơi cực tiểu.
o Thực hiện các công việc sau cho đến khi tất cả các con
của nó đã được xét với thủ tục AlphaBeta hoặc cho đến
khi alpha là bằng hoặc lớn hơn beta.
- Áp dụng thủ tục AlphaBeta với giá trị alpha
và beta hiện tại cho một con. Ghi nhớ lại kết
quả.
- So sánh giá trị ghi nhớ với giá trị beta, nếu giá
  trị đó nhỏ hơn thì đặt beta bằng giá trị mới
này.
o Ghi nhớ lại beta

*  Nếu như mức đang xét là của người chơi cực đại,
o Thực hiện các công việc sau cho đến khi tất cả các con
của nó đã được xét với thủ tục AlphaBeta hoặc cho đến
khi alpha là bằng hoặc lớn hơn beta.
- Áp dụng thủ tục AlphaBeta với giá trị alpha
và beta hiện tại cho một con. Ghi nhớ lại kết
quả.
- So sánh giá trị ghi nhớ với giá trị alpha, nếu
giá trị đó lớn hơn thì đặt alpha bằng giá trị
mới này.
o Ghi nhớ lại alpha.   

16
1.2.2.2 Xây dựng chương trình cho thuật toán AlphaBeta
Từ phát biểu trên ta sẽ xây dựng hàm AlphaBeta bằng ngôn ngữ tựa
Pascal. Hàm này sẽ có dạng khai báo như dưới, trong đó depth là độ sâu tìm
kiếm, INFINITY là giá trị vô cùng, thuật toán tính toán dựa trên thế cờ hiện tại
pos là các biến toàn cục:
public int AlphaBeta(int alpha, int beta, int depth) {
if (!run) {
return 0;
}
int best, value, i;
if (depth == 0) {
return Eval();
} else {
Gen();
best = -INFINITY;
i = gen_begin[ply];
while (i < gen_end[ply] && best < beta) {
if (best > alpha) {
alpha = best;
}
if (makeMove(gen_dat[i].m)) {
value = 1000 - ply;
} else {
value = -AlphaBeta(-beta, -alpha, depth - 1);
}
unMakeMove();
if (value > best) {
best = value;
if (ply == 0) {
newMove = gen_dat[i].m;
}
}
++i;
}
return best;
}
}

17
Cũng tương tự như thuật toán Minimax ta đã gộp hai mục 2 và 3 làm một
nhờ việc đổi dấu thích hợp. So với thuật toán Minimax thì trong thuật toán
AlphaBeta đã đưa thêm hai biến alpha, beta làm hai mức ngưỡng. Ta thấy cứ
mỗi khi best >= beta thì thuật toán không thực hiện tiếp vòng lặp, có nghĩa là nó
không chịu mở rộng tiếp những nhánh còn lại nữa. Các nhánh đó đã bị cắt bỏ -
và do đó ta sẽ tiết kiệm được thời gian. Việc cắt bỏ này hoàn toàn an toàn với
những lí do ta đã xét ở trên. Ta thấy rằng mỗi lần hàm này được gọi thì chỉ có
tham số beta được dùng để so sánh cắt bỏ, còn tham số alpha không được dùng.
Tuy nhiên khi áp dụng cùng thuật toán cho cây con thì ta đã hoán vị hai giá trị
alpha, beta cho nhau (và đảo cả dấu), do đó alpha sẽ có tác dụng trong độ sâu
sau, rồi độ sâu sau nữa lại đến lượt beta... Nói cách khác, một giá trị chỉ luôn
ảnh hưởng đến người chơi cực đại, còn giá trị kia lại luôn ảnh hưởng đến người
chơi cực tiểu. Chúng là các ngưỡng của họ (ngưỡng giữa các nước đi được chấp
nhận và không chấp nhận). Những nước đi cần quan tâm phải nằm lọt giữa hai
giá trị này. Dần dần khoảng cách giữa hai giá trị alpha - beta càng ngày càng
thu hẹp và dẫn đến các nhánh cây có giá trị nằm ngoài khoảng này nhanh chóng
bị cắt bỏ (hình 4). 

1.2.2.3 Đánh giá


Trong điều kiện lí tưởng, thuật toán AlphaBeta chỉ phải xét số nút theo
công thức:

=  với d chẵn  

=    với d lẻ  
Với b = 40 và d = 4 ta có số nút phải xét là 2x402 - 1 = 3199. Như vậy
trong điều kiện lí tưởng thì số nút phải xét nhờ AlphaBeta (chỉ khoảng 3 nghìn
nút) ít hơn thuật toán Minimax (hơn 2,5 triệu nút) là 2560000 / 3199 khoảng
800 lần. Còn với b = 40 và d = 5 ta có số nút phải xét là 403 + 405/2 - 1 =
64000+10119-1 = 74118. Số nút phải xét nhờ AlphaBeta ít hơn thuật toán
Minimax (hơn 102 triệu nút) là 102400000/74118 = 1382 lần.

18
Dưới đây là bảng so sánh số nút phải xét giữa hai thuật toán Minimax và
AlphaBeta.

Minimax AlphaBeta Tỉ
lệ
Đ số
ộ nú
sâ Số lần
Số nút Số lần tăng Số nút t
u tăng
Minimax /
AlphaBeta
1 40 40  1
2 1600 40  79  1.9 20
3 64000  40  1852  23.2 34 
4 2560000  40  3199  1.7 800 
5 102400000  40  74118  23.2 1381 
6 4096000000  40  127999  1.7 32000 
7 163840000000  40  2964770  23.2 55262 
8 6553600000000  40  5120000  1.7  1280000 

Ta có thể nhận xét như sau:

 Số lần tăng số nút khi tăng độ sâu của Minimax luôn là hệ số phân nhánh
b, trong trường hợp này là 40. Số lần tăng của AlphaBeta ít hơn nhiều:
chỉ cỡ 1.7 lần khi tăng từ d lẻ sang d chẵn và 23.2 lần khi từ d chẵn sang
lẻ - trung bình chỉ tăng khoảng hơn 6 lần khi tăng d
   Số nút của AlphaBeta tăng chậm hơn rất nhiều lần so với Minimax. Tỉ
số nút phải xét giữa hai thuật toán này càng cao khi d càng lớn.

Công thức tính số nút cho thấy số nút phải xét khi dùng AlphaBeta ít hơn
nhiều so với Minimax nhưng vẫn là hàm số mũ và vẫn dẫn tới bùng nổ tổ hợp.
Thuật toán AlphaBeta hoàn toàn không chống được bùng nổ tổ hợp mà chỉ
làm giảm tốc độ bùng nổ. Tuy trong thực tế số nút phải xét (lượng giá) thường
nhiều hơn trong điều kiện lí tưởng nhưng nó vẫn đủ để tiết kiệm khá nhiều thời
gian. Trong cùng một khoảng thời gian, thuật toán AlphaBeta có thể tìm đến độ
sâu gấp hai lần độ sâu tìm kiếm bằng Minimax. Hình 5 là đồ thị so sánh giữa
hai thuật toán này.
19
Ví dụ: Ta sẽ xem xét thuật toán AlphaBeta hoạt động như thế nào đối với
cây trò chơi như trong hình 6.

20
Cây này có độ sâu bằng 3 và hệ số phân nhánh bằng 3. Các thứ tự kết
luận (các con số bên trái) được đưa ra như sau:
1-2 Tìm kiếm đi xuống dưới theo nhánh trái cho đến lá. Ở đây giá trị
tĩnh thu được là 8. Giá trị đầu tiên này do người chơi cực đại được phép
chọn trong ba giá trị ở nhánh này đã đảm bảo rằng là kết quả thu được
sẽ ít nhất là bằng 8. Điều lưu ý này được bước 2 ghi lại.
3-5 Để chắc chắn không còn có điểm nào cao hơn 8, người chơi cực
đại phải xét cả hai thế cờ còn lại và thu được các giá trị 7 và 2. Do đó
đến đây đã kết luận chính xác điểm cao nhất có thể đạt được ở cây con
là đúng bằng 8.
6. Leo lên một tầng cây. Đây là các nước đi của người chơi cực tiểu.
Ta không hi vọng anh ta cho người chơi cực đại được nhiều điểm nên
có thể tạm kết luận ở mức này là sẽ đạt được nhiều nhất là 8 điểm.
7-8.  Để xem người chơi cực tiểu còn lựa chọn nào tốt hơn (và tồi tệ
hơn cho người chơi cực đại) ta phải xem xét cả hai nước đi còn lại.
Nước đi còn lại đầu tiên dẫn đến giá trị lượng giá tĩnh là 9 - một giá trị
lớn hơn 8. Như vậy nhánh giữa là tồi tệ hơn cho người chơi cực tiểu.
Đến đây việc cắt bỏ được thực hiện - đừng hòng người chơi cực đại với
tới được điểm đó khi đã có sẵn lựa chọn thấp hơn cho anh ta (là 8).
Điều này cũng dẫn đến không cần thiết phải xét hai nút còn lại - đằng
nào nhánh giữa cũng đủ tồi tệ rồi và người chơi cực tiểu sẽ không chọn
nó để đi.
9- Người chơi cực tiểu cần phải khảo sát tiếp lựa chọn cuối cùng.
14.  Cách làm tương tự như phần trên. Ở đây phải lượng giá cả ba nút cây và
kết luận cuối cùng được đưa ra là người chơi cực đại đi giỏi lắm thì chỉ
đạt được 4 điểm.
15.  Như vậy nhờ việc khảo sát nhánh cây bên phải người chơi cực
tiểu thấy rằng nếu chọn đi theo nhánh này thì người chơi cực đại chỉ
được có 4 điểm thay cho 8.
16.  Bây giờ ta có thể kết luận ở mức trên cùng. Mức này là của người
chơi cực đại. Anh ta thấy rằng nếu chọn đi theo nhánh trái thì được 4
điểm. Như vậy anh ta đã chắc chắn điểm của mình sẽ ít nhất là 4 rồi. Để
xem liệu có thể đạt được điểm cao hơn nữa hay không cần phải xem xét
hai nhánh còn lại.
17-  Tương tự như phần trên, ta kết luận nhánh giữa sẽ mang lại cho
30. người chơi cực đại 5 điểm. 31. Cũng tương tự như kết luận 16, ở đây ta
kết luận khả quan hơn là người chơi cực đại đã cầm chắc 5 điểm và có
thể còn cao hơn.
32- Ta kết luận được rất nhanh là cây con bên phải chỉ cho "thu

21
38 hoạch" nhiều nhất là 3 điểm - một điểm số quá kém do đó thuật toán
không buồn xem xét các trường hợp còn lại nữa. Do đó đã tiết kiệm
được 6 nút không cần phải lượng giá và cũng không phải sinh nước đi
cho hai trường hợp.
39.  Kết luận cuối cùng là điểm cao nhất mà người chơi cực đại có thể
thu được là 5 điểm nhờ chọn đi theo nhánh giữa.

22
Chương 2: PHÂN TÍCH VÀ LẬP TRÌNH GAME

Chương trình cờ tướng áp dụng thuật toán Minimax

Trò chơi Cờ Tướng (tên phiên âm Trung Quốc XiangQi, têntiế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 Minimax và AlphaBeta trên cây này như thế nào. Đây là
một trò chơi thú vị và rất phổ biến ở Việtnam, 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).

2.1 Mô tả ý tưởng
Game có những chức năng:
- Tạo bàn cờ, với các chế độ người đấu với người, người đấu với máy, máy
đấu với máy
- Trong trường hợp, người đấu với máy, chương trình cho phép người
dùng có thể tủy chỉnh độ sâu xét duyệt của thuật toán
- (Máy nhìn trước bao nhiêu nước đi), độ sâu càng cao, độ khó càng tăng
và thời gian máy tính toán càng lâu.
- Game có hiển thị thời gian tính toán, số lượng nút lượng giá, hệ số phân
nhánh
- Cho phép người dùng chuyển qua lại giữa hai thuật toán Minimax và
AlphaBeta để thấy được sự khác biết về tốc độ tính toán.
2.2 Xây dựng chương trình
2.2.1 Lượng giá.
Đá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. 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 - xấu. 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. Đâ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ả. Đ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, phụ thuộc vào tính hãm, tính biến, thế công, thế thủ của từng
quân cờ cũng như cả cục diện trận đấu. Ví dụ, một cặp Mã giao chân, 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.
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 đó. Ta cũng biết rằng, "lạc nước hai Xe đành

23
bỏ phí, gặp thời một Tốt cũng thành công", 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.
Chúng ta bắt đầu với việc công nhận các giả thuyết sau:
1. Ta có thể biểu diễn chất lượng một thế cờ bằng một con số. Ví dụ, con
số đó có thể là đánh giá của ta về xác suất chiến thắng, nhưng đối với
đa số chương trình thì con số đó không có gì đặc biệt, nó chỉ là con số
mà mục đích chính là so sánh được với nhau.
2. 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 đó, 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). Điều này tuy
không thật đúng lắm, 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.
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á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. Do đó, 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, 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.
Cách làm này đã bỏ qua mất những nghệ thuật, 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). 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). Nó còn tạo
cảm giác như cờ "vồ", 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, lí do sẽ trình
bầy dưới). 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, đóng vai trò chủ đạo trong điểm của một thế
cờ. Nó là cơ sở của đa số hàm đánh giá. Ta cũng có thể nhận thấy trong phần
lớn thời gian diễn ra trận đấu, hai bên đều tìm cách tiêu diệt quân của nhau.
Các phương án, 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ờ. 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. Nói cho cùng, 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 - Tướng đối phương.
 Là cách tính đơn giản nhất và nhanh nhất. Do tính nhanh, ta có thể tăng
thêm độ sâu tìm kiếm. Việc tăng thêm độ sâu lại giúp máy có cái nhìn xa
hơn, "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.

24
Đ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ờ. Sau đây là điểm từng quân mà mọi người thường chấp
nhận:
Quân cờ Kí hiệu Điểm
Tốt PAWN 1 (2 nếu đã qua sông)
Sĩ BISHOP 2
Tượng ELEPHANT2
Mã KNIGHT 4
Pháo CANNON 4.5
Xe ROOK 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. 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 - 1 (2 nếu đã qua sông), Sĩ - 2, Tượng - 2.5,
Mã - 4.5, Pháo - 5, Xe - 10.
Trong chương trình cờ của chúng ta, điểm cụ thể của từng quân cờ là các số
nguyên 10, 20, 20, 40, 45 và 90. Ta dùng một mảng piecevalue để lưu điểm
từng quân cờ. 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", 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í", 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).
Trong bàn cờ, quân Tướng là quân quan trọng nhất, 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ờ. Do đó, Tướng thường được cho một điểm rất cao, cách biệt nhiều lần so với
các quân khác, đả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", "thêm" đều không bằng được Tướng. Trong chương trình, ta cho nó
1000 điểm.
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;


const piecevalue:array[1..7]of integer=(10,20,20,40,45,90,1000);
var i, s: integer;
begin
    s := 0;
    for i:=1 to BOARD_SIZE do begin

25
        if color[i] = side then s := s + piecevalue[piece[i]]
        else if color[i] = xside then s := s - piecevalue[piece[i]];
    end;
    Eval := s;
end;

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.

Tham khảo kiến thức cờ Tướng

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. Từ lâu, 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, Pháo 7, 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à, sức mạnh bằng nửa con
Xe (nhất Tốt độ hà, bán Xa chi lực). 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 đủ. 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. Thông thường, theo
thời gian một ván cờ được chia thành ba giai đoạn: khai cuộc, trung cuộc và tàn
cuộc. 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. Do đó các định giá quân cờ thường là cho giai
đoạn này.

Chúng ta đã biết mỗi loại quân cờ hay mỗi binh chủng có đặc điểm, 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, giá trị của chúng
cũng không giống nhau. 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ũ. 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, không thể phát huy tính năng
tác dụng được, hay chỉ phát huy ở mức rất hạn chế. 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ị
vốn có thường được gọi là giá trị cơ bản (giá trị tĩnh), còn giá trị biến động được
gọi là giá trị tương đối.
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ờ. 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. Khi Tốt qua sông, 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). Bây

26
giờ nó mang giá trị tương đối, 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.

Đối với Sĩ, Tượng là loại binh chủng phòng ngự, có nhiệm vụ chính là bảo vệ an
toàn cho Tướng, đôi lúc chúng cũng trợ giúp các quân khác tấn công. Trong giai
đoạn trung cuộc, Tốt đối phương qua sông đổi được một Sĩ hoặc một Tượng. Do
đó người ta đánh giá Sĩ hoặc Tượng có giá trị bằng một Tốt đã qua sông, tức là
bằng 2. 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.5 mới công bằng.

Quân Mã trên bàn cờ có khả năng khống chế tối đa 8 điểm. Mỗi bước nhẩy của nó
vượt được hai tuyến đường ngang hay dọc. So ra thì Mã cũng mạnh đấy nhưng tác
chiến ở tầm cự ly ngắn mà thôi. 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, do đó định giá trị cơ bản của Mã 4.5 là
vừa. Nên nhớ hai Tốt cặp kè nhau khi qua sông phải có giá trị hơn 4.
Pháo là binh chủng tác chiến tầm xa rất có hiệu quả. Trên đường dọc, Pháo
khống chế tối đa chỉ 8 điểm nhưng nó còn khống chế được cả đường ngang. Mặt
khác Pháo là quân có tính cơ động cao, nó có thể đi một bước vượt đến 9 tuyến
đường. Nhưng ở cự li gần, nhiều khi Pháo bất lực, không làm gì được để khống
chế đối phương. Do đó so với Mã thì phải thấy mỗi quân có một sở trường riêng,
sức mạnh của chúng coi như tương đương nhau. 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, Mã đi lại dễ bị cản trở, còn Pháo
thì nhanh nhẹn hơn, 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.

Đối với Xe thì rõ ràng là một binh chủng cơ động mạnh nhất, nó khống chế
ngang, dọc tối đa đến 17 điểm. 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 đối với hai Pháo thì có thể coi là
cân bằng. Do đó giá trị cơ bản của Xe là 10.

Còn Tướng, 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 cho các quân phe nó tấn công có kết quả. Thế nhưng giá trị cơ
bản của nó thì vô cùng to lớn, không một quân nào so sánh được. Tất cả các quân
đều có thể đổi, 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, vì mất Tướng là thua 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ì.

(Sưu tầm)

27
 

2.2.2 Sinh nước đi.


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ờ. Máy sẽ tính toán
để chọn nước đi có lợi nhất cho nó. 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 nước đi sai sẽ làm hỏng mọi tính
toán. Đồng thời chương trình có thể bị trọng tài xử thua luôn. Do số
lượng nước đi sinh ra lớ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ó.
 Đầy đủ (rất quan trọng). Sinh được mọi nước đi có thể có từ một thế
cờ.
 Nhanh. 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.
Sinh nước đi là một thuật toán vét cạn. Máy sẽ tìm mọi nước đi hợp lệ có
thể có. 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). 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, trong đó các phép tính kiểm tra giới hạn chiếm một phần
đáng kể. 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. Đây là một trong những thủ tục phức tạp và dễ sai nhất.
Một nước đi có hai giá trị cần quan tâm. Đó là điểm xuất phát (from) và
điểm đến (dest). 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.
type
  move = record { Định nghĩa cấu trúc nước đi }
  from, dest: byte; 
end;

28
 

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). Bây giờ ta sẽ
sinh thử một nước đi sang trái một ô cho 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. Một nước đi khác cần phải xem xét là sang trái ba ô - ô 81. Ô 81 tuy có
trong bàn cờ nhưng khác hàng nên không được chấp nhận. Như vậy, 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. Việc kiểm tra 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). Ta thấy ((83-1) div 9) = ((84-1) div 9) nên ô 83
được chấp nhận; 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.5).

29
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. 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ờ đó.
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, trong đó x là vị trí của quân Xe này .

i := x - 1; { 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, vị trí ô đang xét);
    if ô thứ i không trống then
    break; { Kết thúc quá trình sinh nước đi sang trái }
    i := i - 1; { Xét tiếp vị trí bên trái }
end;

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. Như vậy, 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, vị trí ô đang xét)
                    if ô đang xét không trống then break; 
                end;

                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
                 ...
                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
30
                ...
                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
                ...
            end; { Xong quân Xe }

        Pháo:
                 ...

Phương pháp này có nhược điểm là chương trình phải viết phức tạp, cồng kềnh,
khó tìm lỗi nhưng khá nhanh.
Trong chương trình mẫu VSCCP, 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 - do các bảng của nó có dạng các hộp phân
thư). 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. Mấu chốt là thay cho bàn cờ có
kích thước bình thường 9x10 = 90, ta dùng một bàn cờ mở rộng, mỗi chiều có
thêm 2 đường nữa (bàn cờ mới có kích thước 13x14 = 182). Các ô ứng với các
đường bao mở rộng đều có giá trị -1, tức là các giá trị báo vượt biên. Các nước
đi trên bàn cờ 9x10 được chuyển tương ứng sang bàn cờ này. 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, phải bỏ đi và ngừng sinh nước về phía đó. 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.6).

Việc chuyển đổi toạ độ thực hiện nhờ hai mảng. Mảng mailbox90 dùng
để chuyển từ toạ độ bàn cờ thường sang toạ độ bàn cờ mới. Mảng ứng với bàn
cờ mới - 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.

31
Ví dụ, nếu vị trí quân Xe nằm ở ô số 84 (trong mảng piece) như hình 2.5,
thì khi đổi sang bàn cờ mở rộng sẽ thành vị trí mailbox90[84] = 148. Có nghĩa
là nó ứng với ô thứ 148 của mảng mailbox182. Bây giờ ta sẽ sinh thử một nước
đi sang trái một ô cho quân Xe này. Việc sinh và kiểm tra sẽ được thực hiện
trong bàn cờ mở rộng, nước đi mới là ô số 148 - 1 = 147. Do mailbox182[147]
= 83 ¹ -1 nên nước đi này được chấp nhận. 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. Tuy nhiên, nước đi sang trái ba ô, có số
148 - 3 = 145 và mailbox182[145] = -1 cho biết đó là một nước đi không hợp
lệ.
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. Chỉ có quân Pháo và Xe có thể đi đến 9 nước đi, còn các quân khác có
nhiều nhất là 1.
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). Ở 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. Để sinh một ô mới theo hàng
dọc, ta phải cộng với +13 hoặc -13 (chú ý, 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).
Để sinh các nước đi chéo ta phải cộng trừ với một hằng số khác. Ta nên lưu các
hằng số này vào mảng offset có 2 chiều. Một chiều dựa vào loại quân cờ nên chỉ số
được đánh từ 1 đến 7. Chiều còn lại là số hướng đi tối đa của một quân cờ. 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. Các quân cờ có số hướng ít hơn sẽ được điền 0 vào phần thừa.
Chú ý là nước đi quân Tốt của hai bên khác nhau hướng tiến. Để tiết kiệm ta chỉ
lưu nước tiến của Tốt đen, còn nước tiến của Tốt trắng chỉ đơn giản lấy ngược dấu.
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. Ta còn phải kiểm tra một số giới hạn khác. Ví dụ
như Tướng và Sĩ không thể đi ra ngoài cung, Tượng chỉ được phép ở 7 điểm cố
định phía bên mình, Tốt chỉ được phép tung hoành trên đất đối phương, còn phía
bên mình cũng bị giới hạn ngặt nghèo một số ô... Để thực hiện những phép kiểm
tra này, 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. Để kiểm tra một quân cờ có được phép ở đó không, 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. 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.

32
 
Ví dụ, ô 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, Xe, Pháo, Mã.
Ngoài ra, 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. Như vậy, tổng thể sinh các
nước đi cho một quân cờ có dạng như sau:
p := piece[i]; { 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;
    x:=mailbox90[i]; { Chuyển sang bàn cờ mở rộng}
    if p in [ROOK, CANNON] then n := 9 else n := 1;
    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 - offset[p, j]
        else x := x + offset[p, j]; { Nước đi mới }
        y := mailbox182[x]; { Chuyển về toạ độ bình thường }
        if side = DARK then t := y else t := 91-y;
        if (y=-1) or { Ra ngoài lề ? }
        ((legalmove[t] and maskpiece[p])=0) { Được phép ở vị trí này
không ? }
        then break; { Thoát nếu nước đi không hợp lệ }

       { Kiểm tra nước cản với Tượng, Mã và xử lí Pháo ở đây }


       ...
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.
Việc kiểm tra được thực hiện nhờ hàm kingface. Hàm sẽ trả lại giá trị true nếu
nước vừa đi gây hở mặt Tướng. Hàm này có thể được đặt trong thủ tục sinh nước
Gen. Tuy nhiên đơn giản hơn, ta đặt nó trong thủ tục gen_push, nếu hở mặt Tướng
thủ tục này sẽ không đưa nước đi đó

33
2.2.3 Giao diện chương trình.

2.3 Cài đặt chương trình

Chương trình cờ tướng được viết trên nền C#, sử dụng IDE là Visual Studio.
Toàn bộ Project gồm Source Code các file liên quan được chứa trong thư mục
China Chess gửi kèm với báo cáo. Để sử dụng có thể chạy file ChineseChess.jar

34
CHƯƠNG 3 : KẾT LUẬN

3.1 Các vấn đề khó khăn và cách thức giải quyết.


Mặc dù đã áp dụng được thuật toán tìm kiếm Minimax và AlphaBeta vào để
giải quyết được bài toán, tuy nhiên trong quá trình làm việc nhóm đã gặp rất nhiều
khó khăn; từ việc nghiên cứu và hiểu được giải thuật MiniMax, AlphaBeta đến
việc ứng dụng vào bài toán cụ thể.
Nhóm đã cùng nhau nghiên cứu tìm hiểu kết hợp với các nguồn tài liệu từ
internet để có thể áp dụng vào chương trình. Bên cạnh đó, kinh nghiệm lập trình
hướng đối tượng ở mỗi thành viên trong nhóm còn có sự chênh lệch, nên việc
thống nhất công việc mới đầu còn khá khó khăn.
Trong quá trình thực hiện đề tài nhóm em đã nhận được sự giúp đỡ tận tình
của các bạn cùng lớp, các thầy cô trong khoa đặc biệt là thầy Phạm Đức Hồng
giúp em hoàn thành tốt báo cáo này.Vì kiến thức vẫn chưa nắm vực cùng kinh
nghiệm thực tế còn chưa nhiều nên báo cáo không tránh khỏi sai sót, rất mong sự
giúp đỡ và góp ý từ thầy cô.
Nhóm em xin chân thành cảm ơn!
3.2 Đánh giá chương trình.
Chương trình mô phỏng thành công thuật toán tìm kiếm MiniMax và
phương pháp cắt tỉa AlphaBeta. Tuy nhiên chương trình mới chỉ dừng lại ở mục
đích học tập nghiên cứu, giao diện chưa được thân thiện với người sử dụng.

3.3 Hướng phát triển


Mặc dù bước đầu đã ứng dụng những thuật toán tìm kiếm với tri thức bổ
sung vào một bài toán cụ thể, song chương trình mô phỏng vẫn chưa thực sự hoàn
thiện về mức độ thời gian cũng như chất lượng đánh giá tối ưu của chương trình
trong từng nước đi của quân cờ, vậy nên nhóm chúng em sẽ vẫn tiếp tục hoàn thiện
chương trình, tối ưu hơn các thuật toán để giúp chương trình có thể được phổ biến
rộng rãi hơn đến người dùng… trong tương lai sẽ cố gắng phát triển trên nhiều
thiết bị hơn ngoài máy tính, như các thiết bị smart phone, tablet…

35
TÀI LIỆU THAM KHẢO

Trong quá trình xây dựng chương trình nhóm đã sử dụng các tài liệu tham khảo
sau:
- Sách “Thuật toán chơi cờ” của Phạm Hồng Nguyên
- Tham khảo code của một số nguồn trên Internet.
- Tài liệu môn nhập môn trí tuệ nhân tạo.

36

You might also like