ในหัวข้อนี้เราจะมาดูกันว่าโปรแกรมทำงานอย่างไร เริ่มจากประกาศออบเจกต์ scanner จากคลาส Scanner เพื่อเอาไว้รอรับข้อมูลจากคีย์บอร์ด จากนั้นเตรียมพื้นที่เล่นเกมส์โดยประกาศอาเรย์ board เป็นอาเรย์ธรรมดาแทนที่จะเป็นอาเรย์ 2 มิติเลียนแบบตารางเพื่อการจัดการที่ง่ายกว่า และกำหนดหมายเลขลงในแต่ละช่องเพื่อให้ผู้เล่นเอาไว้อ้างอิงในการเลือกช่อง ประกาศตัวแปร ai และ user เพื่อเก็บตัวอักษรที่ใช้เล่น และพิมพ์ข้อความบอกวิธีการเล่น
Scanner scanner = new Scanner(System.in);
// prepare game
String[] board = new String[] {"0", "1", "2", "3", "4", "5", "6", "7", "8"};
String ai = "X"; // define ai to use X
String user = "O"; // define human to use O
System.out.println("please select number to mark");
ในขั้นตอนการเล่นจะใช้ while ทำลูปเพื่อสลับการเล่นระหว่างผู้เล่นโดยประกาศตัวแปร loop เพื่อจับค่าการจบเกมส์ซึ่งตัวแปร loop จะได้ค่า false กลับมาจากเมธอด getAndMark และประกาศตัวแปร turn เพื่อเอาไว้ใช้สลับผู้เล่น
boolean loop = true; // flag for game loop
boolean turn = true; // flag for player turn
while (loop) { // game loop until finish (loop = false)
showGrid(board); // show board status
if (turn) { // ai turn
loop = getAndMark(board, ai, scanner); // get position and mark
turn = false; // turn to other opponent
} else { // human turn
loop = getAndMark(board, user, scanner); // get position and mark
turn = true; // turn to other opponent
}
}
เมธอด showGrid เอาไว้แปลงพื้นที่เล่นจากอาเรย์มาแสดงเป็นตารางให้เข้าใจได้ง่ายขึ้น โดยเริ่มจากสร้างอาเรย์ 2 มิติขนาด 3×3 และใช้ for ทำลูปเพื่ออ่านค่ามาใส่ในแต่ละดัชนีโดยใช้ตัวแปร count เป็นตัวคุมดัชนีของอาเรย์ต้นทาง จากนั้นจีงใช้ for ลูปวนรอบอีกครั้งเพื่อแสดงผล
public static void showGrid (String[] board) { // show board
String[][] grid = new String[3][3];
int count = 0;
for (int i = 0; i < grid.length; i++) { // loop to convert flat board to grid
for (int j = 0; j < grid[0].length; j++) {
grid[i][j] = board[count];
count++;
}
}
System.out.println("---------");
for (int i = 0; i < grid.length; i++) { // loop to show grid
System.out.print("| ");
for (int j = 0; j < grid[0].length; j++) {
System.out.print(grid[i][j]+" ");
}
System.out.print("|\n");
}
System.out.println("---------");
}
เมธอด getAndMark จะใช้สำหรับวางค่า X หรือ O ในช่องที่เลือกโดยผู้เล่น โดยจะดูว่าผู้เล่นเป็น X หรือ O หากผู้เล่นเป็น X จะใช้เมธอด minimax ในการหาช่องที่ดีที่สุด โดยในบล๊อก if จะกำหนดตัวแปร bestMove เป็นอาเรย์ ซึ่งตัวแปรนี้จะถูกทิ้งไปเมื่อจบการทำงานในบล๊อก if แล้ว สาเหตุที่กำหนดเป็นอาเรย์เพราะต้องการให้เป็นข้อมูลชนิด reference ซึ่งเมื่อเราส่งข้อมูลไปในเมธอด minimax มันจะสามารถถูกปรับปรุงได้โดยตรงจากในเมธอด และเหตุที่เราไม่สามารถให้เมธอด minimax คืนค่านี้ได้เพราะตัวเมธอด minimax เองต้องคืนค่าที่ได้จากการคำนวนค่าเสียโอกาสซึ่งจะได้ทำความเข้าใจในลำดับถัดๆไป
ในกรณีที่ผู้เล่นเป็น O จะแสดงข้อความให้เลือกหมายเลขช่องที่ต้องการ จากนั้นทั้งกรณีที่ผุ้เล่นเป็น X และ O หลังจากที่วางค่าแล้วจะตรวยสอบว่าชนแแล้วหรือยัง หากชนะจะปรับปรุงตัวแปร loop เป็น false และส่งกลับไปที่ลูป while ในเมธอด main เพื่อจบเกมส์ หากตรวจสอบแล้วยังไม่ชนะก็จะตรวจสอบว่ายังมีช้องว่างอีกหรือไม่ ถ้าไม่มีแล้วแสดงว่าเสมอกันจะปรับปรุงตัวแปร loop เป็น false เช่นเดียวกัน
public static boolean getAndMark(String[] board, String player, Scanner scanner) { // get position and mark
boolean loop = true;
if (player.equals("X")) { // get position from minimax algorithm and mark
System.out.println("playing by AI");
int[] bestMove = new int[1];
minimax(board, player,bestMove);
board[bestMove[0]] = "X";
if (winning(board,"X")) { // check if X win then game over
showGrid(board);
System.out.println("X win.");
loop = false;
}
} else { // get position and mark from user
System.out.print("Enter the number: ");
int move = scanner.nextInt();
scanner.nextLine();
board[move] = "O";
if (winning(board,"O")) { // check if O win then game over
showGrid(board);
System.out.println("O win.");
loop = false;
}
}
int[] available = emptyIndexies(board);
if (available.length == 0) { // check if no available position then game over
showGrid(board);
System.out.println("Draw");
loop = false;
}
return loop; // return boolean as true in case game over
}
เมธอด winning เป็นเมธอดที่ใช้ตรวจสอบว่าชนะหรือไม่โดยเทียบกับรูปแบบที่กำหนดไว้ เมธอดนี้ถูกเรียกใช้ทั้งในเมธอด main และ เมธอด minimax
public static boolean winning(String[]board, String player){ // check if someone win
if (
(board[0].equals(player) && board[1].equals(player) && board[2].equals(player)) ||
(board[3].equals(player) && board[4].equals(player) && board[5].equals(player)) ||
(board[6].equals(player) && board[7].equals(player) && board[8].equals(player)) ||
(board[0].equals(player) && board[3].equals(player) && board[6].equals(player)) ||
(board[1].equals(player) && board[4].equals(player) && board[7].equals(player)) ||
(board[2].equals(player) && board[5].equals(player) && board[8].equals(player)) ||
(board[0].equals(player) && board[4].equals(player) && board[8].equals(player)) ||
(board[2].equals(player) && board[4].equals(player) && board[6].equals(player))
) {
return true;
} else {
return false;
}
}
เมธอด emptyIndexies จะคืนค่าช่องที่ยังว่างอยู่โดยเก็บดัชนีของช่องที่ยังว่างในอาเรย์ เริ่มจากใช้ลูป for เพื่อนับช่องที่ยังว่างอยู่แล้วเอาจำนวนมาสร้างอาเรย์ emptyIndexies และใช้ลูป for เพื่ออ่านค่ามาเก็บไว้
public static int[] emptyIndexies(String[] board){ // check available position
int count = 0;
for (int i = 0; i < board.length; i++) { // count available position
if (!board[i].equals("X") && !board[i].equals("O")) {
count++;
}
}
int[] emptyIndexies = new int[count]; // define array with size according to available position
count = 0;
for (int i = 0; i < board.length; i++) { // keep position index in array
if (!board[i].equals("X") && !board[i].equals("O")) {
emptyIndexies[count] = Integer.parseInt(board[i]);
count++;
}
}
return emptyIndexies;
}
เพื่อไม่ให้หัวข้อยาวเกินไป ไปดูคำอธิบายส่วนเมธอด minimax ได้ที่ Java : เขียน AI ด้วย Minimax Algorithm – 3