* Ý tưởng :
- Đầu tiên xét 1 ô trống trên bàn cờ, khi đó, giả sử ta chỉ kiểm tra trên phương nằm ngang (các phương khác tương tự) bao gồm n ô liên tiếp bên trái và n ô liên tiếp bên phải của nó (tức là xét trên 1 hàng ngang có chiều dài 2n+1 ô liên tiếp, khi đó ta có thể thấy được vài điều sau để tóm gọn lại vấn đề :
+ Ta phải xem xét ô trống này trên 2 phương diện : đối với quân ta hay đối với quân của đối thủ, do đó lúc này thì cách làm cho mỗi bên là tương tự nhau ==> chọn quân ta (quân O).
+ Việc xét n ô bên trái hay bên phải thì cách làm cũng tương tự nhau (cùng xuất phát từ vị trí ô trống đang xét nhưng chỉ khác nhau về hướng), do đó giả sử chỉ xét một bên (trái).
+ Với qui ước :
+ n = 4 và đang kta cho quân O
+ Dấu X,O : ô đã bị chiếm bởi quân X,O + Dấu * : ô trống trên bàn cờ
+ Dấu # : biên của bàn cờ + Dấu ? : ô trống đang xét
thì cấu hình của n ô đó có thể như sau :
<> Không có ô nào đáng để ta quan tâm nữa :
Ví dụ : Xét 1 cấu hình trên bàn cờ : *{/color}*OOOX{/color}?OOO** : Khi đó ta kiểm tra từ phải sang trái (bắt đầu từ dấu '?'), gặp ngay quân X kế tiếp => do đó lúc này dù 3 ô kế tiếp đều chứa quân O nhưng đối với ô trống đang xét thì nó chẳng có tích sự gì => Tức là bên trái không còn ô nào đáng được quan tâm. Điều này cũng xảy ra khi ô trống đang xét là nằm kế biên hoặc là bên trái của nó đã chứa >= 5 con (đúng 5 con mới được ăn).
<> Chỉ có 1 ô cần quan tâm.
OXO?OO** : Khi đó (1) và (2) chỉ còn 1 ô trống kế tiếp bên trái, (3) chỉ còn 1 quân cờ 'O' kế tiếp bên trái => Tức là còn đúng 1 ô cần quan
tâm ...
<> Chỉ còn 2 ô cần quan tâm
Ví dụ : Xét : 1) OX*O?**O* và 2) #**?OO** và 3) OXO*?O** ... <> Có 3 ô cần quan tâm
Ví dụ : Xét : 1) #OOO?O** và 2) *XOO*?*O* và 3) OX*O*?O** ... <> Có 4 ô cần quan tâm
Ví dụ : Xét : 1) #****?*** và 2) XOOOO?* và 3) #OO*O?** và 4) XO*O*?** ...
==>> Như vậy ô cần quan tâm là những ô trống hoặc là những ô của quân ta mà chưa có sự 'che chắn' (là sao nhỉ :p ) của quân đối thủ ... - Và bây giờ, rõ ràng muốn lượng giá 1 thế cờ thì mình phải biết làm sao để đánh giá từng ô trống ngon lành như thế nào, rồi lựa những ô ngon ngon xách ra uýnh ...Tức là làm sao phải lưu lại sư 'quan tâm&đánh giá' này bằng CTDL đàng hoàng, dễ truy cập đến, dễ thay đổi, dễ ... xài mà đừng dễ ... sợ ... hì hì hì ... Beng, beng, beng ! Và sau đây là cách của em :
+ Xem xét các cấu hình có thể có của 1 bên (trái hay phải) chỉ có thể là :
/**** Xét n = 4 ****/
const char StTable[31][5] = {
"####", // Hông còn ô nào đáng quan tâm ! "*###","O###", // Í! Còn 1 ô nè !!
"**##","*O##","O*##","OO##", // Ố! 2 ô lận !!
"***#","**O#","*O*#","*OO#","O**#","O*O#","OO*#"," OOO#", //Á ! 3 ô !
//Chà! Tới 4 ô !
"O***","O**O","O*O*","O*OO","OO**","OO*O","OOO*"," OOOO"};
* Phân tích 1 vài các mẫu : Hãy xét từ trái sang phải :
<> StTable[0] ("####") : Không có ô nào đáng quan tâm vì nó đã bị chiếm bởi quân củaa đối thủ hoặc là ô nằm kế cạnh củaa board.
<> StTable[1] ("*###") : Chỉ còn 1 ô trống kế tiếp, còn lại không đáng quan tâm.
<> StTable[2] ("OO##") : 2 Ô kế tiếp (trên hướng đang xét, ví dụ bên trái) là quân ta, ngoài ra không còn ô nào đáng quan tâm
<> StTable[30]("OOOO") : Có 4 quân cờ bên ta liên tiếp => Tuyệt vời ! <> ...
==> Ta định nghĩa theo kiểu này là rất trực quan, thể hiện các cấu hình có thể có với 4 ô liên tiếp trên bàn cờ, mỗi phần tử của 1 mảng StTable là 1 chuỗi có chiều dài không đổi và bằng 4 ... Nhưng mảng này chỉ có ý nghĩa để trình bày trên màn hình mà thôi, nếu không, cách cài đặt theo CTDL này sẽ rất khó để xử lý ==> cải tiến : chỉ lưu con số chỉ vị trí của từng cấu hình mà thôi, ví dụ nếu có số 3 (bắt đầu từ 0) => đó là cấu hình "*###", có số 30 => "OOOO" ... => cần 1 biến kiểu int thui !... => Đặt tên đại là "Cfg Bên trái/phải" ...
+ Í!...Mỗi ô có 2 phía, mỗi phía là 1 con số có nghĩa từ 0..30 như trên => Do đó ta có :
typedef struct CELL_PATTERN {
int nForward; /* nForward = 0..30 <== Right side */ }
PATTERN_TYPE,*PPATTERN_TYPE; typedef PATTERN_TYPE PatternType;
+ Mà quên, mỗi ô lại còn được xét trên 4 phương nữa chứ :
(-)ngang(horizontal),(|)dọc(vertical), (\)chéo chính(main diagonal) và (/)chéo phụ(sub diagonal) ==> Phải lưu đến 4 cái như vậy cho mỗi ô ... Và cuối cùng là cho cả 2 bên : quân ta và quân địch => Thêm 2 đứa nữa nhá ==> Và em có (các bác cũng có ...) :
PatternType LinePattern[2][BOARD_SIZE*4];// member of CBoard class
- Như vậy xét trên 1 phương thì ta sẽ có tổ hợp 31x31 cấu hình có thể có cho 1 ô !
Ví dụ :
+ Cặp (0,29) biểu thị cho cấu hình 9 ô liên tiếp như sau : "####OOO*"; + Cặp (5,27)= "##*OOO**";
+ (0,30) = "####OOOO"; + (1,13) = "###*OO*#"; + (10,26) = "#OO*O*OO"; + (12,29) = "#O*OOOO*";
... trong đó ô vuông () biểu thị cho ô trống đang xét (tức là y chang dấu '?' đã trình bày ở trên) ...
// Xuất ra file : Out.txt tất cả cấu hình cần xem : #include <fstream.h>
const char StTable[31][5] = { "####", "*###","O###", "**##","*O##","O*##","OO##", "***#","**O#","*O*#","*OO#","O**#","O*O#","OO*#"," OOO#", "****","***O","**O*","**OO","*O**","*O*O","*OO*"," *OOO", "O***","O**O","O*O*","O*OO","OO**","OO*O","OOO*"," OOOO"}; void main() { ofstream out_all("Out.txt"); for (int i = 0;i < 31;i++) {
for (int j = 0;j < 31;j++) {
out_all << "(" << i << "," << j << ")" << "\t: |"; for (int k = 3;k >= 0;k--)
out_all << StTable[i][k] << flush; out_all << char(0x19) << flush; out_all << StTable[j] << "|" << endl; }
}
} // end
Ghi chú : Hổng phải em viết cái đoạn này ra là để xem chơi mà thôi đâu ! Sẽ có việc làm với nó sau này : đánh giá thế nào là "hảo hảo ..."
một cách trực quan dựa trên sự chọn lựa của mình ..., vì lý do này, có thể xem cấu hình (a,b) và (b,a) là 1 (chỉ thay đổi vị trí thôi) để sau này dễ làm hơn ...
Ví dụ nó in cái rẹt ra tương tự như vậy : (5,29) : |##*OOOO*|1
(11,27) : |#**OOO**|2 (19,27) : |**O*OO**|3 (11,19) : |#**O*O**|4 (7,23) : |#***O***|5
=> Các số 1,2,3,4,5 là em thêm vào để đánh giá sự tốt xấu của thế cờ kế tiếp nếu quân O đánh vào vị trí ô vuông đang xét ... => Từ đó viết 1 chương trình để đọc & xử lý lại file này thì ta sẽ có 1 cơ sở dữ liệu để lưu được sự tốt xấu của từng thế cờ ...
2. Làm việc với CTDL này:
- Việc truy xuất đến thì chắc dễ rồi, vì chỉ cần biết nForward Và
nBackward là biết ngay nó đang có dạng (configuration) như thế nào, sau đó tuỳ theo dạng này mà lượng giá ... Do đó mục tiêu khó khăn đặt ra là phải cập nhật các cấu hình của các ô sau mỗi nước đi của cả 2 bên sao cho cấu hình này nó phản ánh đúng & chính xác cấu hình thật sự của từng ô trống trên bàn cờ...việc làm này cũng phải nhanh vì nó sẽ nằm trong phần gọi đệ qui. Em đưa ra cách cài đặt như sau :
+ Khai báo 1 mảng để lưu các biến đổi từ cấu hình này sang cấu hình khác sau khi một ô trống bị thay đổi trạng thái (tức là thành quân ta hay quân đối thủ), mảng này sẽ có nhiệm vụ chuyển từ 1 cấu hình có sẵn sang 1 cấu hình mới (dĩ nhiên nó vẫn sẽ nằm trong tập 0..30) sau khi đánh 1 quân cờ ...
+ Bây giờ giả sử ta xét trên 1 hàng ngang 9 ô, ô thứ 5 (ở chính giữa) là ô trống đang xét), 4 ô ở bên trái (từ 1 đến 4) và 4 ô ở bên phải (từ 6 đến 9) tương ứng là 1 cặp cấu hình (trong số tổ hợp 31 cấu hình ở trên) sẽ có dạng sau.
<> Vị trí đang xét là ô trống thứ 5 (hình vuông) Vị trí : 1 2 3 4 5 6 7 8 9
Mảng a[i] : x x x x x x x x
Trong đó x là 1 trạng thái chưa biết(nằm trong tập : {O,X,#,*}) nhưng không quan trọng vì ta chỉ quan tâm các cấu hình dưới dạng các con số mà thôi! (Rõ ràng là vậy vì 4 chữ x bên trái ô vuông là thành phần nBackward, 4 chữ x bên phải là nForward, cả nBackward & nForward đều là 1 con số thuộc 0..30 nằm trong cấu trúc PATTERN đã nói ở trên). ==> Đánh 1 quân cờ vào vị trí ô trống đang xét ==> Cập nhật lại cấu hình bàn cờ :
<> Cập nhật cho 4 ô ở phía bên trái của ô đang xét bằng cách chạy 1 vòng lặp 4 lần, bắt đầu từ vị trí số 4 (kế bên trái ô vuông), giảm dần, nếu gặp 1 ô trống thì ta sẽ cập nhật lại mới cấu hình của phía bên phải nó (tức là cập nhật lại thành phần nForward của ô trống đó), nếu gặp quân ta thì chạy tiếp (hông làm gì cả), nếu gặp quân đối thủ hoặc bắt đầu đụng biên thì break.
<> Cập nhật lại 4 ô bên phải của ô đang xét thì tương tự và ngược lại ... ==> Bây giờ câu hỏi đặt ra là tại sao chỉ là vòng lặp 1->4 (thật ra là 0- >3) là đủ cập nhật lại toàn bộ cấu hình của bàn cờ sau nước đi vào ô trống hình vuông số 5 ??? Thật ra là việc thay đổi ô trống số 5 thì chỉ làm thay đổi đúng 8 ô trống trên hàng ngang mà kế cận nó, bao gồm 4 ô bên trái và 4 ô bên phải, vì rõ ràng là cấu hình bên trái (nBackward) của ô số 9 thì có thể phụ thuộc vào giá trị ô số 5 nhưng những ô >= 10 thì không còn phụ thuộc nữa (nhớ lại là em đang xét với n = 4 thôi nhá!) + Việc cập nhật lại 1 cấu hình thật sự là rất đơn giản như sau :
<> Lập sẵn 1 bảng UpdateFriendPattern (cập nhật mẫu cho quân ta). <> Lập sẵn 1 bảng UpdateEnemyPattern (cập nhật mẫu cho quan đối thủ).
<> Tra bảng để chuyển cấu hình tương ứng với ô đang xét.
này sẽ lộn xộn rất khó đọc, các bác chịu khó copy nó ra file *.txt thì mới dễ thấy ... còn nếu không nhờ mấy bác mod sửa dùm...)
/************************ UPDATE FRIEND PATTERN ********************/
N Form-1 Form-2 Form-3 Form-4 Form-5 Lookup-Table
--- ---
0 "####" const const const const {0,0,0,0} 1 "*###" "O###" const const const {2,1,1,1} 2 "O###" const const const const {2,2,2,2} 3 "**##" "O*##" "*O##" const const {5,4,3,3} 4 "*O##" "OO##" const const const {6,4,4,4} 5 "O*##" const "OO##" const const {0,6,5,5} 6 "OO##" const const const const {6,6,6,6}
7 "***#" "O**#" "*O*#" "**O#" const {11,9,8,7} 8 "**O#" "O*O#" "*OO#" const const {12,10,8,8} 9 "*O*#" "OO*#" const "*OO#" const {13,9,10,9} 10 "*OO#" "OOO#" const const const {14,10,10,10} 11 "O**#" const "OO*#" "O*O#" const {11,13,12,11} 12 "O*O#" const "OOO#" const const {12,14,12,12} 13 "OO*#" const const "OOO#" const {13,13,14,13} 14 "OOO#" const const const const {14,14,14,14}
15 "****" "O***" "*O**" "**O*" "***O" {23,19,17,16}16 "***O" "O**O" "*O*O" "**OO" const {24,20,18,16} 16 "***O" "O**O" "*O*O" "**OO" const {24,20,18,16} 17 "**O*" "O*O*" "*OO*" const "**OO" {25,21,17,18} 18 "**OO" "O*OO" "*OOO" const const {26,22,18,18} 19 "*O**" "OO**" const "*OO*" "*O*O" {27,19,21,20} 20 "*O*O" "OO*O" const "*OOO" const {28,20,22,20}
21 "*OO*" "OOO*" const const "*OOO" {29,21,21,22} 22 "*OOO" "OOOO" const const const {30,22,22,22} 23 "O***" const "OO**" "O*O*" "O**O" {23,27,25,24} 24 "O**O" const "OO*O" "O*OO" const {24,28,26,24} 25 "O*O*" const "OOO*" const "O*OO" {25,29,25,26} 26 "O*OO" const "OOOO" const const {26,30,26,26} 27 "OO**" const const "OOO*" "OO*O" {27,27,29,28} 28 "OO*O" const const "OOOO" const {28,28,30,28} 29 "OOO*" const const const "OOOO" {29,29,29,30} 30 "OOOO" const const const const {30,30,30,30}
/*********************** UPDATE ENEMY PATTERN **********************/
N Form-1 Form-2 Form-3 Form-4 Form-5 Lookup-Table
--- ---
0 "####" const const const const {0,0,0,0} 1 "*###" "####" const const const {0,1,1,1} 2 "O###" const const const const {2,2,2,2} 3 "**##" "####" "*###" const const {0,1,3,3} 4 "*O##" "####" const const const {0,4,4,4} 5 "O*##" const "O###" const const {5,2,5,5} 6 "OO##" const const const const {6,6,6,6} 7 "***#" "####" "*###" "**##" const {0,1,3,7} 8 "**O#" "####" "*###" const const {0,1,8,8} 9 "*O*#" "####" const "*O##" const {0,9,4,9} 10 "*OO#" "####" const const const {0,10,10,10} 11 "O**#" const "O###" "O*##" const {11,2,5,11} 12 "O*O#" const "O###" const const {12,2,12,12} 13 "OO*#" const const "OO##" const {13,13,6,13}
14 "OOO#" const const const const {14,14,14,14} 15 "****" "####" "*###" "**##" "***#" {0,1,3,7} 16 "***O" "####" "*###" "**##" const {0,1,3,16} 17 "**O*" "####" "*###" const "**O#" {0,1,17,8} 18 "**OO" "####" "*###" const const {0,1,18,18} 19 "*O**" "####" const "*O##" "*O*#" {0,19,4,9} 20 "*O*O" "####" const "*O##" const {0,20,4,20} 21 "*OO*" "####" const const "*OO#" {0,21,21,10} 22 "*OOO" "####" const const const {0,22,22,22} 23 "O***" const "O###" "O*##" "O**#" {23,2,5,11} 24 "O**O" const "O###" "O*##" const {24,2,5,24} 25 "O*O*" const "O###" const "O*O#" {25,2,25,12} 26 "O*OO" const "O###" const const {26,2,26,26} 27 "OO**" const const "OO##" "OO*#" {27,27,6,13} 28 "OO*O" const const "OO##" const {28,28,6,28} 29 "OOO*" const const const "OOO#" {29,29,29,14} 30 "OOOO" const const const const {30,30,30,30}
/************************************************** ****************/