HỌC VIỆN KỸ THUẬT QUÂN SỰ
KHOA CÔNG NGHỆ THÔNG TIN
BÁO CÁO MÔN HỌC
TRÍ TUỆ NHÂN TẠO
Giáo viên hướng dẫn: Ngô Hữu Phúc
HÀ NỘI 3/2010
LỜI MỞ ĐẦU
ờ tướng là một trò chơi giải trí mang tính tư duy logic cao. Ván cờ được tiến hành giữa
hai đấu thủ, một người cầm quân đỏ, một người cầm quân đen. Mục đích của mỗi đấu
thủ là tìm mọi cách đi quân trên bàn cờ đúng luật để chiếu tướng của đối phương và
giành thắng lợi.
C
Bàn cờ là một hình chữ nhật do 9 đường dọc và 10 đường ngang cắt vuông góc tại 90 điểm hợp
thành. Một khoảng trống gọi là sông nằm giữa bàn cờ, chia bàn cờ thành hai phần đối xứng
bằng nhau. Mỗi ván cờ mở đầu phải có đủ 32 quân, gồm 7 loại chia đều cho mỗi bên gồm 16
quân đỏ và 16 quân đen.
Ở đây em thực hiện mô tả không gian trạng thái trò chơi cờ tướng theo giải thuật minimax.Mặc
dù em đã cố gắng mô tả các luật đi của các quân cờ, xong vẫn không thể tránh khỏi các thiếu
sót. Rất mong sự giúp đỡ tận tình của Thầy để chương trình của em có thể được hoàn thiện.
Em xin chân thành cảm ơn Thầy!
Chương trình:
I) Giao diện:
II). Các hàm sử dụng trong chương trình:
Mỗi 1 quân cờ ta đều phải lưu tọa độ, tên và màu sắc của quân cờ. Ở đây em sử dụng mảng Bàn
Cờ để lưu mỗi quân cờ với những thuộc tính đó.
Các quân cờ có thể đi đến bất kì điểm nào trên bàn cờ. Muốn di chuyển được quân cờ ta phải xác
định được tọa độ của quân cờ và tọa độ của các vị trí trên bàn cờ. Vì vậy ta phải có 1 hàm để xác
định tọa độ của quân cờ và lưu các tọa độ đó vào mảng Bàn Cờ như sau:
public void XacDinhToaDoDiem()
{
int i, j;
}
return tdChon;
}
Quân cờ được quy định ban đầu có bán kính bằng 30 cm. Vì vậy khi kích vào bất kì điểm nào
trên bàn cờ, hàm KiemTra sẽ tính toán khoảng cách giữa tọa độ của chuột khi đó và tọa độ của
điểm gần đó nhất. Nếu khoảng cách giữa điểm đó với tọa độ của chuột nhỏ hơn hoặc bằng bán
kính của quân cờ thì di chuyển quân cờ ra vị trí đó.
Khi ăn quân cờ, bắt buộc ta phải xóa bỏ quân cờ vừa bị ăn, ở đây em xây dựng hàm xóa quân cờ.
Nó sẽ xóa bỏ tên, màu sắc và tọa độ của quân cờ đó ra khỏi màn hình. Hàm xóa được viết như
sau:
public void XoaQuanCo(QuanCo q)
{
foreach (QuanCo qc in BanCo)
{
if (qc.x == q.x && qc.y == q.y)
{
BanCo.Remove(qc);
break;
}
}
}
Hàm này thực hiện duyệt từng quân cờ trong mảng bàn cờ và xóa quân cờ đó ra khỏi bàn cờ.
Việc di chuyển quân cờ đã thực hiện xong, nhưng các quân cờ ở đây có thể di chuyển lung tung
trên bàn cờ. Nó vẫn chưa tuân theo đúng luật của cờ tướng. Muốn xây dựng các nước đi đúng
cho quân cờ tuân thủ luật cờ tướng Việt Nam hiện hành, ta phải viết các hàm dành riêng cho mỗi
quân cờ như sau:
Ta nhận thấy quân Tốt có nước đi đơn giản nhất, nó chỉ di chuyển lên trên (đối với quân đen) và
di chuyển xuống dưới (đối với quân đỏ), khi sang sông nó mới được phép đi ngang mà mỗi nước
đi chỉ di chuyển được 1 ô. Hàm cho quân Tốt đi như sau:
public bool TotDi(QuanCo qTot, ToaDoDiem tdDich)
if ((qTot.y - tdDich.y) == D1)
{
return true;
}
else
return false;
}
else if (qTot.y == tdDich.y)
{
//tot den di ngang
if ((qTot.y <= (4 * D1 + 40)) && (Math.Abs(tdDich.x -
qTot.x) == D))
{
return true;
}
else
return false;
}
else return false;
}
}
Đầu tiên ta xét với quân đỏ. So sánh tọa độ y của đích đến có lớn hơn tọa độ y của quân tốt hay
không (vì lúc này Tốt vẫn chưa sang sông nên chỉ có thể đi dọc, vì vậy ta chỉ cần quan tâm tới
tung độ của nó). Nếu tung độ của đích lớn hơn tung độ của quân tốt chứng tỏ là Tốt đi xuống
dưới. Tiếp tục ta xét tung độ của điểm đích có lớn hơn tung độ của con Tốt đúng bằng D1 hay
không? Nếu đúng thì chứng tỏ nó đang đi dọc xuống 1 bước
Nếu tọa độ y của đích bằng tọa độ y của quân tốt chứng tỏ lúc này tốt đang đi ngang. Ta xét xem
hoành độ của đích có lớn hơn hoành độ của quân tốt đúng bằng khoảng cách D hay không. Nếu
đúng thì chứng tỏ Tốt đi sang ngang đúng 1 nước. Hàm trả về true, ngược lại hàm trả về false.
Đối với Tốt đen cũng tương tự chỉ khác là quân tốt chỉ được phép đi lên trên và khi nào sang
}
}
return true;
}
else if (step <= -2)
//xe đi lên
{
for (i = -1; i > step; i )
{
x = qXe.x;
y = qXe.y + i * D1;
foreach (QuanCo qc in BanCo)
{
if (qc.x == x && qc.y == y)
{
return false;
}
}
}
return true;
}
return false;
}
}
else if (qXe.y == tdDich.y)
//đi ngang
{
int step = (tdDich.x - qXe.x) / D;
int i, x, y;
if (step == 1 || step == -1)
return false;
}
}
return true;
}
return false;
}
}
return false;
}
Ta xét quân đi theo chiều dọc, với biết step là số bước dịch chuyển. Nếu số bước dịch chuyển là
1 hoặc -1 (xuống hoặc đi lên) thì hàm trả về true (đi 1 bước luôn đúng). Nếu số bước dịch
chuyển lớn hơn 2 hoặc nhỏ hơn -2 thì ta phải xét xem giữa bước dịch chuyển đến điểm đích có
quân cờ nào không? Nếu có thì không thể đi được. Hàm trả về false.
Tương tự đối với đi xuống, đi sang trái hoặc đi sang phải. Nếu có quân cờ giữa điểm cần di
chuyển và điểm đến thì không thể đi được. lúc này trên màn hình sẽ xuất hiện thông báo:
Xét tiếp hàm Sĩ đi. Quân này chỉ có thể đi xung quanh để bảo vệ tướng nhưng đi theo đường
chéo và mỗi lần đi chỉ đi được 1 bước. Vì vậy độ dịch chuyển theo hoành độ phải bằng D và theo
tung độ phải bằng D1. Ở đây ta xét đối với từng trường hợp của quân sĩ bên quân đỏ sau đó ta
tịnh tiến vùng này sang quân đen bằng cách cộng thêm 1 khoảng cách distance = 7 ô. Hàm này
như sau:
public bool SiDi(QuanCo qSi, ToaDoDiem tdDich)
{
int distance = 0;
if (!qSi.isRed)
{ distance = 7 * D1; }
if ((qSi.x == 40 + 3 * D) && (qSi.y == 40 + distance))
{
if ((tdDich.x == 40 + 4 * D) && (tdDich.y == 40 + D1 +
distance))
else return false;
}
if ((qSi.x == 40 + 4 * D) && (qSi.y == 40 + D1 + distance))
{
if ((tdDich.x == 40 + 3 * D) && (tdDich.y == 40 + distance))
{
return true;
}
if ((tdDich.x == 40 + 5 * D) && (tdDich.y == 40 + distance))
{
return true;
}
if ((tdDich.x == 40 + 3 * D) && (tdDich.y == 40 + 2 * D1 +
distance))
{
return true;
}
if ((tdDich.x == 40 + 5 * D) && (tdDich.y == 40 + 2 * D1 +
distance))
{
return true;
}
return false;
}
return false;
}
Hàm tướng đi, ta đã biết quân tướng chỉ đi trong vùng ô vuông gạch chéo cùng với phần mà Sĩ
được đi nhưng Tướng khác Sĩ ở chỗ là nó đi theo đường dọc, ngang chứ không đi chéo như Sĩ.
Mỗi lần nó dịch chuyển chỉ được 1 bước.
public bool TuongDi(QuanCo qTuong, ToaDoDiem tdDich)
sông. Nếu ở giữa đường chéo có quân khác đứng thì quân Tượng bị cản, không đi được. Ta đặt
dX và dY là hoành độ và tung độ dịch chuyển của quân Tượng nó phải thỏa mãn là dịch chuyển
2 bước (tức dX = 2*D và dY = 2*D1, xGiữa và yGiữa là tọa độ để ta kiểm tra xem giữa đường
chéo có quân cờ nào đứng không? Nếu có thì hàm trả về false. Nếu không có thì ta tiếp tục xét
tiếp. Hàm tượng đi như sau:
public bool VoiDi(QuanCo qVoi, ToaDoDiem tdDich)
{
int dX, dY;
int xGiua, yGiua;
dX = Math.Abs(qVoi.x - tdDich.x);
dY = Math.Abs(qVoi.y - tdDich.y);
if ((dX == 2 * D) && (dY == 2 * D1))
{
//tinh x giua va y giua;
if (tdDich.x > qVoi.x)
xGiua = qVoi.x + D;
else xGiua = qVoi.x - D;
if (tdDich.y > qVoi.y)
yGiua = qVoi.y + D1;
else yGiua = qVoi.y - D1;
//
bool tontai = true;
foreach (QuanCo qc in BanCo)
{
if (qc.x == xGiua && qc.y == yGiua)
{
tontai = false;
break;
}
}
th2 = true;
}
else th2 = false;
//
if (th1 || th2)
{
if (th1)
{
xKt = qMa.x;
if (tdDich.y > qMa.y)
yKt = qMa.y + D1;
else
yKt = qMa.y - D1;
}
else if (th2)
{
yKt = qMa.y;
if (tdDich.x > qMa.x)
xKt = qMa.x + D;
else
xKt = qMa.x - D;
}
//co xkt va ykt
foreach (QuanCo qc in BanCo)
{
if (qc.x == xKt && qc.y == yKt)
{
return false;
break;
}
{
x = qPhaoAn.x;
y = qPhaoAn.y + i * D1;
foreach (QuanCo qc in BanCo)
{
if (qc.x == x && qc.y == y)
{
songoi++;
}
}
}
if (songoi == 1)
return true;
else return false;
}
else if (step <= -2)
//pháo đi lên
{
for (i = -1; i > step; i )
{
x = qPhaoAn.x;
y = qPhaoAn.y + i * D1;
foreach (QuanCo qc in BanCo)
{
if (qc.x == x && qc.y == y)
{
songoi++;
}
}
}
}
}
if (songoi == 1)
return true;
else return false;
}
else if (step <= -2)
//di sang trai
{
for (i = -1; i > step; i )
{
x = qPhaoAn.x + i * D;
y = qPhaoAn.y;
foreach (QuanCo qc in BanCo)
{
if (qc.x == x && qc.y == y)
songoi++;
}
}
if (songoi == 1)
return true;
else return false;
return true;
}
return false;
}
Pháo chỉ ăn được khi có quân đứng trước nó làm ngòi cho nó bất kể là quân cùng phe hay khác
phe. Ta đặt 1 biến songoi để tính số ngòi của pháo. Pháo này chỉ ăn được khisố ngòi giữa quân
pháo và quân định ăn là 1. Các trường hợp khác hàm đều trả về false. Số ngòi được tính khi trên
đường đi của nó tới quân định ăn nếu gặp bất kì quân nào thì số ngòi được cộng thêm 1. Đặt biến