五子棋java判断平局_2020-10-03 Java初级项目——从零开始制作一个简易五子棋游戏...
一、棋盤的繪制
使用JFrame容器制作五子棋的窗體
創建一個類——UI,如下:
public class UI {
private JFrame frame = new JFrame();
public void init() {
frame.setTitle("五子棋");
frame.setSize(518, 540);
frame.setLocationRelativeTo(null); //居中
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); //讓窗體顯示
}
public static void main(String[] args) {
new UI().init();
}
}
使用JPanel和Graphics畫出大小為15X15的棋盤
再創建一個新的類——Chessboard,繼承JPanel:
public class Chessboard extends JPanel {
//規定由15條橫豎線組成
private static final int CHESSBOARD_SIZE = 15;
//外邊距
private int margin = 20;
/**
* 繪圖工具
*
* @param g 畫筆工具
*/
@Override
public void paint(Graphics g) {
super.paint(g);
drawChessBoard(g);
drawPieces(g);
}
/**
* 畫棋盤
*
* @param g 畫筆工具
*/
private void drawChessBoard(Graphics g) {
int cellSize = (getWidth() - 2 * margin) / (CHESSBOARD_SIZE - 1);
for (int i = 0; i < CHESSBOARD_SIZE; i++) {
//畫橫線
g.drawLine(margin, margin + cellSize * i, getWidth() - margin, margin + cellSize * i);
//畫豎線
g.drawLine(margin + cellSize * i, margin, margin + cellSize * i, getHeight() - margin);
}
}
}
實現點擊鼠標落子的功能
在UI類中添加以下代碼:
public class UI {
private JFrame frame = new JFrame();
private Chessboard chessboard = new Chessboard();
public void init() {
frame.setTitle("五子棋");
frame.setSize(518, 540);
frame.setLocationRelativeTo(null); //居中
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); //讓窗體顯示
//給棋盤添加鼠標監聽事件,具體來說就是鼠標點擊事件
chessboard.addMouseListener(new MouseAdapter() { //匿名內部類
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
//調用畫棋子的方法
play(e);
}
});
}
/**
* 處理鼠標點擊事件的方法
*/
private void play(MouseEvent e) {
int cellSize = chessboard.getCellSize();
int x = (e.getX() - 5) / cellSize;
int y = (e.getY() - 5) / cellSize;
chessboard.move(new Pieces(x, y, 1));
}
public static void main(String[] args) {
new UI().init();
}
}
再在Chessboard類中添加drawPieces、move、getCellSize方法,如下:
public class Chessboard extends JPanel {
private List piecesList = new ArrayList<>();
/**
* 繪圖工具
*
* @param g 畫筆工具
*/
@Override
public void paint(Graphics g) {
super.paint(g);
drawChessBoard(g);
drawPieces(g);
}
public int getCellSize() {
return (getWidth() - 2 * margin) / (CHESSBOARD_SIZE - 1);
}
public void drawPieces(Graphics g) {
for (Pieces piece : piecesList) {
if (piece.getPlayer() == 1) {
//默認值為1代表人類,棋子顏色為黑色
g.setColor(Color.black);
} else {
g.setColor(Color.white);
}
int cellSize = (getWidth() - 2 * margin) / (CHESSBOARD_SIZE - 1);
g.fillOval(piece.getX() * cellSize + margin - cellSize / 2, piece.getY() * cellSize + margin - cellSize / 2, cellSize, cellSize);
}
}
/**
* 落子的方法
*/
public void move(Pieces piece) {
piecesList.add(piece);
repaint();
}
}
然后創建一個新的類——Pieces,如下:
/***
* 棋子對象
*/
public class Pieces {
private int x;
private int y;
private int player; //表示黑棋還是白棋,1代表黑棋,-1代表白棋
public Pieces(int x, int y, int player) {
this.x = x; //非實際坐標,而是格子數,第x格
this.y = y; //同上,第y格
this.player = player;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getPlayer() {
return player;
}
}
二、判斷勝負
實現五子棋勝負判斷的思路是:
判斷勝負就看哪個顏色的棋子首先達到五子相連。若黑棋先達到五子相連,則黑棋勝,否則,白棋勝;
也就是下在每個棋子時都要判斷它的八個方向的棋子相連個數,如果判斷某一方向與其相鄰點的顏色相同,將循環計數;
還需要注意邊界點的范圍,在循環的時候應注意設置邊界條件;
可以建一個類,在類中寫判斷輸贏的方法;
在Chessboard類中添加如下代碼:
public class Chessboard extends JPanel {
private static final int CHESSBOARD_SIZE = 15;
private int margin = 20;
private List piecesList = new ArrayList<>();
//創建一個數組,用來表示棋盤上被占用的位置
private int[][] location = new int[CHESSBOARD_SIZE][CHESSBOARD_SIZE];
}
但是,落子的位置是有限制的,具體來說就是:
落子的位置不能有其它棋子;
不能超過棋盤的邊界;
所以要在UI類中的play()方法里添加一個落子合法性的判定:
private void play(MouseEvent e) {
int cellSize = chessboard.getCellSize();
int x = (e.getX() - 5) / cellSize;
int y = (e.getY() - 5) / cellSize;
if (chessboard.isLegal(x, y)) {
//添加棋子
chessboard.move(new Pieces(x, y, 1));
//記錄人類落子的位置
chessboard.setLocation(x, y, 1);
//判斷輸贏
if (chessboard.checkWinner(x, y, 1)) {
JOptionPane.showMessageDialog(frame, "人類獲勝", "您贏了!", JOptionPane.PLAIN_MESSAGE);
return;
}
}
}
而對應Chessboard類中新增的isLegal方法、setLocation方法以及checkWinner方法就是:
/**
* 判斷是否重復落子以及落子位置是否合法
*/
public boolean isLegal(int x, int y) {
return x >= 0 && x <= CHESSBOARD_SIZE && y >= 0 && y <= CHESSBOARD_SIZE && location[x][y] == 0;
}
/**
* 記錄落子后,棋子占用棋盤的位置
*/
public void setLocation(int x, int y, int player) {
location[x][y] = player;
}
/**
* 判斷勝負
*/
public boolean checkWinner(int x, int y, int player) {
int sum = 0;
//判斷水平方向,水平左側
for (int i = x - 1; i >= 0; i--) {
if (location[i][y] == player) {
sum++;
} else {
break;
}
}
//水平右側
for (int i = x + 1; i <= CHESSBOARD_SIZE; i++) {
if (location[i][y] == player) {
sum++;
} else {
break;
}
}
if (sum >= 4) {
return true;
}
//判斷垂直方向
sum = 0;
for (int i = y - 1; i >= 0; i--) {
if (location[x][i] == player) {
sum++;
} else {
break;
}
}
for (int i = y + 1; i <= CHESSBOARD_SIZE; i++) {
if (location[x][i] == player) {
sum++;
} else {
break;
}
}
if (sum >= 4) {
return true;
}
//判斷左上到右下這個對角線方向
sum = 0;
for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
if (location[i][j] == player) {
sum++;
} else {
break;
}
}
for (int i = x + 1, j = y + 1; i <= CHESSBOARD_SIZE && j <= CHESSBOARD_SIZE; i++, j++) {
if (location[i][j] == player) {
sum++;
} else {
break;
}
}
if (sum >= 4) {
return true;
}
//判斷右上到左下這個對角線方向
sum = 0;
for (int i = x + 1, j = y - 1; i <= CHESSBOARD_SIZE && j >= 0; i++, j--) {
if (location[i][j] == player) {
sum++;
} else {
break;
}
}
for (int i = x - 1, j = y + 1; i >= 0 && j <= CHESSBOARD_SIZE; i--, j++) {
if (location[i][j] == player) {
sum++;
} else {
break;
}
}
if (sum >= 4) {
return true;
}
sum = 0;
return false;
}
三、簡易的AI算法
思路:
五元組:棋盤中橫豎斜四個方向的所有相鄰五個可連成一條線的格子
五元組的表示:用某格子的橫縱坐標以及+1/-1來表示相鄰四個格子的坐標,從而計算五元組坐標
五子棋棋盤大小是15X15,橫豎斜四個方向共有572個五元組,給每個五元組一個評分
這個五元組將為它的每個位置貢獻的分數就是這個五元組自身的得分
對整個棋盤來說,每個位置的得分就是該位置所在的橫豎斜四個方向所有五元組的得分之和
然后從所有空位置中選得分最高的位置即為機器落子的位置
首先,添加機器落子的代碼,完善UI類中play方法:
private void play(MouseEvent e) {
int cellSize = chessboard.getCellSize();
int x = (e.getX() - 5) / cellSize;
int y = (e.getY() - 5) / cellSize;
if (chessboard.isLegal(x, y)) {
//添加棋子
chessboard.move(new Pieces(x, y, 1));
//記錄人類落子的位置
chessboard.setLocation(x, y, 1);
//判斷輸贏
if (chessboard.checkWinner(x, y, 1)) {
JOptionPane.showMessageDialog(frame, "人類獲勝", "您贏了!", JOptionPane.PLAIN_MESSAGE);
return;
}
//機器落子
Pieces piece = chessboard.searchLocation();
chessboard.move(piece);
chessboard.setLocation(piece.getX(),piece.getY(),piece.getPlayer());
//判斷勝負
if (chessboard.checkWinner(piece.getX(),piece.getY(),piece.getPlayer())){
JOptionPane.showMessageDialog(frame, "電腦獲勝", "您輸了!", JOptionPane.PLAIN_MESSAGE);
}
}
}
其次,還需要一個評估函數(評分表),來對整個棋局中有效位置進行評價,但評分表很難確定,沒有所謂最好的,只能根據經驗和測試來選擇,我選擇的評分表如下:
既有人類落子,又有機器落子,判分為0;
全部為空,沒有落子,判分為7;
機器落1子,判分為35
機器落2子,判分為800
機器落3子,判分為15000
機器落4子,評分為800000
人類落1子,評分為15
人類落2子,評分為400
人類落3子,評分為1800
人類落4子,評分為100000
在Chessboard類中添加tupleScore方法:
private int tupleScore(int humanChessmanNum, int machineChessmanNum) {
if (humanChessmanNum > 0 && machineChessmanNum > 0) {
return 0;
}
if (humanChessmanNum == 0 && machineChessmanNum == 0) {
return 7;
}
if (machineChessmanNum == 1) {
return 35;
}
if (machineChessmanNum == 2) {
return 800;
}
if (machineChessmanNum == 3) {
return 15000;
}
if (machineChessmanNum == 4) {
return 800000;
}
if (humanChessmanNum == 1) {
return 15;
}
if (humanChessmanNum == 2) {
return 400;
}
if (humanChessmanNum == 3) {
return 1800;
}
if (humanChessmanNum == 4) {
return 100000;
}
return -1;
}
在Chessboard類中添加searchLocation方法:
/**
* 計算機器落子的最佳位置
*/
public Pieces searchLocation() {
//每次都初始化一下score的評分數組
for (int i = 0; i < CHESSBOARD_SIZE; i++) {
for (int j = 0; j < CHESSBOARD_SIZE; j++) {
score[i][j] = 0;
}
}
//每次機器尋找了落子位置,評分都重新計算一遍
int humanChessmanNum = 0;
int machineChessmanNum = 0;
int tupleScoreTmp = 0;
int goalX = -1;
int goalY = -1;
int maxScore = -1;
//縱向掃描棋盤
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 11; j++) {
int k = j;
while (k < j + 5) {
if (location[j][k] == -1) machineChessmanNum++;
else if (location[i][k] == 1) humanChessmanNum++;
k++;
}
// 將每一個五元組中的黑棋個數與白棋個數傳入評分表中
tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
//為該五元組的每一個位置添加分數
for (k = j; k < j + 5; k++) {
score[i][k] += tupleScoreTmp;
}
//歸零
humanChessmanNum = 0;
machineChessmanNum = 0;
tupleScoreTmp = 0;
}
}
//橫向掃描棋盤
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 11; j++) {
int k = j;
while (k < j + 5) {
if (location[k][i] == -1) machineChessmanNum++;
else if (location[k][i] == 1) humanChessmanNum++;
k++;
}
// 將每一個五元組中的黑棋個數與白棋個數傳入評分表中
tupleScoreTmp = tupleScore(humanChessmanNum, machineChessmanNum);
//為該五元組的每一個位置添加分數
for (k = j; k < j + 5; k++) {
score[k][i] += tupleScoreTmp;
}
//歸零
humanChessmanNum = 0;
machineChessmanNum = 0;
tupleScoreTmp = 0;
}
}
//3.掃描右對角線 上側部分
for (int i = 14; i >= 4; i--) {
for (int k = i, j = 0; j < 15 && k >= 0; j++, k--) {
int m = k; //x軸
int n = j; //y軸
while (m > k - 5 && k - 5 >= -1) {
if (location[m][n] == -1) machineChessmanNum++;
else if (location[m][n] == 1) humanChessmanNum++;
m--;
n++;
}
//注意斜向判斷時,可能不構成五元組(靠近四個角落),遇到這種情況要忽略掉
if (m == k - 5) {
tupleScoreTmp = tupleScore(machineChessmanNum, humanChessmanNum);
for (m = k, n = j; m > k - 5; m--, n++) {
score[m][n] += tupleScoreTmp;
}
}
//歸零
humanChessmanNum = 0;
machineChessmanNum = 0;
tupleScoreTmp = 0;
}
}
//4.掃描右對角線 下側部分
for (int i = 1; i < 15; i++) {
for (int k = i, j = 14; j >= 0 && k < 15; j--, k++) {
int m = k;
int n = j;
while (m < k + 5 && k + 5 <= 15) {
if (location[n][m] == -1) machineChessmanNum++;
else if (location[n][m] == 1) humanChessmanNum++;
m++;
n--;
}
if (m == k + 5) {
tupleScoreTmp = tupleScore(machineChessmanNum, humanChessmanNum);
for (m = k, n = j; m < k + 5; m++, n--) {
score[n][m] += tupleScoreTmp;
}
}
//歸零
humanChessmanNum = 0;
machineChessmanNum = 0;
tupleScoreTmp = 0;
}
}
//5.掃描左對角線上側部分
for (int i = 0; i < 11; i++) {
for (int k = i, j = 0; j < 15 && k < 15; j++, k++) {
int m = k;
int n = j;
while (m < k + 5 && k + 5 <= 15) {
if (location[m][n] == -1) machineChessmanNum++;
else if (location[m][n] == 1) humanChessmanNum++;
m++;
n++;
}
if (m == k + 5) {
tupleScoreTmp = tupleScore(machineChessmanNum, humanChessmanNum);
for (m = k, n = j; m < k + 5; m++, n++) {
score[m][n] += tupleScoreTmp;
}
}
//歸零
humanChessmanNum = 0;
machineChessmanNum = 0;
tupleScoreTmp = 0;
}
}
//6.掃描左對角線下側部分
for (int i = 1; i < 11; i++) {
for (int k = i, j = 0; j < 15 && k < 15; j++, k++) {
int m = k;
int n = j;
while (m < k + 5 && k + 5 <= 15) {
if (location[n][m] == -1) machineChessmanNum++;
else if (location[n][m] == 1) humanChessmanNum++;
m++;
n++;
}
if (m == k + 5) {
tupleScoreTmp = tupleScore(machineChessmanNum, humanChessmanNum);
for (m = k, n = j; m < k + 5; m++, n++) {
score[n][m] += tupleScoreTmp;
}
}
//歸零
humanChessmanNum = 0;
machineChessmanNum = 0;
tupleScoreTmp = 0;
}
}
//從空位置中找到最大的位置
for (int i = 0; i < 15; i++) {
for (int j = 0; j < 15; j++) {
if (location[i][j] == 0 && score[i][j] > maxScore) {
goalX = i;
goalY = j;
maxScore = score[i][j];
}
}
}
if (goalX != -1) {
return new Pieces(goalX, goalY, -1);
}
//沒找到坐標說明平局,暫不處理
return new Pieces(-1, -1, -1);
}
(完)
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的五子棋java判断平局_2020-10-03 Java初级项目——从零开始制作一个简易五子棋游戏...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java移除ssl认证_Java SSL
- 下一篇: java.util.list 赋值_ja