Java五子棋,老程式員也花了3天
作者簡介
作者名:編程界明世隱
簡介:CSDN博客專家,從事軟體開發多年,精通Java、JavaScript,博主也是從零開始一步步把學習成長、深知學習和積累的重要性,喜歡跟廣大ADC一起打野升級,歡迎您關注,期待與您一起學習、成長、起飛!
系列目錄
1. Java俄羅斯方塊
2. 老Java程式員花2天寫了個連連看
3. 老Java程式員花一天時間寫了個飛機大戰
4. Java植物大戰僵尸
5. Java消消樂(天天愛消除)
6. Java貪吃蛇小游戲
7. Java掃雷小游戲
8. Java坦克大戰
效果圖

實作思路
1.創建運行視窗并添加背景色,
2.繪制棋盤,
3.用二維陣列來控制起碼落子位置、繪制指示器,
4.滑鼠在落子位置處點擊可落子,
5.落子后檢查是否獲得勝利,
6.機器判斷下一步,并落子,
7.機器判斷是否獲得勝利,
代碼實作
創建視窗
首先創建一個游戲表單類GameFrame,繼承至JFrame,用來顯示在螢屏上(window的物件),每個游戲都有一個視窗,設定好視窗標題、尺寸、布局等就可以,
/*
* 游戲表單類
*/
public class GameFrame extends JFrame {
public GameFrame() {
setTitle("五子棋");//設定標題
setSize(620, 670);//設定尺寸
getContentPane().setBackground(new Color(209,146,17));//添加背景色
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//點擊關閉按鈕是關閉程式
setLocationRelativeTo(null); //設定居中
setResizable(false); //不允許修改界面大小
}
}
創建面板容器GamePanel繼承至JPanel
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* 畫布類
*/
public class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
GamePanel gamePanel=this;
private JFrame mainFrame=null;
//構造里面初始化相關引數
public GamePanel(JFrame frame){
this.setLayout(null);
this.setOpaque(false);
mainFrame = frame;
mainFrame.requestFocus();
mainFrame.setVisible(true);
}
}
再創建一個Main類,來啟動這個視窗,
public class Main {
//主類
public static void main(String[] args) {
GameFrame frame = new GameFrame();
GamePanel gamePanel = new GamePanel(frame);
frame.add(gamePanel);
frame.setVisible(true);//設定顯示
}
}
右鍵執行這個Main類,視窗建出來了

創建選單及選單選項
創建選單
private void initMenu(){
// 創建選單及選單選項
jmb = new JMenuBar();
JMenu jm1 = new JMenu("游戲");
jm1.setFont(new Font("思源宋體", Font.BOLD, 18));// 設定選單顯示的字體
JMenu jm2 = new JMenu("幫助");
jm2.setFont(new Font("思源宋體", Font.BOLD, 18));// 設定選單顯示的字體
JMenuItem jmi1 = new JMenuItem("開始新游戲");
JMenuItem jmi2 = new JMenuItem("退出");
jmi1.setFont(new Font("思源宋體", Font.BOLD, 18));
jmi2.setFont(new Font("思源宋體", Font.BOLD, 18));
JMenuItem jmi3 = new JMenuItem("操作說明");
jmi3.setFont(new Font("思源宋體", Font.BOLD, 18));
JMenuItem jmi4 = new JMenuItem("成功/失敗判定");
jmi4.setFont(new Font("思源宋體", Font.BOLD, 18));
jm1.add(jmi1);
jm1.add(jmi2);
jm2.add(jmi3);
jm2.add(jmi4);
jmb.add(jm1);
jmb.add(jm2);
mainFrame.setJMenuBar(jmb);// 選單Bar放到JFrame上
jmi1.addActionListener(this);
jmi1.setActionCommand("Restart");
jmi2.addActionListener(this);
jmi2.setActionCommand("Exit");
jmi3.addActionListener(this);
jmi3.setActionCommand("help");
jmi4.addActionListener(this);
jmi4.setActionCommand("lost");
}
實作ActionListener并重寫方法actionPerformed

此時GamePanel是報錯的,重寫actionPerformed方法,
actionPerformed方法的實作
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
System.out.println(command);
UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋體", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋體", Font.ITALIC, 18)));
if ("Exit".equals(command)) {
Object[] options = { "確定", "取消" };
int response = JOptionPane.showOptionDialog(this, "您確認要退出嗎", "",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) {
System.exit(0);
}
}else if("Restart".equals(command)){
if(!"end".equals(gamePanel.gameFlag)){
JOptionPane.showMessageDialog(null, "正在游戲中無法重新開始!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}else{
if(gamePanel!=null) {
gamePanel.restart();
}
}
}else if("help".equals(command)){
JOptionPane.showMessageDialog(null, "滑鼠在指示器位置點下,則落子!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}else if("lost".equals(command)){
JOptionPane.showMessageDialog(null, "五子連珠方獲得勝利!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}
}
此時的GamePanel代碼如下:
package main;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;
/*
* 畫布類
*/
public class GamePanel extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
GamePanel gamePanel=this;
private JFrame mainFrame=null;
JMenuBar jmb=null;
public String gameFlag="";
//構造里面初始化相關引數
public GamePanel(JFrame frame){
this.setLayout(null);
this.setOpaque(false);
mainFrame = frame;
//創建按鈕
initMenu();
mainFrame.requestFocus();
mainFrame.setVisible(true);
}
private void initMenu(){
// 創建選單及選單選項
jmb = new JMenuBar();
JMenu jm1 = new JMenu("游戲");
jm1.setFont(new Font("思源宋體", Font.BOLD, 18));// 設定選單顯示的字體
JMenu jm2 = new JMenu("幫助");
jm2.setFont(new Font("思源宋體", Font.BOLD, 18));// 設定選單顯示的字體
JMenuItem jmi1 = new JMenuItem("開始新游戲");
JMenuItem jmi2 = new JMenuItem("退出");
jmi1.setFont(new Font("思源宋體", Font.BOLD, 18));
jmi2.setFont(new Font("思源宋體", Font.BOLD, 18));
JMenuItem jmi3 = new JMenuItem("操作說明");
jmi3.setFont(new Font("思源宋體", Font.BOLD, 18));
JMenuItem jmi4 = new JMenuItem("成功/失敗判定");
jmi4.setFont(new Font("思源宋體", Font.BOLD, 18));
jm1.add(jmi1);
jm1.add(jmi2);
jm2.add(jmi3);
jm2.add(jmi4);
jmb.add(jm1);
jmb.add(jm2);
mainFrame.setJMenuBar(jmb);// 選單Bar放到JFrame上
jmi1.addActionListener(this);
jmi1.setActionCommand("Restart");
jmi2.addActionListener(this);
jmi2.setActionCommand("Exit");
jmi3.addActionListener(this);
jmi3.setActionCommand("help");
jmi4.addActionListener(this);
jmi4.setActionCommand("lost");
}
//重新開始
public void restart() {
//游戲開始標記
gameFlag="start";
}
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
System.out.println(command);
UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋體", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋體", Font.ITALIC, 18)));
if ("Exit".equals(command)) {
Object[] options = { "確定", "取消" };
int response = JOptionPane.showOptionDialog(this, "您確認要退出嗎", "",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) {
System.exit(0);
}
}else if("Restart".equals(command)){
if(!"end".equals(gamePanel.gameFlag)){
JOptionPane.showMessageDialog(null, "正在游戲中無法重新開始!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}else{
if(gamePanel!=null) {
gamePanel.restart();
}
}
}else if("help".equals(command)){
JOptionPane.showMessageDialog(null, "滑鼠在指示器位置點下,則落子!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}else if("lost".equals(command)){
JOptionPane.showMessageDialog(null, "五子連珠方獲得勝利!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}
}
}
運行一下

繪制棋盤
重寫paint方法
@Override
public void paint(java.awt.Graphics g) {
super.paint(g);
}
繪制橫豎相交接的線
定義15行、15列
public static final int ROWS=15;
public static final int COLS=15;
繪制網格
//繪制網格
private void drawGrid(Graphics g) {
Graphics2D g_2d=(Graphics2D)g;
int start=26;
int x1=start;
int y1=20;
int x2=586;
int y2=20;
for (int i = 0; i < ROWS; i++) {
y1 = start + 40*i;
y2 = y1;
g_2d.drawLine(x1, y1, x2, y2);
}
y1=start;
y2=586;
for (int i = 0; i < COLS; i++) {
x1 = start + 40*i;
x2 = x1;
g_2d.drawLine(x1, y1, x2, y2);
}
}
繪制5個圓點
//繪制5個黑點
private void draw5Point(Graphics g) {
//第1個點
g.fillArc(142, 142, 8, 8, 0, 360);
//第2個點
g.fillArc(462, 142, 8, 8, 0, 360);
//第3個點
g.fillArc(142, 462, 8, 8, 0, 360);
//第4個點
g.fillArc(462, 462, 8, 8, 0, 360);
//中心點
g.fillArc(302, 302, 8, 8, 0, 360);
}
在paint方法里面呼叫以上2個方法
@Override
public void paint(java.awt.Graphics g) {
super.paint(g);
//繪制網格
drawGrid(g);
//繪制5個黑點
draw5Point(g);
}
棋盤已經繪制完成

實作落子指示器
- 創建指示器類
package main;
import java.awt.Color;
import java.awt.Graphics;
//指示器類
public class Pointer {
private int i=0;//二維下標i
private int j=0;//二維下標j
private int x=0;//坐標X
private int y=0;//坐標Y
private GamePanel panel=null;
private Color color=null;
private int h=40;//指示的大小
private boolean isShow=false;//是否展示
private int qizi = 0 ;//棋子型別 0:無 1:白棋 2:黑棋
public Pointer(int x,int y,int i,int j,Color color,GamePanel panel){
this.x=x;
this.y=y;
this.i=i;
this.j=j;
this.panel=panel;
this.color=color;
}
//繪制
void draw(Graphics g){
Color oColor = g.getColor();
if(color!=null){
g.setColor(color);
}
if(isShow){
//繪制指示器
g.drawRect(x-h/2, y-h/2, h, h);
}
if(color!=null){//用完設定回去顏色
g.setColor(oColor);
}
}
//判斷滑鼠是否在指標范圍內
boolean isPoint(int x,int y){
//大于左上角,小于右下角的坐標則肯定在范圍內
if(x>this.x-h/2 && y >this.y-h/2
&& x<this.x+h/2 && y <this.y+h/2){
return true;
}
return false;
}
public boolean isShow() {
return isShow;
}
public void setShow(boolean isShow) {
this.isShow = isShow;
}
public int getQizi() {
return qizi;
}
public void setQizi(int qizi) {
this.qizi = qizi;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
}
- 初始化二維陣列
public Pointer points[][] = new Pointer[ROWS][COLS];
- 創建指示器實體物件
//創建二維陣列
private void createArr() {
int x=0,y=0;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
y = 26 + 40*i;
x = 26 + 40*j;
Pointer pointer = new Pointer(x, y, i,j,new Color(255,0,0), this);
points[i][j] = pointer;
}
}
}
- 初始化呼叫
//初始化相關物件
private void init() {
createArr();
//游戲開始標記
gameFlag="start";
}
同時 init方法 在GamePanel 的構造方法呼叫,
- paint方法中遍歷二維陣列并且繪制,
@Override
public void paint(java.awt.Graphics g) {
super.paint(g);
//繪制網格
drawGrid(g);
//繪制5個黑點
draw5Point(g);
//繪制指示器
Pointer pointer = null;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
pointer = points[i][j] ;
if(pointer!=null){
pointer.draw(g);
}
}
}
}
運行如下

但是這個指示器方塊不是我們想要的,需要改一下
- 修改指示器類的繪圖代碼
在Pointer方法中新增方法,并在指示器繪制的時候呼叫此方法,而不是之前的drawRect,
private void drawPointer(Graphics g) {
Graphics2D g2 = (Graphics2D)g; //g是Graphics物件
g2.setStroke(new BasicStroke(2.0f));
/*
* 1.先計算4個頂點
* 2.依次從每個頂點繪制橫豎兩條線
*/
//左上角
int x1 = x-h/2;
int y1 = y-h/2;
//向右畫線
int x2 = x1+1*h/4;
int y2 = y1;
g2.drawLine(x1, y1, x2, y2);
//向下畫線
x2 = x1;
y2 = y1+1*h/4;
g2.drawLine(x1, y1, x2, y2);
//右上角
x1 = x+h/2;
y1 = y-h/2;
//向左畫線
x2 = x1-1*h/4;
y2 = y1;
g2.drawLine(x1, y1, x2, y2);
//向下畫線
x2 = x1;
y2 = y1+1*h/4;
g2.drawLine(x1, y1, x2, y2);
//右下角
x1 = x+h/2;
y1 = y+h/2;
//向左畫線
x2 = x1-1*h/4;
y2 = y1;
g2.drawLine(x1, y1, x2, y2);
//向上畫線
x2 = x1;
y2 = y1-1*h/4;
g2.drawLine(x1, y1, x2, y2);
//左下角
x1 = x-h/2;
y1 = y+h/2;
//向右畫線
x2 = x1+1*h/4;
y2 = y1;
g2.drawLine(x1, y1, x2, y2);
//向上畫線
x2 = x1;
y2 = y1-1*h/4;
g2.drawLine(x1, y1, x2, y2);
}
再運行

落子
- 創建ImageValue加載類
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageValue {
public static BufferedImage whiteQiziImage ;
public static BufferedImage blackQiziImage ;
//路徑
public static String ImagePath = "/images/";
//將圖片初始化
public static void init(){
String whitePath = ImagePath +"white.png";
String blackPath = ImagePath +"black.png";
try {
whiteQiziImage = ImageIO.read(ImageValue.class.getResource(whitePath));
blackQiziImage = ImageIO.read(ImageValue.class.getResource(blackPath));
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 創建落子類
import java.awt.Color;
import java.awt.Graphics;
import common.ImageValue;
public class Qizi {
private int x = 0;
private int y = 0;
private int r = 36;
private GamePanel panel = null;
private Color color = null;
private int type = 1;// 棋子型別 1:白棋 2:黑棋
public Qizi(int x, int y, int type, GamePanel panel) {
this.x = x;
this.y = y;
this.panel = panel;
this.type=type;
}
// 繪制
void draw(Graphics g) {
Color oColor = g.getColor();
if (type == 1) {// 白色
g.drawImage(ImageValue.whiteQiziImage, x - r / 2, y - r / 2,r,r, null);
} else {// 黑色
g.drawImage(ImageValue.blackQiziImage, x - r / 2, y - r / 2,r,r, null);
}
if (color != null) {// 用完設定回去顏色
g.setColor(oColor);
}
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
- 在createMouseListener方法中重寫mouseClicked,創建棋子
@Override
public void mouseClicked(MouseEvent e) {
//在合適的位置點擊則進行落子操作
if(!"start".equals(gameFlag)) return ;
int x = e.getX();
int y = e.getY();
Pointer pointer;
for (int i = 0; i <ROWS; i++) {
for (int j = 0; j < COLS; j++) {
pointer = points[i][j];
if(pointer==null)continue;
//被點擊,且沒有棋子,則可以落子
if(pointer.isPoint(x, y) && pointer.getQizi()==0){
Qizi qizi = new Qizi(pointer.getX(), pointer.getY(), 2, gamePanel);
pointer.setQizi(2);
qizis.add(qizi);
//重繪畫布
repaint();
return ;
}
}
}
}
- 在paint 方法中繪制棋子
@Override
public void paint(java.awt.Graphics g) {
super.paint(g);
//繪制網格
drawGrid(g);
//繪制5個黑點
draw5Point(g);
//繪制指示器
Pointer pointer = null;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
pointer = points[i][j] ;
if(pointer!=null){
pointer.draw(g);
}
}
}
//繪制棋子
Qizi qizi=null;
for (int i = 0; i < qizis.size(); i++) {
qizi = (Qizi)qizis.get(i);
qizi.draw(g);
}
}

加入電腦AI
- 創建AI類
- 創建靜態方法next(下一步)
- 創建靜態方法has5(連成5子)
public class AI {
//AI進行下一步
static void next(GamePanel gamePanel){
}
//判斷五子連珠
static boolean has5(Pointer pointer1,GamePanel gamePanel){
return false;
}
}
- 在你落子后,會先執行has5方法,根據回傳來決定走向
- has5回傳true則表示你獲得勝利,否則AI將會走一步棋
在剛才的mouseClicked修改代碼
@Override
public void mouseClicked(MouseEvent e) {
//在合適的位置點擊則進行落子操作
if(!"start".equals(gameFlag)) return ;
int x = e.getX();
int y = e.getY();
Pointer pointer;
for (int i = 0; i <ROWS; i++) {
for (int j = 0; j < COLS; j++) {
pointer = points[i][j];
if(pointer==null)continue;
//被點擊,且沒有棋子,則可以落子
if(pointer.isPoint(x, y) && pointer.getQizi()==0){
Qizi qizi = new Qizi(pointer.getX(), pointer.getY(), 2, gamePanel);
pointer.setQizi(2);
qizis.add(qizi);
//重繪畫布
repaint();
//判斷有沒有五子連珠的情況
if(AI.has5(pointer, gamePanel)){
gamePanel.gameWin();
}else{
AI.next(gamePanel);
}
return ;
}
}
}
}
- 在AI類中隨機落子(創建方法)
- 隨機獲取下標 i 和 j,
- 通過下標從二維陣列取到指示器物件,
- 如果此指示器被占用則再次隨機(遞回),直到正確獲取到指示器物件,
- 在此指示器位置,創建棋子物件并更新指示器的棋子資訊,
- Qizi類中加入last屬性,表示AI下的最后一個棋子,
- 根據last在對應的位置創建一個小紅方塊標示AI的最后落子,
//隨機落子
static boolean luoziRandom(GamePanel gamePanel){
Pointer pointer = getRandomPointer(gamePanel);
luozi(pointer, gamePanel,1);
return true;
}
//獲取隨機下的棋子
static Pointer getRandomPointer(GamePanel gamePanel){
Random random = new Random();
int i = random.nextInt(gamePanel.ROWS);
int j = random.nextInt(gamePanel.COLS);
//取得隨機到的格子
Pointer pointer = gamePanel.points[i][j];
if(pointer.getQizi()!=0){//如果當前格子已經下了棋子,則遞回重新取
pointer = getRandomPointer(gamePanel);
}
return pointer;
}
//AI落子操作
static void luozi(Pointer pointer,GamePanel gamePanel,int type){
if(pointer.getQizi()==0){//如果沒有棋子,則落子
Qizi qizi = new Qizi(pointer.getX(), pointer.getY(), type, gamePanel);
qizi.setLast(true);
pointer.setQizi(type);
gamePanel.qizis.add(qizi);
//重繪畫布
gamePanel.repaint();
//判斷電腦有沒有五子連珠的情況
if(AI.has5(pointer, gamePanel)){
gamePanel.gameOver();
}
}
}
- AI的next方法呼叫隨機落子
//AI進行下一步
static void next(GamePanel gamePanel){
luoziRandom(gamePanel);
}
運行效果:

- 小紅方塊一直在,修改代碼
僅需在落子前將其他小紅方塊清除即可
//清除電腦棋子的最后一個棋子指示器
private void clearAILast() {
Qizi qizi;
for (int i = 0; i < qizis.size(); i++) {
qizi = (Qizi)qizis.get(i);
if(qizi!=null && qizi.getType()==1){
qizi.setLast(false);
}
}
}


AI演算法
棋子的4個方向
-
橫向

-
豎向

-
右捺

-
左撇

權重分
描述:計算出分數,最高的分數來決定下一步的落子,
左開:就是說左邊可落子
右開:右邊可落子
3子的相關定義(4、5子類似)
什么是3子左開,就是目前有2個子,下一個子可以落子左邊

3子只能落子在中間,圖示:

3子右開就是落子在右邊

只計算3個子以上的分數
| 型別 | 3子左開 | 3子 | 3子右開 |
|---|---|---|---|
| 得分 | 32 | 30 | 31 |
| 型別 | 4子左開 | 4子 | 4子右開 |
|---|---|---|---|
| 得分 | 42 | 40 | 41 |
| 型別 | 5子左開 | 5子 | 5子右開 |
|---|---|---|---|
| 得分 | 52 | 50 | 51 |
通過上述表可以看到,落子權重分順序:5>4>3,同時:左>右>中,
計算橫向權重分
- 從左往右判斷
* 橫向下標 i 是一樣的,回圈從當前位置 j 加1開始,
* 當碰到和當前子一樣的就計數器 +1,
* 當碰到不一樣的就退出回圈,表示堵住 ,
* 如果碰到空子,是第一次計數器 +1,第二次退出回圈,
* 判斷左開和右開的狀態,
* 根據計數器和左右開的狀態,計算出分數
- 從右往左判斷
* 橫向下標 i 是一樣的,回圈從當前位置 j 減1開始,
* 當碰到和當前子一樣的就計數器 +1,
* 當碰到不一樣的就退出回圈,表示堵住 ,
* 如果碰到空子,是第一次計數器 +1,第二次退出回圈,
* 判斷左開和右開的狀態,
* 根據計數器、左右開的狀態,計算出分數和落子的位置,
其實左右還是很相似的
static Data getData(Pointer pointer,int dir,int type,GamePanel gamePanel){
Pointer[][] points = gamePanel.points;
int i = pointer.getI();
int j = pointer.getJ();
Data resData = new Data();
Pointer tempPointer;
int num=1;//默認是1,因為其中自己就是一個子,
int num2=1;//默認是1,用來累加連續的棋子數
int breakNum=0;//默認是0,有一個則不能通過了,
boolean lClosed=false;//左邊是否關閉
boolean rClosed=false;//右邊是否關閉
if(dir==1){//橫向
//往右回圈,判斷能與當前pointer 相同的棋子連續多少個,
if(type==1){
for (int k = j+1; k < gamePanel.COLS; k++) {
tempPointer = points[i][k];
if(tempPointer.getQizi()==pointer.getQizi()){//連續
num++;
num2++;
if(k == gamePanel.COLS-1){//如果最后一個子也是連續的,則也是右關閉的
rClosed = true;
}
}else if(tempPointer.getQizi()==0){//空白子
if(breakNum==1){//有一個則不能通過了
if(points[i][k-1].getQizi()==0){//如果前一個是空子,要設定成不是中斷的
breakNum=0;
}else{
breakNum=2;
}
break;
}
breakNum=1;
num++;
//是中斷的那種,這里設定好落子位置
resData.setI(i);
resData.setJ(k);
}else{//對立子,右關閉
rClosed = true;
break;
}
}
//判斷是否左關閉
if(j==0){//當前子就是最左邊的子
lClosed = true;
}else{
tempPointer = points[i][j-1];
if(tempPointer.getQizi()!=0){//如果當前子的左邊有子,則左關閉
lClosed = true;
}
}
}else{//從右往左
for (int k = j-1; k >=0; k--) {
tempPointer = points[i][k];
if(tempPointer.getQizi()==pointer.getQizi()){//連續
num++;
num2++;
if(k == 0){//如果最后一個子也是連續的,則也是左關閉的
lClosed = true;
}
}else if(tempPointer.getQizi()==0){//空白子
if(breakNum==1){//有一個則不能通過了,
if(points[i][k+1].getQizi()==0){//如果前一個是空子,要設定成不是中斷的
breakNum=0;
}else{
breakNum=2;
}
break;
}
breakNum=1;
num++;
//是中斷的那種,這里設定好落子位置
resData.setI(i);
resData.setJ(k);
}else{//對立子,左關閉
lClosed = true;
break;
}
}
//判斷是否右關閉
if(j==gamePanel.COLS-1){//當前子就是最右邊的子
rClosed = true;
}else{
tempPointer = points[i][j+1];
if(tempPointer.getQizi()!=0){//如果當前子的右邊有子,則右關閉
rClosed = true;
}
}
}
}
setCount(resData, i, j, dir, type, num,num2, breakNum, lClosed, rClosed);
return resData;
}
//計算并設定分數
static void setCount(Data data,int i,int j,int dir,int type,
int num,int num2,int breakNum,boolean lClosed,boolean rClosed){
int count=0;
if(num>2){//連續3個子以上
if(num==3){//設定默認分
count=30;
}else if(num==4){
count=40;
}else if(num==5){
count=50;
}
if(num2>=5&&breakNum==0){//用來判斷是否五子或五子以上
count=100;
//設定好權重分
data.setCount(count);
return ;
}
if(breakNum==0){//如果不是中斷的那種
if(lClosed&&rClosed){//如果沒有中斷,并且左右都關閉了,則分數為-1,-1表示落子的時候要過濾掉
count = -1;
}else if(!lClosed){//如果是中斷的那種,左邊未關閉
count+=2;//加2分
if(dir==1){
if(type==1){
data.setI(i);
data.setJ(j-1);
}else{
data.setI(i);
data.setJ(j-num+1);
}
}else if(dir==2){
if(type==1){
data.setI(i-1);
data.setJ(j);
}else{
data.setI(i-num+1);
data.setJ(j);
}
}else if(dir==3){
if(type==1){
data.setI(i-1);
data.setJ(j-1);
}else{
data.setI(i-num+1);
data.setJ(j-num+1);
}
}else if(dir==4){
if(type==1){
data.setI(i+1);
data.setJ(j-1);
}else{
data.setI(i+num-1);
data.setJ(j-num+1);
}
}
}else if(!rClosed){//如果是中斷的那種,右邊未關閉
count+=1;//加1分
if(dir==1){
if(type==1){
data.setI(i);
data.setJ(j+num-1);
}else{
data.setI(i);
data.setJ(j+1);
}
}else if(dir==2){
if(type==1){
data.setI(i+num-1);
data.setJ(j);
}else{
data.setI(i+1);
data.setJ(j);
}
}else if(dir==3){
if(type==1){
data.setI(i+num-1);
data.setJ(j+num-1);
}else{
data.setI(i+1);
data.setJ(j+1);
}
}else if(dir==4){
if(type==1){
data.setI(i-num+1);
data.setJ(j+num-1);
}else{
data.setI(i-1);
data.setJ(j+1);
}
}
}
}else{//如果中斷,
if(num!=5){//num不是5, 并且左右都關閉,也要過濾
if(lClosed&&rClosed){
count = -1;
}
}
}
//設定好權重分
data.setCount(count);
}
}
AI落子處理
- 回圈取橫向、豎向、右捺、左撇 4種分數,放到List集合中
- 對集合進行排序(分數從高到底)
- 第一個分數值作為下一步落子的位置
- 落子操作(如果集合沒有值,則進行隨機落子)
//進行下一步
static boolean go(GamePanel gamePanel){
List<Data> datas=new ArrayList<Data>();
//回圈找出黑棋,判斷此棋子的1橫向 2縱向 3右捺 4左撇 是否有4子的情況,
Pointer pointer;
for (int i = 0; i <gamePanel.ROWS; i++) {
for (int j = 0; j < gamePanel.COLS; j++) {
pointer = gamePanel.points[i][j];
if(pointer==null)continue;
if(pointer.getQizi()==0){//沒有棋子則跳過
continue;
}
//回圈4個方向
int dir=1;
for (int k = 1; k <= 4; k++) {
dir = k;
Data data = getData(pointer, dir,1, gamePanel);
if(data.getCount()!=-1&&data.getCount()!=0){//0和-1 的過濾掉
datas.add(data);
}
data = getData(pointer, dir, 2,gamePanel);
if(data.getCount()!=-1&&data.getCount()!=0){//0和-1 的過濾掉
datas.add(data);
}
}
}
}
//按權重分排序處理,從大到小
Collections.sort(datas, new DataCount());
/*for (int i = 0; i < datas.size(); i++) {
System.out.println("----------"+datas.get(i).getCount());
}*/
if(datas.size()>0){//取第一個位置落子
Data data = datas.get(0);
Pointer p = gamePanel.points[data.getI()][data.getJ()];
luozi(p, gamePanel, 1);
return true;
}
return false;
}
五子或者以上的判斷就很簡單了,當棋子是連續的并且計數器大于5就成功了!
這里只介紹了橫向的,另外3個情況也差不多,就是注意下標的處理即可,

最后
1. AI還不是特別智能,應該算簡單版吧,贏的難度不大.
2. 可能會有我沒發現的bug吧,望理解!
看到這里的大佬,動動發財的小手 點贊 + 回復 + 收藏,能【 關注 】一波就更好了,
相關閱讀
1. Java俄羅斯方塊
2. 老Java程式員花2天寫了個連連看
3. 老Java程式員花一天時間寫了個飛機大戰
4. JavaWeb圖書管理系統
5. JavaWeb學生宿舍管理系統
6. JavaWeb在線考試系統
為了幫助更多小白從零進階 Java 工程師,從CSDN官方那邊搞來了一套 《Java 工程師學習成長知識圖譜》,尺寸 870mm x 560mm,展開后有一張辦公桌大小,也可以折疊成一本書的尺寸,原件129元現價 29 元,先到先得,有興趣的小伙伴可以了解一下!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289576.html
標籤:java
