2 3 MỤC LỤC CHƯƠNG I. GIỚI THIỆU VÀ MÔ TẢ BÀI TOÁN ...................................... 4 I. Đề tài ....................................................................................................... 4 II. Mô tả bài toán ......................................................................................... 4 CHƯƠNG II. PHƯƠNG PHÁP GIẢI QUYẾT BÀI TOÁN ......................... 6 I. Game tree (cây biểu diễn trò chơi) ........................................................ 6 II. Thuật toán Minimax ............................................................................. 7 III. Thuật toán AlphabetaPrunning .......................................................... 7 CHƯƠNG III. CHỨC NĂNG VÀ CÁCH SỬ DỤNG HỆ THỐNG ........... 11 CHƯƠNG IV. CÁC VẤN ĐỀ GẶP PHẢI VÀ HƯỚNG GIẢI QUYẾT .... 15 I. Giao diện chương trình ....................................................................... 15 II. Thuật toán AI ...................................................................................... 15 III. Một số lỗi phát sinh khi chạy chương trình ..................................... 16 IV. Hướng phát triển của bài toán .......................................................... 16 CHƯƠNG V. KẾT QUẢ ................................................................................. 17 CHƯƠNG VI. CÔNG NGHỆ VÀ THUẬT TOÁN SỬ DỤNG ................... 18 I. Ngôn ngữ lập trình Java ...................................................................... 18 II. Lập trình hướng đối tượng trong Java ............................................... 18 III. Game engine JGame ........................................................................... 19 Tài liệu tham khảo ............................................................................................ 20 4 CHƯƠNG I. GIỚI THỆU VÀ MÔ TẢ BÀI TOÁN I. ĐỀ TÀI Xây dựng phần mềm chơi cờ Hexxagon. Về cờ Hexxagon Hexxagon là một biến thể của Ataxx, được chơi trên bàn cờ hình lục giác thay vì hình vuông của Ataxx. Bản chính thức được viết bởi Argo Games vào năm 1992. Năm 1993 nó được phát hành dưới dạng PC Game bởi công ty Software Creations, luật chơi vẫn được giữ như của Ataxx. Hiện nay nó được phát triển và cập nhật trên web bởi Neave. http:www.neave.comgameshexxagon
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
ĐỒ ÁN MÔN HỌC
TRÍ TUỆ NHÂN TẠO
ĐỀ TÀI: XÂY DỰNG PHẦN MỀM CHƠI CỜ
HEXXAGON
GIẢNG VIÊN HƯỚNG DẪN:
TS Nguyễn Nhật Quang
Tháng 11/2010
Trang 3- 3 -
MỤC LỤC
CHƯƠNG I GIỚI THIỆU VÀ MÔ TẢ BÀI TOÁN 4
I Đề tài 4
II Mô tả bài toán 4
CHƯƠNG II PHƯƠNG PHÁP GIẢI QUYẾT BÀI TOÁN 6
I Game tree (cây biểu diễn trò chơi) 6
II Thuật toán Minimax 7
III Thuật toán AlphabetaPrunning 7
CHƯƠNG III CHỨC NĂNG VÀ CÁCH SỬ DỤNG HỆ THỐNG 11
CHƯƠNG IV CÁC VẤN ĐỀ GẶP PHẢI VÀ HƯỚNG GIẢI QUYẾT 15
I Giao diện chương trình 15
II Thuật toán AI 15
III Một số lỗi phát sinh khi chạy chương trình 16
IV Hướng phát triển của bài toán 16
CHƯƠNG V KẾT QUẢ 17
CHƯƠNG VI CÔNG NGHỆ VÀ THUẬT TOÁN SỬ DỤNG 18
I Ngôn ngữ lập trình Java 18
II Lập trình hướng đối tượng trong Java 18
III Game engine JGame 19
Tài liệu tham khảo 20
Trang 4CHƯƠNG I GIỚI THỆU VÀ MÔ TẢ BÀI TOÁN
I ĐỀ TÀI
Xây dựng phần mềm chơi cờ Hexxagon
Về cờ Hexxagon
Hexxagon là một biến thể của Ataxx, được chơi trên bàn cờ hình lục giác
thay vì hình vuông của Ataxx Bản chính thức được viết bởi Argo Games
vào năm 1992 Năm 1993 nó được phát hành dưới dạng PC Game bởi
công ty Software Creations, luật chơi vẫn được giữ như của Ataxx Hiện
nay nó được phát triển và cập nhật trên web bởi Neave
http://www.neave.com/games/hexxagon/
II MÔ TẢ BÀI TOÁN
1 Mục đích:
Tạo ra một trò chơi đối kháng giữa hai đối thủ máy tính và con người
2 Yêu cầu:
Đưa ra thuật toán giúp giải quyết bài toán đặt ra: Sao cho khả năng để
máy tính thắng là lớn nhất
Hoàn thiện chương trình, đồng thời tối ưu hoá thuật toán đưa ra sao cho
chương trình làm việc hiệu quả
Khắc phục lỗi phát sinh trong quá trình giải quyết bài toán
3 Cách thức chơi:
Ban đầu mỗi bên có 3 quân khác màu được bố trí xen kẽ tại sáu đỉnh của
bàn cờ hình lục giác
Máy sẽ được đi nước đầu tiên, sau đó thay phiên nhau đi các nước
Ở mỗi lần đi nếu di chuyển trong 1 ô quân đó sẽ được nhân đôi, nếu di
chuyển 2 ô quân đó sẽ thực hiện nhảy mà ko nhân đôi
Sau khi đi tất cả quân của đối phương bên cạnh mà cách 1 ô sẽ biến
thành quân cùng màu với nó
Trang 5- 5 -
Ở mỗi nước đi tác tử máy luôn chọn nước đi tốt nhất có thể đó là ăn được
nhiều quân của đối phương nhất và để các nước đi tiếp theo đối
phương ăn được ít quân nhất của nó nhất
Khi bàn cờ đầy bên nào có nhiều quân trên bàn cờ hơn thì thắng hoặc khi
một bên nào đó không còn nước đi thì các ô trống còn lại trên bàn cờ sẽ
thuộc về đối phương, trò chơi kết thúc và so sánh số quân hai bên để tìm
người thắng
Trang 6CHƯƠNG II PHƯƠNG PHÁP GIẢI QUYẾT BÀI TOÁN
Đây là một chò trơi đối kháng hai tác tử, thuật giải hiều quả để giải quyết
bài toán là các thuật giải tìm kiếm thông minh Minimax/Maximin Với yêu cầu
là một PC game nên thuật toán cần phải có thời gian thực hiện trong khoảng thời
gian chấp nhận được đối với gamer, nhưng với bàn cờ này khi độ sâu của cây
tìm kiếm là 5 thì số lượng nút cần xét có thể lên đến 3-4 triệu nút, thời gian chạy
của máy tính có thể lên đến hàng phút Vậy nên để được kết quả hiệu quả hơn
thì cài đặt thuật toán cải tiến AlphaBetaPrunning là điều bắt buộc
Chi tiết về phương pháp:
I GAME TREE (CÂY BIỂU DIỄN TRÒ CHƠI)
Xem xét vấn đề thực hiện một chương trình máy tính để chơi một trò
chơi Để đơn giản hoá, chúng ta chỉ xem xét trò chơi với hai thuộc tính sau:
Trò chơi đối kháng với hai người chơi
Chỉ có một người thắng duy nhất, còn người chơi còn lại là người thua,
không có bất cứ sự hợp tác nào giữa hai người chơi
Các loại trò chơi này thường có các bàn cờ cổ điển, như cờ caro (tic tac
toe), cờ vua (chess), cờ tướng (checkers),… Với loại trò chơi này, chúng ta có
thể mô hình hoá chúng bằng cách sử dụng cây biểu diễn trò chơi (game tree)
Trên đây là một phần cây biểu diễn trò chơi của trò cờ caro (tic tac toe)
Mỗi nút đại diện cho một trạng thái của bàn cờ và các con của mỗi nút là các
trạng thái hợp lệ khi thực hiện nước đi tiếp theo Để điểm từng trạng thái, chúng
ta sẽ gán cho mỗi trạng thái là thuận lợi với người chơi thứ nhất một số dương
(càng dương thì càng thuận lợi) Tương tự, chúng ta sẽ gán cho mỗi trạng thái đó
Trang 7- 7 -
là thuận lợi đối với người chơi thứ hai một số âm (càng âm thì càng thuận lợi)
Trong thí dụ này, người chơi thứ nhất đánh dấu “X”, còn người chơi thứ hai
đánh dấu “O”, và chỉ với ba điểm (-1, 0, +1), chúng ta sẽ có +1 cho chiến thắng
của “X”, -1 cho chiến thắng của “O”, và 0 nếu không ai thắng (hoà) Lưu ý ở
đây, các điểm màu xanh chỉ có thể được gán cho trạng thái hiện tại Để tính
điểm cho các vị trí khác, chúng ta phải quan sát các trạng thái nút con của nó, và
chúng ta sẽ sử dụng thuật toán dưới đây:
II THUẬT TOÁN MINIMAX
Bây giờ, chúng ta có một cách để diễn tả trò chơi trong chương trình của
chúng ta, làm thế nào để có thể tính toán được nước đi tối ưu? Giả định rằng đối
thủ luôn có thể tính toán được nước đi như chúng ta, và đối thủ sẽ luôn chọn
nước đi tối ưu nhất (Tương phản với điều này, ví dụ như, người chơi đầu tiên sẽ
tạo ra một cái bẫy với hy vọng rằng đối thủ sẽ mắc bẫy và giành được chiến
thắng một cách nhanh chóng Tuy nhiên, nếu đối thủ không mắc bẫy, người chơi
này sẽ nhận thấy rằng vị trí của mình hiện đang bị giảm sút)
Một thuật toán để tính toán nước đi tốt nhất là thuật toán Minimax:
Thuật toán này sẽ diễn ra trong bao lâu? Với các trò chơi phức tạp như Cờ
vua hay Cờ vây thì để tìm kiếm lời giải đến khi kết thúc trò chơi sẽ phải trả một
giá rất đắt về thời gian Trên thực tế, nếu làm như vậy đối với Cờ vua, trong thời
gian để phân tích một nước đi đó có lẽ mặt trời và trái đất cũng không còn tồn
tại Tuy nhiên, có một cách tối ưu hoá để cho thuật toán đơn giản ở trên tiết
kiệm rất nhiều việc tìm kiếm và cho phép chúng ta tăng tối đa độ sâu tìm kiếm
Thuật toán tối ưu hoá đựa trên Minimax đó là AlphaBetaPrunning
III THUẬT TOÁN AlphaBetaPrunning
Với các trò chơi cờ đối kháng như trên, ý tưởng về cơ bản được thể hiện
như sau: giả sử đến lượt chúng ta đi nước cờ của mình và chúng ta tính được
nước đi A cho chúng ta một lợi thế lớn Bây giờ chúng ta tính đến một nước đi B
và thấy rằng nước đi đáp trả đầu tiên của đối thủ chúng ta đoán trước đối thủ có
thể ép chúng ta về thế hoà! Vậy thì chúng ta cần gì phải xét đến nước đi B này
nữa, bởi ở thời điểm này ta biết rằng nếu ta đi nước B thì kết quả tốt nhất mà ta
đạt được chỉ là một nước đi hoà cho cả hai và chúng ta lại đã biết nước đi A sẽ
cho ta lợi thế hơn nếu chúng ta đi nước đó
minimax(player, board)
if(game over in current board position)
return winner
children = all legal moves for player from this board
if(max's turn)
return maximal score of calling minimax on all the children
else (min's turn)
return minimal score of calling minimax on all the children
Trang 8Để hình thức hoá ý tưởng, chúng ta sẽ theo dõi hai con số, alpha và beta
cho mỗi nước đi mà chúng ta phân tích Alpha sẽ là giá trị của nước đi tốt nhất
có thể mà chúng ta đi được chúng ta tính toán xa nhất có thể (tức là số nước đi
của hai bên xa nhất mà ta tính được) Beta là giá trị của nước đi tốt nhất có thể
của đối thủ mà chúng ta tính được xa nhất có thể Nếu một lúc nào đó
alpha >= beta thì nước đi tốt nhất của đối thủ có thể ép chúng ta vào thế tồi hơn
là nước đi tốt nhất của chúng ta lúc đó, do đó ta không cần phải phân tích xa hơn
nước đi đó nữa Điều kiện cắt tỉa cũng được áp dụng tương tự nếu chúng ta là
người chơi ở vị trí min, nhưng thay vì tìm nước đi lợi cho alpha thì ta tìm nước
đi lợi cho beta Sau đây là thuật toán thực hiện alpha beta cắt tỉa
( http://www.ocf.berkeley.edu/~yosenl/extras/alphabeta/alphabeta.html )
Dựa trên thuật toán này nhóm em đã thực hiện thuật toán trên bàn cờ của
mình như sau:
//AlphaBetaPrunning Algorithm
alpha-beta(player, board, alpha, beta)
if(game over in current board position)
return winner
children = all legal moves for player from this board
if(max's turn)
for each child
score = alpha-beta(other player,child,alpha,beta)
if score > alpha then alpha = score (we have found a
better best move)
if alpha >= beta then return alpha (cut off)
return alpha (this is our best move)
else (min's turn)
for each child
score = alpha-beta(other player,child,alpha,beta)
if score < beta then beta = score (opponent has found
a better worse move)
if alpha >= beta then return beta (cut off)
return beta (this is the opponent's best move)
Trang 9- 9 -
public int alphaBetaPrunning( GameBoard currentBoard, int depth, int
alpha, int beta, boolean returnBestMove) {
if (currentBoard.isEndGame() || depth == 0) {
return evaluate(currentBoard);
}
ArrayList < Move > validMoves = currentBoard
getAllValidMoves(currentBoard.getCurrentPlayer());
if (returnBestMove) {
prunned = 0;
call = 0;
this bMove = null;
if (validMoves.size() > 0) {
this bMove = validMoves.get(0);
}
}
if (call >= 1000000){
return evaluate(currentBoard);
}
if (currentBoard.getCurrentPlayer() == Piece COMPUTER) {
for ( int i = 0; i < validMoves.size(); i++) {
GameBoard copy = currentBoard.copyBoard();
Move move = validMoves.get(i);
copy.move(move.getPFrom(), move.getPTo());
copy.setCurrentPlayer( Piece HUMAN);
call++;
int score = alphaBetaPrunning(copy, depth - 1, alpha,
beta, false );
if (score > alpha) {
alpha = score;
if (returnBestMove) {
this bMove = move;
}
}
if (alpha >= beta) {
prunned ++;
return alpha;
}
}
return alpha;
}
else {
for ( int i = 0; i < validMoves.size(); i++) {
GameBoard copy = currentBoard.copyBoard();
Move move = validMoves.get(i);
copy.move(move.getPFrom(), move.getPTo());
copy.setCurrentPlayer( Piece COMPUTER);
call++;
int score = alphaBetaPrunning(copy, depth - 1, alpha,
beta, false );
copy.setCurrentPlayer( Piece HUMAN);
Trang 10Hàm alphaBetaPrunning trả về 1 giá trị là số nguyên, có 5 tham số đầu vào:
currentBoard: Trạng thái hiện tại của bàn cờ
Độ sâu tìm kiếm: (int depth) được chọn = 3
Giá trị alpha: (int alpha) giá trị của nước đi tốt nhất đối với máy
Giá trị beta: (int beta) giá trị của nước đi tốt nhất đối với người
Boolean returnBestMove: dùng để chặn không cho các lần gọi
alphabetaPrunning ghi đè giá trị bestMove, nên ở lần gọi đầu tiên giá trị
của nó là true nhưng trong các lần gọi đệ quy, giá trị của nó là false
Với trạng thái bàn cờ hiện tại copy ra 1 bàn cờ khác, lấy ra 1 mảng bao
gồm tất cả các nước đi hợp lệ đối với người chơi hiện tại, với từng nước đi thực hiện di chuyển nó trên bàn cờ copy, cập nhật trạng thái mới của bàn cờ, đánh giá giá trị của bàn cờ mới bằng cách gọi hàm
alphaBetaPrunning với độ sâu giảm đi 1
Nếu là nước đi của máy, nó sẽ luôn chọn giá trị ứng với
alphaBetaPrunning là lớn nhất, ngược lại người sẽ chọn ứng với giá
trị nhỏ nhất
Kiểm tra nếu alpha >= beta thì nhánh tìm kiếm đó được cắt đi
Hàm đánh giá trạng thái bàn cờ (int evaluate(board)) trả lại
giá trị của bàn cờ đối với một người chơi (người hoặc máy):
if (score < beta) {
beta = score;
}
if (alpha >= beta) {
prunned++;
return beta;
}
}
return beta;
}
}
Trang 1111
-CHƯƠNG III CHỨC NĂNG VÀ CÁCH SỬ DỤNG
HỆ THỐNG
Cài đặt và chạy chương trình
AIHexxagon\Hexxagon\src\dist
Game hexxagon có giao diện khá đơn giản
Bàn cờ gồm các ô cờ màu tím
Mỗi một ô cờ là một hình lục giác (hexagon)
Hai góc trên thể hiện số quân cờ đang có của mỗi bên
Góc dưới bên trái thể hiện việc trỏ chuột của người chơi, thông báo đến
lượt đi của người chơi hay của máy tính
Góc phía dưới bên phải của màn hình có hiển thị thông tin (chủ yếu dùng
cho việc debug việc click tìm vị trí quân cờ)
Toạ độ con trỏ (screen)
Vị trí của con trỏ trên bàn cờ (index)
Thời gian (timer)
Để vào chơi ta nhấn phím cách (space)
Trang 12Khi bắt đầu trò chơi
Mỗi bên (máy – người) sẽ có ba quân cờ được bố trí sole đối xứng như
hình dưới
Máy có quân cờ màu caro trắng-đỏ
Người có quân cờ màu xanh
Trên cùng bên trái hiển thị số quân cờ của người chơi (You)
Trên cùng bên phải hiển thị số quân cờ của máy (Com)
Máy được đi trước (do yêu cầu chơi với chương trình chơi cờ khác)
Cách chơi
Để đi quân cờ nào ta kích chuột vào quân cờ đó, khi đó trên bàn cờ sẽ
hiển thị các ô cờ có viền (được phép đi):
Viền màu xanh: Nếu đi vào các ô cờ có viền màu xanh thì quân cờ
sẽ được nhân đôi, tức ngoài quân cờ ở vị trí ô cờ cũ, ta được thêm một quân cờ ở vị trí ô cờ mới đi (Số lượng các quân cờ của ta tăng thêm 1)
Trang 1313
- Viền màu vàng: Nếu đi vào các ô cờ có viền màu vàng thì quân cờ
sẽ được di chuyển tới ô cờ đó (số lượng các quân cờ của ta vẫn
được giữ nguyên)
Với mỗi nước đi, tất cả các quân cờ của đối phương nằm ngay cạnh nước
đi đó sẽ bị “ăn”, tức sẽ chuyển màu và biến thành quân cờ của mình
Game over!
Lượt đi của người và máy xen kẽ nhau, cứ thế ta đi hết bàn cờ
Khi các ô cờ đã kín hoặc một bên không thể đi nữa do đã bị đối phương
vây kín thì trò chơi kết thúc Khi đó bên nào có số quân cờ chiếm đa số
(nhiều hơn đối phương) thì giành chiến thắng
Trang 14
Vì giải thuật có độ sâu tìm kiếm lớn nên bạn rất khó có thể thắng được
máy Và đây là kết cục…
Trang 1515
-CHƯƠNG IV CÁC VẤN ĐỀ GẶP PHẢI
VÀ HƯỚNG GIẢI QUYẾT
I GIAO DIỆN CHƯƠNG TRÌNH
Khó khăn: Vì chưa có kinh nghiệm lập trình trò chơi do đó lúc đầu nhóm
em chưa tìm ra được hướng giải quyết tốt nhất cho việc thiết kế giao diện:
lựa chọn giữa dùng thư viện có sẵn của Java là JGame cho trò chơi 2D
hay chọn 1 thư viện khác như LWJGL có hỗ trợ OpenAL và OpenGL để
có thể dễ dàng thiết kế giao diện trò chơi…
Hướng giải quyết: Nếu sử dụng các thư viện có hỗ trợ openGL thì việc
tìm hiểu và viết code sẽ mất rất nhiều thời gian Sau khi cùng nhau tìm
hiểu nhóm em đã thấy được rằng thư viện JGame của Java có rất nhiều
tiện ích đủ để tạo ra được 1 giao diện tốt và cũng không quá phức tạp
trong lập trình Việc làm chủ được Jgame chỉ mất 4-5 ngày làm việc liên
tục, từ đọc tutorial đến document của Jgame
II THUẬT TOÁN AI
1 Thuật toán AI được coi là phần quan trọng nhất của đồ án, tuy nhiên vì
mới được làm quen với thuật toán này, do đó lúc đầu khi tìm hiểu để lập
trình với một thuật toán đơn giản và dễ hiểu nhóm đã gặp nhiều khó khăn
2 Vấn đề bộ nhớ khi cây tìm kiếm quá sâu, lúc đầu cả nhóm đã không sử
dụng độ sâu giới hạn nên vừa chạy chương trình đã gây lỗi
StackOverflow
3 Các phương thức movehay getAllvalidMoveshoạt động không chính
xác gây ra kết quả hiển thị cũng như kết quả của các nước đi không chính
xác Có khi quân của máy tính ăn được quân của người chơi nhưng trên
bàn cờ cũng như trong bộ nhớ lưu trữ bàn cờ không thể hiện điều này
4 Phương thức evaluate hoạt động không được hiệu quả khi đánh giá bàn
cờ đối với mỗi nước đi của người chơi nên thỉnh thoảng còn có những
nước đi bằng mắt thường có thể nhận xét là chưa được tốt (thấy rõ khi để
depth = 4 và cho hai bên đều là máy chơi cờ với nhau)
Hướng giải quyết:
1 Sau khi đọc được thuật toán trình bày khá trong sáng và dễ hiểu tại
http://www.ocf.berkeley.edu/~yosenl/extras/alphabeta/alphabeta.html chúng em đã
quyết định dùng thuật toán này để cài đặt cho chương trình của mình