1)JFrame窗体需要设置基本的大小、布局、默认的关闭方式,以及最重要的设置可见。
1)棋盘棋子的绘制:自定义一个类去继承JPanel,把绘制棋盘和棋子的方法重写进入paint()方法里,这样当窗体发生变化(放大、缩小、移动等操作时,棋盘棋子不会消失,棋局得以保存)。
2)悔棋、认输等操作通过JButton按钮添加鼠标监听来实现。
1)给棋盘添加鼠标监听,获取点击位置的坐标
UI类负责初始化1中的图形化界面,并给DrawChessBoard类添加GameMouse类的监听。这样在鼠标点击棋盘时通过重写GameMouse类的mouseClicked(),就可以获取鼠标在棋盘上点击的像素坐标。
2)坐标转化成二位数组中的坐标
保存棋盘上所有棋子位置用到QiZi类中的int[][] memory二维数组,这样把像素坐标转化为二维数组中的坐标,并附上棋子颜色对应的值,就可以保存棋盘上所有棋子的位置。
3)在棋盘上画出棋子
在DrawChessBoard类paint()方法中遍历QiZi类中的int[][] memory二维数组非零值,就可以在相应位置调用画笔方法画出黑白棋子。
1)下完棋子后,将下棋位置保存到QiZi类的int[][] memory二维数组中后,就可以以该点为中心计算其四个方向上连续棋子的数目,达到五个则通过JOptionPane类生成弹窗确定赢家。
2.2悔棋功能和提示最后落子位置功能的实现
1)每次成功下一颗棋子,就可以创建一个保存了棋子坐标的Location对象,并将该对象添加到 QiZi类的ArrayList或者Stack容器当中,当鼠标点击悔棋Button后,清除QiZi类的int[][] memory二维数组相应位置的数值(将之改为0即可),然后棋盘重绘棋子,就可以完成悔棋的效果。
2)同时可以找到容器中最后落子的位置,并在棋盘相应的坐标出画出最后落子提示。
1)开始游戏,即重置游戏,将棋子类的相应属性清零即可,比如int[][] memory二维数组(即棋谱),owener=1(重置为白色),以及清楚棋盘上面的棋子。
2)认输就可以判断当前QiZi.owner的值,来判断输的一方并给出提示即可。
1)默认AI为黑方的情况下,需要在白色方落子之后调用AI下黑色棋子,所以在需要在GameMouse中下白棋的if分支中调用AI方法
2)AI的厉害与否取决于其设计,在这里提供一个思路:设置一个棋型对照表,给不同棋型赋值(如1111,代表白子四连,权重较高),轮到AI时,可以根据该表计算棋盘上每一个空位在八个方向总的权重大小,在权重最大处落子即可。棋型对照表中不同棋的权重设置,可以通过python等分析大量棋局来获取,以此来训练AI,当权重设置越合理,AI就越强。
下子的动画效果音效等可以通过开辟不同的线程来实现,而网络对战则可增加网络通信相关模块即可。
package wuziqi925;
import javax.swing.*;
import java.awt.*;
public class GameUI {
public static void main(String[] args) {
GameUI gameUI=new GameUI();
gameUI.showUI();
}
public void showUI(){
//创建棋子对象
QiZi qizi=new QiZi();
//获取窗体
JFrame jFrame=new JFrame();
jFrame.setSize(1000,795);
jFrame.setDefaultCloseOperation(3);
jFrame.setLocationRelativeTo(null);
jFrame.setLayout(null);
jFrame.setTitle("五子棋");
jFrame.setResizable(false);
//窗体添加棋盘面板
DrawChessBoard chessBoard=new DrawChessBoard(qizi);
jFrame.add(chessBoard);
chessBoard.setSize(760,760);
chessBoard.setBackground(Color.ORANGE);
//测试JFrame框架像素大小,Insets[top=32,left=3,bottom=3,right=3]
//System.out.println(jFrame.getInsets());
//窗体添加选项面板,用来画棋盘
JPanel bp=new JPanel();
bp.setSize(236,760);
bp.setBackground(Color.lightGray);
bp.setLocation(760,0);
bp.setLayout(null);
jFrame.add(bp);
//选项面板添加按钮
JButton start=new JButton("开始");
start.setBackground(Color.white);
start.setFont(new Font("华文行楷",Font.BOLD,20));
start.setBounds(40,350,150,50);
JButton quit=new JButton("认输");
quit.setBackground(Color.white);
quit.setFont(new Font("华文行楷",Font.BOLD,20));
quit.setBounds(40,440,150,50);
JButton undo=new JButton("悔棋");
undo.setBackground(Color.white);
undo.setFont(new Font("华文行楷",Font.BOLD,20));
undo.setBounds(40,530,150,50);
bp.add(start);
bp.add(quit);
bp.add(undo);
//选择模式选项
ButtonGroup bg=new ButtonGroup();
JRadioButton rrdz=new JRadioButton("玩家对战");
JRadioButton rjdz=new JRadioButton("人机对战");
rrdz.setSize(120,30);
rrdz.setLocation(55,60);
rrdz.setFont(new Font("华文行楷",Font.BOLD,20));
rrdz.setVisible(true);
rjdz.setSize(120,30);
rjdz.setLocation(55,90);
rjdz.setFont(new Font("华文行楷",Font.BOLD,20));
rjdz.setVisible(true);
bg.add(rjdz);
bg.add(rrdz);
bp.add(rjdz);
bp.add(rrdz);
bp.setVisible(true);
//设置窗体可见
jFrame.setVisible(true);
AI ai=new AI(qizi,chessBoard);
//获取棋盘的鼠标监听和画笔并将该画笔添加给鼠标
Graphics g1=chessBoard.getGraphics();
GameMouse gameMouse=new GameMouse(qizi,chessBoard,ai);
chessBoard.addMouseListener(gameMouse);
start.addActionListener(gameMouse);
quit.addActionListener(gameMouse);
undo.addActionListener(gameMouse);
rrdz.addActionListener(gameMouse);
rjdz.addActionListener(gameMouse);
}
}
package wuziqi925;
import javax.swing.*;
import java.awt.*;
public class DrawChessBoard extends JPanel {
QiZi qiZi;
private static int LINE_NUM = 15;
private static int MARGIN_WIDTH = 30;
public static int CELL_SIZE = 50;
public DrawChessBoard(QiZi qiZi) {
this.qiZi = qiZi;
}
@Override
public void paint(Graphics g) {
super.paint(g);
//画棋盘
for (int i = 0; i < LINE_NUM; i++) {
g.drawLine(MARGIN_WIDTH, MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH + (LINE_NUM - 1) * CELL_SIZE, MARGIN_WIDTH + i * CELL_SIZE);
g.drawLine(MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH, MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH + (LINE_NUM - 1) * CELL_SIZE);
}
//画棋盘上的点
g.fillOval(CELL_SIZE*3+22,CELL_SIZE*3+22,16,16);
g.fillOval(CELL_SIZE*11+22,CELL_SIZE*3+22,16,16);
g.fillOval(CELL_SIZE*3+22,CELL_SIZE*11+22,16,16);
g.fillOval(CELL_SIZE*11+22,CELL_SIZE*11+22,16,16);
//画棋子
int[][] a = qiZi.memory;
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
if (a[i][j] == 1) {
g.setColor(Color.white);
g.fillOval(CELL_SIZE * i + 7, CELL_SIZE * j + 7, 46, 46);
} else if (a[i][j] == 2) {
g.setColor(Color.black);
g.fillOval(CELL_SIZE * i + 7, CELL_SIZE * j + 7, 46, 46);
}
}
}
//画出最后一步棋子的位置上的十字架
if (qiZi.arr.size() > 0) {
Graphics2D g1=(Graphics2D)g;
g1.setColor(Color.red);
g1.setStroke(new BasicStroke(3.0f));
Location l = qiZi.arr.get(qiZi.arr.size() - 1);
g1.drawLine(CELL_SIZE * l.x + MARGIN_WIDTH - 8, CELL_SIZE * l.y + MARGIN_WIDTH, CELL_SIZE * l.x + MARGIN_WIDTH + 8, CELL_SIZE * l.y + MARGIN_WIDTH);
g1.drawLine(CELL_SIZE * l.x + MARGIN_WIDTH, CELL_SIZE * l.y + MARGIN_WIDTH - 8, CELL_SIZE * l.x + MARGIN_WIDTH, CELL_SIZE * l.y + MARGIN_WIDTH + 8);
}
}
}
package wuziqi925;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class GameMouse implements MouseListener, ActionListener {
QiZi qizi;
DrawChessBoard drawChessBoard;
AI ai;
public GameMouse() {
}
public GameMouse( QiZi qiZi, DrawChessBoard drawChessBoard,AI ai) {
this.qizi = qiZi;
this.drawChessBoard=drawChessBoard;
this.ai=ai;
}
@Override
public void mouseClicked(MouseEvent e) {
int cellSize= DrawChessBoard.CELL_SIZE;
int x=e.getX();
int xx=0;
int y=e.getY();
int yy=0;
//在点击位置画棋子,并将下子位置转换成棋子在二维数组中的坐标
if (qizi.owner==1) {
if ((x - 30) % cellSize > 25 & (y - 30) % cellSize > 25) {
xx=(x - 30) / cellSize + 1;
yy=(y - 30) / cellSize + 1;
} else if ((x - 30) % cellSize > 25 & (y - 30) % cellSize < 25) {
xx=(x - 30) / cellSize + 1;
yy=(y - 30) / cellSize;
} else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize > 25) {
xx=(x - 30) / cellSize;
yy=(y - 30) / cellSize + 1;
} else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize < 25) {
xx=(x - 30) / cellSize;
yy=(y - 30) / cellSize;
}
if(qizi.memory[xx][yy]==0) {//判断无子
qizi.x=xx;
qizi.y=yy;
qizi.memory[qizi.x][qizi.y] = qizi.owner;//下棋子并将棋子放入容器
Location location=new Location(qizi);//记录刚下的棋子位置顺序
qizi.arr.add(location);
drawChessBoard.repaint();//绘制刚下的棋子
qizi.judgeWinner();//判断输赢
qizi.owner=2;//交换棋权
}
if (ai.state){
ai.initiateAI();
}
}else {
qizi.owner=2;
if ((x - 30) % cellSize > 25 & (y - 30) % cellSize > 25) {
xx=(x - 30) / cellSize + 1;
yy=(y - 30) / cellSize + 1;
} else if ((x - 30) % cellSize > 25 & (y - 30) % cellSize < 25) {
xx=(x - 30) / cellSize + 1;
yy=(y - 30) / cellSize;
} else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize > 25) {
xx=(x - 30) / cellSize;
yy=(y - 30) / cellSize + 1;
} else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize < 25) {
xx=(x - 30) / cellSize;
yy=(y - 30) / cellSize;
}
if(qizi.memory[xx][yy]==0) {
qizi.x=xx;
qizi.y=yy;
qizi.memory[qizi.x][qizi.y] = qizi.owner;//下棋子并将棋子放入容器
Location location=new Location(qizi);//记录刚下的棋子位置顺序
qizi.arr.add(location);
drawChessBoard.repaint();//绘制刚下的棋子
qizi.judgeWinner();//判断输赢
qizi.owner=1;//交换棋权
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
int cellSize= DrawChessBoard.CELL_SIZE;
String s=e.getActionCommand();
JOptionPane tc=new JOptionPane();
//悔棋功能的实现
if (s.equals("悔棋")){
if (qizi.arr.size()>0) {
Location l = qizi.arr.get(qizi.arr.size() - 1);
qizi.memory[l.x][l.y] = 0;
qizi.arr.remove(qizi.arr.size() - 1);
if (qizi.owner == 1) {
qizi.owner = 2;
} else if (qizi.owner == 2) {
qizi.owner = 1;
}
}else {
tc.showMessageDialog(null,"无棋可毁,请下棋!");
}
//刷新棋盘
drawChessBoard.repaint();
}
if (s.equals("开始")){
qizi.owner=1;
qizi.memory=new int[15][15];
qizi.arr.clear();
qizi.win=false;
drawChessBoard.repaint();
}
if(s.equals("认输")){
int whiteCount=0;
int blackCount=0;
for (int i = 0; i < qizi.memory.length; i++) {
for (int j = 0; j < qizi.memory[i].length; j++) {
if (qizi.memory[i][j]==1){
whiteCount++;
}else if (qizi.memory[i][j]==2){
blackCount++;
}
}
}
if (whiteCount==blackCount){
tc.showMessageDialog(null,qizi.owner==1 ?"黑方投降,白方胜!":"白方投降,黑方胜!");
}else if(whiteCount>blackCount){
tc.showMessageDialog(null,"黑方投降,白方胜!");
}else {
tc.showMessageDialog(null,"白方投降,黑方胜!");
}
}
if (s.equals("人机对战")){
ai.state=true;
}
if (s.equals("玩家对战")){
ai.state=false;
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
package wuziqi925;
import javax.swing.*;
import java.util.ArrayList;
public class QiZi {
int x;
int y;
int owner=1;//1 代表白色,2代表黑色
int[][] memory;
ArrayList arr;
boolean win=false;
public QiZi() {
memory=new int[15][15];
arr=new ArrayList<>(50);
}
public void judgeWinner() {
JOptionPane tc=new JOptionPane();
int count1=0;
int count2=0;
int count3=0;
int count4=0;
//竖直方向检测
for (int i = y-1; i >-1 ; i--) {
if (memory[x][i]==owner){
count1++;
}else {
break;
}
}
for (int i = y+1; i <15; i++) {
if (memory[x][i]==owner){
count1++;
}else {
break;
}
}
if (count1 > 3){
tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
win=true;
return;
}
//水平方向检测
for (int i = x-1; i >-1 ; i--) {
if (memory[i][y]==owner){
count2++;
}else {
break;
}
}
for (int i = x+1; i <15; i++) {
if (memory[i][y]==owner){
count2++;
}else {
break;
}
}
if (count2 > 3){
tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
win=true;
return;
}
//在\方向上检测
int yy=y;
for (int i = x+1; i <15; i++) {
if(yy==14){
break;
}
yy++;
if (memory[i][yy]==owner){
count3++;
}else {
break;
}
}
yy=y;
for (int i = x-1; i >-1; i--) {
if (yy==0){
break;
}
yy--;
if (memory[i][yy]==owner){
count3++;
}else {
break;
}
}
if (count3 > 3){
tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
win=true;
return;
}
//在/方向上检测
yy=y;
for (int i = x+1; i <15; i++) {
if(yy==0){
break;
}
yy--;
if (memory[i][yy]==owner){
count4++;
}else {
break;
}
}
yy=y;
for (int i = x-1; i >-1; i--) {
if(yy==14){
break;
}
yy++;
if (memory[i][yy]==owner){
count4++;
}else {
break;
}
}
if (count4 > 3){
tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
win=true;
return;
}
}
}
package wuziqi925;
public class Location {
QiZi qiZi;
int x;
int y;
public Location(QiZi qiZi) {
//记录棋谱
x=qiZi.x;
y=qiZi.y;
}
public Location(int x, int y) {
this.x = x;
this.y = y;
}
}
package wuziqi925;
import java.util.*;
public class AI {
boolean state=false;//true为on false为off
QiZi qiZi;
//存储棋型权值
private HashMap playValueTable=new HashMap<>();
//存储每个可下点的权重大小
private HashMap locationsAndValues=new HashMap<>();
DrawChessBoard drawChessBoard;
int AIDO=0;
public AI(QiZi qiZi,DrawChessBoard drawChessBoard){
this.drawChessBoard=drawChessBoard;
this.qiZi=qiZi;
//1代表该方向为白方棋子(玩家),2代表该方向为黑方棋子(AI)
playValueTable.put("22221",100);
playValueTable.put("2222",100);
playValueTable.put("11112",99);
playValueTable.put("1111",0);
playValueTable.put("2221",40);
playValueTable.put("222",45);
playValueTable.put("1112",35);
playValueTable.put("111",46);
playValueTable.put("22",25);
playValueTable.put("221",20);
playValueTable.put("11",15);
playValueTable.put("112",10);
playValueTable.put("21",5);
playValueTable.put("2",10);
playValueTable.put("1",8);
playValueTable.put("12",2);
}
public void initiateAI(){
if (qiZi.win){
return;
}
int chessValue=0;
//遍历棋盘找到空点
for (int i = 0; i locationsAndValues进行排序找到最大值
Set> set=locationsAndValues.entrySet();
List> list=new ArrayList<>(set);
list.sort(new Comparator>() {
@Override
public int compare(Map.Entry o1, Map.Entry o2) {
return o2.getValue()-o1.getValue();
}
});
//排序完毕取最大//获取最大权重值对应的空点坐标
Map.Entry maxSet=list.get(0);
Location toDo=maxSet.getKey();
qiZi.x= toDo.x;
qiZi.y= toDo.y;
qiZi.memory[qiZi.x][qiZi.y]=qiZi.owner;
Location location=new Location(qiZi);//记录刚下的棋子位置顺序
qiZi.arr.add(location);
drawChessBoard.repaint();//绘制刚下的棋子
qiZi.judgeWinner();//判断输赢
qiZi.owner=1;//交换棋权
System.out.println(++AIDO);
locationsAndValues.clear();
}
private int countValue(int i,int j) {
int totalValue=0;
StringBuilder s1=new StringBuilder();
StringBuilder s2=new StringBuilder();
StringBuilder s3=new StringBuilder();
StringBuilder s4=new StringBuilder();
StringBuilder s5=new StringBuilder();
StringBuilder s6=new StringBuilder();
StringBuilder s7=new StringBuilder();
StringBuilder s8=new StringBuilder();
//八个方向去去判定
//North
for (int k = j-1; k >-1 ; k--) {
if (qiZi.memory[i][k]==1){
s1.append(1);
}else if (qiZi.memory[i][k]==2) {
s1.append(2);
}else {
break;
}
}
int count1=playValueTable.get(s1.toString())==null?0:playValueTable.get(s1.toString());
totalValue+=count1;
//South
for (int k = j+1; k <15; k++) {
if (qiZi.memory[i][k]==1){
s2.append(1);
}else if (qiZi.memory[i][k]==2) {
s2.append(2);
}else {
break;
}
}
int count2=playValueTable.get(s2.toString())==null?0:playValueTable.get(s2.toString());
totalValue+=count2;
//West
for (int k = i-1; k >-1 ; k--) {
if (qiZi.memory[k][j]==1){
s3.append(1);
}else if (qiZi.memory[k][j]==2) {
s3.append(2);
}else {
break;
}
}
int count3=playValueTable.get(s3.toString())==null?0:playValueTable.get(s3.toString());
totalValue+=count3;
//East
for (int k = i+1; k <15; k++) {
if (qiZi.memory[k][j]==1){
s4.append(1);
}else if (qiZi.memory[k][j]==2) {
s4.append(2);
}else {
break;
}
}
int count4=playValueTable.get(s4.toString())==null?0:playValueTable.get(s4.toString());
totalValue+=count4;
//SE
int yy=j;
for (int k = i+1; k < 15; k++) {
if(yy==14){
break;
}
yy++;
if (qiZi.memory[k][yy]==1){
s5.append(1);
}else if (qiZi.memory[k][yy]==2) {
s5.append(2);
}else {
break;
}
}
int count5=playValueTable.get(s5.toString())==null?0:playValueTable.get(s5.toString());
totalValue+=count5;
//NW
yy=j;
for (int k = i-1; k >-1; k--) {
if(yy==0){
break;
}
yy--;
if (qiZi.memory[k][yy]==1){
s6.append(1);
}else if (qiZi.memory[k][yy]==2) {
s6.append(2);
}else {
break;
}
}
int count6=playValueTable.get(s6.toString())==null?0:playValueTable.get(s6.toString());
totalValue+=count6;
//NE
yy=j;
for (int k = i+1; k <15; k++) {
if(yy==0){
break;
}
yy--;
if (qiZi.memory[k][yy]==1){
s7.append(1);
}else if (qiZi.memory[k][yy]==2) {
s7.append(2);
}else {
break;
}
}
int count7=playValueTable.get(s7.toString())==null?0:playValueTable.get(s7.toString());
totalValue+=count6;
//SW
yy=j;
for (int k = i-1; k >-1; k--) {
if(yy==14){
break;
}
yy++;
if (qiZi.memory[k][yy]==1){
s8.append(1);
}else if (qiZi.memory[k][yy]==2) {
s8.append(2);
}else {
break;
}
}
int count8=playValueTable.get(s8.toString())==null?0:playValueTable.get(s8.toString());
totalValue+=count8;
return totalValue;
}
}