Giải thuật cắt tỉa Alpha-beta

Một phần của tài liệu Nghiên cứu, thiết kế và chế tạo máy chơi game caro tự động dựa trên nền tảng trí tuệ nhân tạo (Trang 45)

Cắt tỉa Alpha-beta sẽ giúp loại bỏ những không gian trạng thái không cần thiết và hỗ trợ tối ưu hóa thuật toán tìm kiếm Minimax.

Giải thuật cắt tỉa Alpha-beta từng được nhiều nhà khoa học máy tính đề xuất ý tưởng và không ngừng được cải tiến cho đến ngày nay. Giải thuật này thường sử dụng chung với thuật toán tìm kiếm Minimax nhằm hỗ trợ giảm bớt các không gian trạng thái trong cây trò chơi, giúp thuật toán Minimax có thể tìm kiếm sâu và nhanh hơn. Giải thuật cắt tỉa Alpha-beta có nguyên tắc đơn giản "Nếu biết là trường hợp xấu thì không cần phải xét thêm".

4.4.1. Giải thuật MinMax

Giải thuật Minimax là một thuật toán đệ quy lựa chọn bước đi kế tiếp trong một trò chơi có hai người. Xét một trò chơi đối kháng trong đó hai người thay phiên đi nước đi của mình như tic-tac-toe, cờ vua, cờ tướng, cờ caro, cờ vây… Khi chơi bạn có thể khai triển hết không gian trạng thái nhưng khó khăn chủ yếu là bạn phải tính toán được phản ứng và nước đi của đối thủ mình như thế nào? Cách xử lý đơn giản là bạn giả sử đối thủ của bạn cũng sử dụng kiến thức về không gian trạng thái giống bạn. Giải thuật Minimax áp dụng giả thuyết này để tìm kiếm không gian trạng thái của trò chơi.

Hai đối thủ trong trò chơi được gọi là MIN và MAX luân phiên thay thế nhau đi. MAX đại diện cho người quyết dành thắng lợi và cố gắng tối đa hóa ưu thế của mình, ngược lại người chơi đại diện cho MIN lại cố gắng giảm điểm số của MAX và cố gắng làm cho điểm số của mình càng âm càng tốt. Giả thiết đưa ra MIN và MAX có kiến thức như nhau về không gian trạng thái trò chơi và cả hai đối thủ đều cố gắng như nhau.

Mỗi Node biểu diễn cho một trạng thái trên cây trò chơi. Node lá là Node chứa trạng thái kết thúc của trò chơi.

Giải thuật Minimax thể hiện bằng cách định trị các Node trên cây trò chơi: Node thuộc lớp MAX thì gán cho nó giá trị lớn nhất của con Node đó. Node thuộc lớp MIN thì gán cho nó giá trị nhỏ nhất của con Node đó.

Từ các giá trị này người chơi sẽ lựa chọn cho mình nước đi tiếp theo hợp lý nhất.

4.4.2. Các bước giải thuật MinMax:

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ức là trạng thái kết thúc của trò chơi).

Nếu như mức đang xét là của người chơi cực tiểu (nút MIN), á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 (nút MAX), á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.

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ức là trạng thái kết thúc của trò chơi).

Tính giá trị 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 (nút MIN), á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 (nút MAX), á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.

4.4.3. Ưu điểm và khuyết điểm của giải thuật MinMax:

a. Ưu điểm

Tìm kiếm được mọi nước đi tiếp theo sau đó lựa chọn nước đi tốt nhất, vì giải thuật có tính chất vét cạn nên không bỏ soát trạng thái.

b. Khuyết điểm

Đối với các trò chơi có không gian trạng thái lớn như caro, cờ tướng… việc chỉ áp dụng giải thuật Minimax có lẽ không còn hiệu quả nữa do sự bùng nổ tổ hợp quá lớn.

Giải thuật áp dụng nguyên lý vét cạn không tận dụng được thông tin của trạng thái hiện tại để lựa chọn nước đi, vì duyệt hết các trạng thái nên tốn thời gian.

4.4.4 Giải thuật cắt tỉa Alpha-beta

Nút Max có một giá trị alpha (lớn hơn hoặc bằng alpha – luôn tăng), nút min có một giá trị beta (nhỏ hơn hoặc bằng beta – luôn giảm). Khi chưa có alpha và beta xác định thì thực hiện tìm kiếm sâu (depth-first) để xác định được alpha, beta, và truyền ngược lên các nút cha.

Một số tài liệu có đề cập với việc cắt tỉa alpha và cắt tỉa beta, ở đây tôi cũng sẽ đề cập về việc đó và dùng một cách khác, dùng các khoảng trong toán học.

Hình 5.1. Cắt tỉa alpha

Chắc hẳn những bạn đang đọc bài này sẽ đều thắc mắc vấn đề tại sao chúng ta có thể cắt bỏ toàn bộ những nút con của C trên cây trò chơi trên.

Đầu tiên là xét cây từ trái sang phải ta sẽ thấy S là Max, theo chiến lược đưa ra vậy chúng ta sẽ có giá trị alpha ≥ 10 tại S.

Tiếp theo, ở C ở đây là nút Min (trạng thái trò chơi dành cho Max) tức là sẽ lấy giá trị nhỏ nhất của các nút con ở dưới. Nếu như vậy thì giá trị chúng ta phải lấy là beta ≤ 3.

Sau khi xác định được alpha và beta, chúng ta có thể dễ dàng xác định việc có cắt tỉa hay không. Ở nút S (Max), giá trị alpha luôn ≥ 10 (luôn tăng) nhưng ở C (Min) thì giá trị luôn luôn ≤ 3 (luôn giảm), nên việc xét các con còn lại ở C là không cần thiết.

Nếu theo khoảng thì hiện tại chúng ta chỉ nhận khoảng ≥ 10 tại nút gốc S, vậy thì đâu cần bận tâm đến việc khoảng ≤ 3 tại nút C. (adsbygoogle = window.adsbygoogle || []).push({});

Hình 5.2. Ví dụ cắt tỉa alpha cho cây trung bình

Ở đây chúng ta cũng xét từ trái qua phải bắt đầu từ nút gốc và nút con bên trái sẽ được

ưu tiên duyệt trước. Duyệt nguyên cây này sẽ khá dài dòng nhưng để bạn hiểu tôi sẽ viết ra các bước sau.

Xét duyệt từ trên gốc xuống sâu (vì ban đầu chưa hề tồn tại giá trị alpha hay beta của các nút).

Nút đầu tiên ta duyệt là E sẽ gặp giá trị 2 (alpha ≥ 2), khi đó ở trên chưa có giá trị beta để ta có thể so sánh nên sẽ bắt đầu duyệt con tiếp theo của nút E đó và ở đây ta sẽ chọn cho alpha = 3 (Max).

Lưu ý là luôn luôn duyệt từ trái sang phải và phải lần lượt từng nhánh một, sau đó sang nhánh tiếp theo cùng gốc. Vậy nên tiếp theo chúng ta sẽ đưa giá trị alpha này lên nút B (Min) và nút B – beta ≤ 3, sau đó nút F sẽ được duyệt, và ta phải tìm alpha của F.

Khi duyệt con đầu tiên mang giá trị 5 vậy alpha của F – alpha ≥ 5.

Tại B – beta ≤ 3 và tại F – alpha ≥ 5. Như vậy chúng ta không cần xem xét các nút con còn lại của F vì cái ta cần ở đây chỉ là khoảng ≤ 3 nên ta cắt toàn bộ các con còn lại.

Sau khi duyệt toàn bộ các con của B thì tại B – beta = 3, và tại nút A – alpha ≥ 3. Các bạn tự duyệt phần còn lại. Đừng lo lắng, nếu không hiểu có thể đọc lại vài lần hoặc có thể comment, mình sẽ giải đáp giúp các bạn.

Từ đó, chúng tôi sử dụng giải thuật cắt tỉa Alpha – Beta để tìm ra nước đi tốt nhất có thể cho máy tính, với tình hình bàn cờ hiện tại. Ý tưởng chính của giải thuật cắt tỉa Alpha – Beta là không khai triển (tạo nút con) các nút mà nó không thể thay đổi quyết định cuối cùng ở nút cha của nó. Phương châm của giải thuật này là “nếu biết điều đó thật sự “tồi tệ” thì đừng tốn thời gian tìm hiểu nó sẽ “tồi tệ” đến đâu” (nguyên văn tiếng Anh: “If you have an idea that is surely bad, don't take the time to see how truly awful it is.” -- Pat Winston). Được thực hiện theo kiểu tìm kiếm sâu, nhưng thay vì tìm kiếm tất cả các nhánh trên cây, giải thuật này sẽ cắt bớt các nhánh không cần thiết, làm giảm số nước đi phải sinh ra và từ đó làm giảm việc định trị, do đó có thể gia tăng tốc độ tìm kiếm nước đi tốt nhất có thể.

CHƯƠNG 5: CHẾ TẠO THỬ NGHIỆM – KẾT QUẢ - ĐÁNH GIÁ 5.1. Lưu đồ giải thuật

5.2. Quy trình thực hiện

Bước Nội dung

Tra cứu tài liệu liên quan: 1 Thư viện. Internet. Hội thảo. Chọn phương án thực 2 hiện

Phân chia nhiệm vụ cho từng thành viên

3

Tiến hành tính toán,

4 thiết kế, gia công, đặt

hàng linh kiện thiết bị phù hợp

5 Tập hợp nhóm lắp ráp hoàn thành phần cơ khí 6 Tập hợp nhóm lắp ráp hoàn thành phần điện tử Tập hợp nhóm lắp ráp 7 hoàn thành phần điều khiển 8 Chạy thử rút kinh nghiệm

5.3. Phần điều khiển hoạt động

KẾT LUẬN - ĐỀ NGHỊ

Chúng tôi vừa trình bày các kỹ thuật để xây dựng một hệ thống chơi tic-tac-toe với người đấu với máy trong phiên bản tương tác thực tế. Quy định của trò chơi là người chơi sẽ chọn lượt chơi của mình, người chơi sẽ dùng tay chạm lên ô mà mình muốn chọn trong 9 ô trên bàn cờ đã được chuẩn bị trước, bộ cảm biến sẽ nhận tín hiệu từ người chơi, sau khi người chơi đã ra nước đi của mình và hệ thống đã nhận dạng được nước đi đó thì đến lượt chơi của máy tính. Để máy tính có thể đưa ra các nước đi “thông minh” nhất có thể thì em áp dụng giải thuật cắt tỉa Alpha – Beta, với ưu điểm là dễ dàng cài đặt và tốc độ tính toán nhanh. Với cơ sở đã xây dựng được, chúng tôi có thể phát triển các trò chơi tương tự cần sử dụng trí tuệ nhân tạo như trò giải ô số sudoku, trò cờ ca-rô, cờ vua,… Cũng với trò tic-tac-toe phiên bản tương tác thực tế, nhưng có thể nâng cấp lên cách thức chơi mới hơn, đó là thay vì chơi 3x3, có thể tăng kích thước bàn cờ lên lớn hơn để tăng được sự tương tác vận động của người chơi. Bên cạnh đó em sẽ cố gắng phát triển để sản phẩm không chỉ dùng để chơi caro một cách đơn thuần

TÀI LIỆU THAM KHẢO

[1]Arthur Sacek; lego tic tac toe

[2] David B.Fogel; Using evolutionary programing to create neural networks that are capable of playing tic-tac-toe; ORINCON Corporation 9363 Towne Centre Dr. San Diego, CA 92121

[3] Riegel, Mary Jennifer, "Nontraditional Positional Games: New methods and boards for playing Tic-Tac- Toe" (2012).

[4]BEHAVIOUR DESIGN OF NAO HUMANOID ROBOT Playing Tic Tac Toe Game

[5] Designing an Artificial Neural Network for state evaluation in Arimaa – Using a Convolutional Neural Network

[6] Matematika és számítástudományok doktori iskola szegedi tudományegyetem természettudományi és informatikai kar bolyai intézet (adsbygoogle = window.adsbygoogle || []).push({});

[7] https://staff.agu.edu.vn/nvhoa/AI/lecture4.pdf [8] https://www.stdio.vn/article/giai-thuat-tim-kiem-minimax-s1EVnH [9] https://www.stdio.vn/article/giai-thuat-cat-tia-alpha-beta-Wu7F1 [10]https://vi.wikipedia.org/wiki/C%C6%A1_c%E1%BA%A5u_servo#%C4%90%E 1%BB%99ng_c%C6%A1_servo [11] http://arduino.vn/bai-viet/181-gioi-thieu-servo-sg90-va-cach-dieu- khien-bang- bien-tro [12] https://arduinokit.vn/dieu-khien-dong-co-rc-servo-su-dung-arduino [13] http://blog.thanhnt.com/co-ban-dien-tro-keo-la-gi/ [14] http://arduino.vn/bai-viet/1026-cam-ung-thanh-tuu-cong-nghe-no-ro-cua-ky- xx- mot-ung-dung-co-ban-su-dung-cam-bien-cham [15] https://grabcad.com/library

PHỤ LỤC 1. Chương trình #include <Servo.h> Servo myservo0; Servo myservo1; Servo myservo2; Servo myservo3; Servo myservo4; Servo myservo5; Servo myservo6; Servo myservo7; Servo myservo8; // int posD2 = 90; int posD3 = 80; int posD4 = 80; int posD5 = 90; int posD6 = 80; int posD7 = 80; int posD8 = 90; int posD9 = 80; int posD10 = 80; int X = 180; int O = 0; int a ; int b ; int c; int d;

// int servoPin1 = 2; int servoPin2 = 3; int servoPin3 = 4; int servoPin4 = 5; int servoPin5 = 13; int servoPin6 = 7; int servoPin7 = 8; int servoPin8 = 9; int servoPin9 = 10; void setup() {

//Khai báo IN/OUT pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(9, OUTPUT); pinMode(10, OUTPUT); pinMode(13, OUTPUT); pinMode(12, OUTPUT); pinMode(A0, INPUT_PULLUP); pinMode(A1, INPUT_PULLUP); pinMode(A2, INPUT_PULLUP);

pinMode(A3, INPUT_PULLUP); pinMode(A4, INPUT_PULLUP); pinMode(A5, INPUT_PULLUP); pinMode(A6, INPUT_PULLUP); pinMode(A7, INPUT_PULLUP); pinMode(11, INPUT_PULLUP); Serial.begin(9600);

//khai bao chân tín hiệu cho servo myservo0.attach(servoPin1); myservo1.attach(servoPin2); myservo2.attach(servoPin3); myservo3.attach(servoPin4); myservo4.attach(servoPin5); myservo5.attach(servoPin6); myservo6.attach(servoPin7); myservo7.attach(servoPin8); myservo8.attach(servoPin9); //các chân đọc tín hiệu từ cảm biến /* int voilA0 = analogRead(A0);

int voilA1 = analogRead(A1); int voilA2 = analogRead(A2); int voilA3 = analogRead(A3); int voilA4 = analogRead(A4); int voilA5 = analogRead(A5); int voilA6 = analogRead(A6); int voilA7 = analogRead(A7);

int voil11 = digitalRead(11);*/ / set vị trí chờ cho từng ô myservo0.write(posD2); myservo1.write(posD3); myservo2.write(posD4); myservo3.write(posD5); myservo4.write(posD6); myservo5.write(posD7); myservo6.write(posD8); myservo7.write(posD9); myservo8.write(posD10); delay(150); }

/ CHƯƠNG TRÌNH XỬ LÝ THÔNG MINH

using namespace std; int index1;

char board[9] = {'*', '*', '*', '*', '*', '*', '*', '*', '*'}; int isFull()// Board is full

{

for (int i = 0; i < 9; i++) { if (board[i] != 'X') { if (board[i] != 'O') { return 0; } }

}

return (1); }

int user_won()//Checks whether user has won {

for (int i = 0; i < 9; i += 3) {

if ((board[i] == board[i + 1]) && (board[i + 1] == board[i + 2]) && (board[i] == 'O')) return 1;

}

for (int i = 0; i < 3; i++) {

if ((board[i] == board[i + 3]) && (board[i + 3] == board[i + 6]) && (board[i] == 'O')) return 1;

}

if ((board[0] == board[4]) && (board[4] == board[8]) && (board[0] == 'O')) {

return 1; }

if ((board[2] == board[4]) && (board[4] == board[6]) && (board[2] == 'O')) {

return 1; }

return 0; }

int cpu_won()// Checks whether CPU has won {

{

if ((board[i] == board[i + 1]) && (board[i + 1] == board[i + 2]) && (board[i] == 'X')) return 1; (adsbygoogle = window.adsbygoogle || []).push({});

}

for (int i = 0; i < 3; i++) {

if ((board[i] == board[i + 3]) && (board[i + 3] == board[i + 6]) && (board[i] == 'X')) return 1;

}

if ((board[0] == board[4]) && (board[4] == board[8]) && (board[0] == 'X')) {

return 1; }

if ((board[2] == board[4]) && (board[4] == board[6]) && (board[2] == 'X')) {

return 1; }

return 0; }

void draw_board() //display tic-tac-toe board {

Serial.println();

Serial.print(board[0]); Serial.print("|"); Serial.print(board[1]); Serial.print("|"); Serial.print(board[2]); Serial.println();

Serial.println("---");

Serial.print(board[3]); Serial.print("|"); Serial.print(board[4]); Serial.print("|"); Serial.print(board[5]); Serial.println();

Serial.print(board[6]); Serial.print("|"); Serial.print(board[7]); Serial.print("|"); Serial.print(board[8]); Serial.println();

}

int minimax(bool flag)// The minimax function {

int max_val = -1000, min_val = 1000; int i, j, value = 1; if (cpu_won() == 1) { return 10; } else if (user_won() == 1) { return -10; } else if (isFull() == 1) { return 0; }

int score[9] = {1, 1, 1, 1, 1, 1, 1, 1, 1}; //if score[i]=1 then it is empty

for (i = 0; i < 9; i++) {

if (board[i] == '*') {

if (min_val > max_val) // reverse of pruning condition... {

{ board[i] = 'X'; value = minimax(false); } else { board[i] = 'O'; value = minimax(true); } board[i] = '*'; score[i] = value; } } } if (flag == true) { max_val = -1000; for (j = 0; j < 9; j++) {

if (score[j] > max_val && score[j] != 1) { max_val = score[j]; index1 = j; } } return max_val; } if (flag == false)

{

min_val = 1000; for (j = 0; j < 9; j++) {

if (score[j] < min_val && score[j] != 1) { min_val = score[j]; index1 = j; } } return min_val; } }

void loop() //The main function { myservo0.write(90); myservo1.write(90); myservo2.write(90); myservo3.write(90); myservo4.write(90); myservo5.write(90); myservo6.write(90); myservo7.write(90); myservo8.write(90); int moe;

Serial.println("---TIC TAC TOE---"); Serial.println("USER--->(O) CPU--->(X)");

//if (true) // { (adsbygoogle = window.adsbygoogle || []).push({});

draw_board();

up: Serial.println("Enter the move:"); do {

int voilA0 = digitalRead(A0); int voilA1 = digitalRead(A1); int voilA2 = digitalRead(A2); int voilA3 = digitalRead(A3); int voilA4 = digitalRead(A4); int voilA5 = digitalRead(A5); int voilA6 = digitalRead(A6); int voilA7 = digitalRead(A7); int voil11 = digitalRead(11); Serial.println("XUẤT GIÁ TRỊ INPUT"); Serial.print("voilA0: "); Serial.println(voilA0); Serial.print("voilA1: "); Serial.println(voilA1); Serial.print("voilA2: "); Serial.println(voilA2); Serial.print("voilA3: "); Serial.println(voilA3); Serial.print("voilA4: "); Serial.println(voilA4); Serial.print("voilA5: "); Serial.println(voilA5); Serial.print("voilA6: "); Serial.println(voilA6); Serial.print("voilA7: "); Serial.println(voilA7); Serial.print("voil11: "); Serial.println(voil11); // delay (2000); if ( voilA0 == HIGH ) { c = 1;

if ( voilA1 == HIGH ) { c = 1; if ( voilA2 == HIGH ) { c = 1; if ( voilA3 == HIGH ) { c = 1; if ( voilA4 == HIGH ) { c = 1; if ( voilA5 == HIGH ) { c = 1; if ( voilA6 == HIGH ) { c = 1; if ( voilA7 == HIGH ) { c = 1; if ( voil11 == HIGH ) { c = 1; } while (c == 0); if (board[moe - 1] == '*') { board[moe - 1] = 'O'; draw_board(); if ( moe == 1 ) {

Serial.println(" player chọn ô:" + moe );

delay(15); } }

if ( moe == 2) {

Serial.println(" player chọn ô:" + moe );

for (posD3 = 90 ; posD3 >= O; posD3 -= 1) { myservo1.write(posD3); delay(15); }

}

if ( moe == 3) {

Serial.println(" player chọn ô:" + moe );

for (posD4 = 90 ; posD4 >= O; posD4 -= 1) { myservo2.write(posD4); delay(15); }

}

if ( moe == 4) {

Serial.println(" player chọn ô:" + moe );

for (posD5 = 90 ; posD5 >= O; posD5 -= 1) { myservo3.write(posD5); delay(15); }

}

if ( moe == 5) {

Serial.println(" player chọn ô:" + moe );

for (posD6 = 90 ; posD6 >= O; posD6 -= 1) { myservo4.write(posD6); delay(15); }

}

if ( moe == 6) {

66 | P a g e

if ( moe == 7) {

Serial.println(" player chọn ô:" + moe ); for (posD8 = 90 ; posD8 >= O; posD8 -= 1)

delay(15); }

} }

if ( moe == 8) {

Serial.println(" player chọn ô:" + moe ); for (posD9 = 90 ; posD9 >= O; posD9 -= 1) (adsbygoogle = window.adsbygoogle || []).push({});

delay(15); } } if ( moe == 9) { {myservo5.write(posD7); {myservo6.write(posD8); {myservo7.write(posD9);

Serial.println(" player chọn ô:" + moe );

for (posD10 = 90 ; posD10 >= O; posD10 -= 1) { myservo8.write(posD10); delay(15); }

}

/ }

/*else {

Serial.println("Invalid Move...Try different move"); goto up;

//} while (true) { Serial.println("CPU MOVE...."); minimax(true); board[index1] = 'X'; draw_board(); if ( index1 == 0 ) {Serial.println("CPU đánh ô 1");

for (posD2 = 90; posD2 <= X; posD2 += 1) { myservo0.write(posD2); delay(15); }

}

if ( index1 == 1 )

{Serial.println("CPU đánh ô 2");

for (posD3 = 90; posD3 <= X; posD3 += 1) { myservo1.write(posD3); delay(15); }

}

if ( index1 == 2 )

{Serial.println("CPU đánh ô 3");

for (posD4 = 90; posD4 <= X; posD4 += 1) { myservo2.write(posD4); delay(15); }

}

if ( index1 == 3 )

{Serial.println("CPU đánh ô 4");

for (posD5 = 90; posD5 <= X; posD5 += 1) { myservo3.write(posD5); delay(15); }

}

{Serial.println("CPU đánh ô 5");

for (posD6 = 90; posD6 <= X; posD6 += 1) {

delay(15); }

myservo4.write(posD6);

Một phần của tài liệu Nghiên cứu, thiết kế và chế tạo máy chơi game caro tự động dựa trên nền tảng trí tuệ nhân tạo (Trang 45)