目录
1.简介
我们的课程设计题目是《五子棋大战》,该项目基于java swing进行开发,实现了人与电脑的五子棋对战,其中五子棋的人机对战采用了AI算法,真实模拟了双人对战的场景,另外在人机对战以外,添加了双人对战模式,可以实时进行双人对战。
2.项目展示
3.项目概览
内容介绍
本项目体现面向对象程序设计,体现了多用组合,少用继承的程序设计原则。本项目分为以下几类:
1.main类:控制程序运行,实现游戏窗口的启动以及游戏的运行。
2.GamePanel类:此类为游戏运行的主体与核心,其中包含了棋盘的布局(棋盘背景,棋盘网格实现,移动红框的绘制),棋盘二维数组的实现,主菜单以及子菜单,鼠标监听方法(鼠标移动事件,鼠标点击时间),菜单中元素的指令处理,以及指令触发后对话框的实现。
3.GameFrame类:此类实现了游戏的窗体,并将GamePanel中的布局元素添加到窗体中。
4.Pointer类:指示器类,此类中传入了棋盘二维数组的下标,并实现了判断鼠标是否在指定区域内的方法,方便了鼠标移动事件的实现。
5.Qizi类与ImageValue类:这两个类是传入白色棋子图片,黑色棋子图片的类,在GamePanel类中通过创建Qizi对象,并调用方法来实现棋子图片的加载与显示。
6.AI类:此类实现了人机对战时,AI棋子的走向算法,实现方法是实时计算每个棋子的分数,获取最高分数点进行落子位置的选择。具体实现方法将在下面介绍。此外,改类中还实现了判断“五子连珠”的public方法,设置为public是为了在GamePanel类中进行调用。
7.Data类:此类服务于AI类。AI类中有计算棋子分数的方法,Data类实现了对分数以及棋子元素的封装,方便了分数在GamePanel类中的调用。
源代码结构图
以下图片为源代码结构演示
4.实现方式
通过以下图片来演示两种模式的实现过程
1.AI算法:
在选择了人机对战之后,通过指令“restart”重置棋盘,并进入到人机模式的鼠标监视器中,调用方法如下。
if("restart".equals(command)){
if(!"end".equals(gameFlag)){
JOptionPane.showMessageDialog(null,"正在游戏中,无法重新开始!","提示",JOptionPane.INFORMATION_MESSAGE);
}else{
restart();
}
restart();
}
这里再调用restart()方法
void restart() {
//重置数据
//游戏状态
//指示器
//棋子
gameFlag="start";
System.out.println("++"+gameFlag);
Pointer pointer;
for(int i=0;i<ROWS;i++){
for(int j=0;j<CLOS;j++){
pointer = pointers[i][j];
pointer.setType(0);
pointer.setShow(false);
}
}
qizis.clear();
reset();
}
通过reset();方法调用鼠标监听后,系统会先执行人工落子,落子之后立即判断胜负,如果胜利则停止运行,否则使用AI.next();让AI落子。
落子过程:AI一共有两种落子方式,第一种是随机落子,
随机落子:
private static boolean louziRandom(GamePanel panel) {
//随机获取指示器位置
Pointer pointer = getRandomPointer(panel);
//根据位置落子
luozi(pointer,1,panel);
return false;
}
private static void luozi(Pointer pointer, int type,GamePanel panel) {
//创建棋子对象
Qizi qizi = new Qizi(pointer.getX(),pointer.getY(),type);
qizi.setLast(true);
//将棋子添加到集合中
panel.qizis.add(qizi);
//设置类型
pointer.setType(type);
//重绘
panel.repaint();
if(has5(pointer,panel)){
panel.gameOver();
}
}
private static Pointer getRandomPointer(GamePanel panel) {
Random random = new Random();
int i = random.nextInt(panel.ROWS);//0-14
int j = random.nextInt(panel.CLOS);//0-14
//获取对应位置的指示器对象
Pointer pointer= panel.pointers[i][j];
if(pointer.getType() != 0){
return getRandomPointer(panel);//递归
}
return pointer;
}
}
louziRandom(GamePanel panel): 这个方法用于在游戏板上随机落子。它首先调用getRandomPointer(panel)方法获取一个随机的指示器位置,然后调用luozi(Pointer pointer, int type, GamePanel panel)方法来落子,并将落子的类型设为1。最后,它返回了一个false值。
luozi(Pointer pointer, int type, GamePanel panel): 这个方法用于在指定的位置落子。它首先创建一个棋子对象Qizi,并设置其坐标和类型。然后将该棋子对象添加到游戏板的棋子集合中,设置对应位置的指示器类型为落子的类型,并重新绘制游戏板。如果这次落子导致获胜,就调用panel.gameOver()方法来结束游戏。
getRandomPointer(GamePanel panel): 这个方法用于随机获取一个可用的指示器位置。它使用Random类生成随机数来选择一个随机的行和列。然后检查所选位置的指示器类型,如果不为0(表示已经有棋子落在该位置),就通过递归调用自身来重新选择一个位置。最后返回一个可用的指示器位置。
AI落子:
AI落子使用分数计算法来确定落点的位置。
首先通过pointer指示器获取二维数组中改点是否被占用,再进行分数计算
type == 1由人工落子,落子为黑子,再棋盘中计算黑子的分数即可。
if(type == 1){
for(int k = j+1;k<panel.CLOS;k++){
temPointer = pointers[i][k];//拿到循环元素(指示器)
if(temPointer.getType() == pointer.getType())
设置分数方式:
如果当前找到一个黑子,有以下情况:
连续 | 空 | 黑 | 黑 | 32 |
黑 | 黑 | 空 | 31 | |
间断 | 黑 | 空 | 黑 | 30 |
计算分数要求棋子数大于2
以上棋子基础分30分
以该棋子为中心,如果左边可以放置棋子,放在左边加为32分,如果可以放在右边,放在右边分数为31分,如果为间断的情况放在中间为30分。
也就是说,如果是连续的情况,左边放棋子的权重更高,优先往左边放置棋子。其次是右边,最后是中间。
以此类推,如果3个棋子连续,基础分为40分,4个棋子连续基础分50分。这样再后期筛选分数时,通过最高分来判定优先往哪里放置棋子。显然,如果基础为40分或者更高,则放置棋子的优先级就更高,以此方法来优先堵住玩家即将完成“五子连珠”的棋子。
上述表格仅为对棋子的横向判断,完成“五子连珠”共有四种方向,分别是横向,竖向,左撇,右撇。所以定义四种方向的变量dir。而且以每一个棋子为中心,向左计算分数以及向右计算分数也是不一样的,所以每一种方向分别计算两次分数,并添加到分数的数组中进行排序。
if(dir == 1){
//从左到右
if(type == 1){
for(int k = j+1;k<panel.CLOS;k++){
temPointer = pointers[i][k];//拿到循环元素(指示器)
if(temPointer.getType() == pointer.getType()){ //连续
num++;
num2++;
if(k == panel.CLOS-1){
rClose = true;
}
}else if(temPointer.getType() == 0){//如果是空
if(breakFlag){
//判断前一个子是否空白
if(pointers[i][k-1].getType() == 0){//如果空白
breakFlag = false;
}
break;
}
num++;
breakFlag = true;
resData.setI(i);
resData.setJ(k);
}else{//对立,不同颜色
rClose = true;
break;
}
//处理左边关闭
if(j == 0){
lClose = true;
}else{
if(pointers[i][j-1].getType() != 0){
lClose = true;
}
}
}
}else{//从右往左
for(int k = j-1;k>=0;k--){
temPointer = pointers[i][k];//拿到循环元素(指示器)
if(temPointer.getType() == pointer.getType()){ //连续
num++;
num2++;
if(k == 0){
lClose = true;
}
}else if(temPointer.getType() == 0){//如果是空
if(breakFlag){
//判断前一个子是否空白
if(pointers[i][k+1].getType() == 0){//如果空白
breakFlag = false;
}
break;
}
num++;
breakFlag = true;
resData.setI(i);
resData.setJ(k);
}else{//对立,不同颜色
lClose = true;
break;
}
//处理右边关闭
if(j == panel.CLOS-1){
rClose = true;
}else{
if(pointers[i][j-1].getType() != 0){
rClose = true;
}
}
}
}
}else if(dir == 2){
if(type == 1){
for(int k = i+1;k<panel.ROWS;k++){
temPointer = pointers[k][j];
if(temPointer.getType() == pointer.getType()){//连续
num++;
num2++;
if(k == panel.ROWS-1){//左后一个连续,右边关闭
rClose = true;
}
}else if(temPointer.getType() == 0) {//null
if (breakFlag) {//有一个不通过
if (pointers[k - 1][j].getType() == 0) {
breakFlag = false;
}
break;
}
num++;
if (k == 0 || k == panel.ROWS - 1) {
break;
}
breakFlag = true;
//食终端那种,设定落子位置
resData.setI(k);
resData.setJ(j);
}else{//对立右边关闭
rClose = true;
break;
}
}
//判读左边关闭
if(i == 0){//当前为最左边子
lClose = true;
}else{
temPointer= pointers[i-1][j];
if(temPointer.getType() != 0){//如果左边有子,关闭
lClose = true;
}
}
}else{
for(int k = i-1;k>=0;k--){
temPointer = pointers[k][j];
if(temPointer.getType() == pointer.getType()){
num++;
num2++;
if(k == 0){
lClose = true;
}
}else if(temPointer.getType() == 0){
if(breakFlag){
if(pointers[k+1][j].getType() == 0){
breakFlag = false;
}
break;
}
breakFlag = true;
resData.setI(k);
resData.setJ(j);
}else{
lClose = true;
break;
}
}
//判断有关闭
if(i==panel.ROWS-1){
rClose = true;
}else{
temPointer = pointers[i+1][j];
if(temPointer.getType() != 0){
rClose = true;
}
}
}
}else if(dir == 3){
int tempi = i;
if(type == 1){
for(int k=j+1;k<panel.CLOS;k++){
tempi++;
if(tempi>panel.CLOS-1){//超粗边界
rClose = true;
break;
}
temPointer = pointers[tempi][k];
if(temPointer.getType() == pointer.getType()){//连续
num++;
num2++;
if(k == panel.CLOS-1){
rClose = true;
}
}else if(temPointer.getType() == 0){
if(breakFlag){//有一个不能通过
if(pointers[tempi-1][k-1].getType() == 0){//如果钱一个食空子,设置不中断
breakFlag = false;
}
break;
}
breakFlag = true;
resData.setI(tempi);
resData.setJ(k);
}else{//对立
rClose = true;
break;
}
}
//对立食做关闭
if(j == 0||i == 0){
lClose = true;
}else{
temPointer = pointers[i-1][j-1];
if((temPointer.getType() != 0)){
lClose = true;
}
}
}else{//从右往左
for(int k=j-1;k>=0;k--){
tempi--;
if(tempi<0){
lClose = true;
break;
}
temPointer = pointers[tempi][k];
if(temPointer.getType() == pointer.getType()){//连续
num++;
num2++;
if(k == 0){
lClose = true;
}
}else if(temPointer.getType() == 0){
if(breakFlag){
if(pointers[tempi+1][k+1].getType() == 0){
breakFlag = false;
}
break;
}
breakFlag = true;
resData.setI(tempi);
resData.setJ(k);
}else{
lClose = true;
break;
}
}
//判断左关闭
if(j==panel.CLOS-1||i == panel.ROWS-1){
rClose = true;
}else{
temPointer = pointers[i+1][j+1];
if(temPointer.getType() != 0){
rClose = true;
}
}
}
}else if(dir == 4){
int tempi = i;
if(type == 1){
for(int k = j+1;k<panel.ROWS;k++){
tempi--;
if(tempi<0){
rClose = true;
break;
}
temPointer = pointers[tempi][k];
if(temPointer.getType()==pointer.getType()){
num++;
num2++;
if(k == panel.CLOS-1){
rClose = true;
}
}else if(temPointer.getType() == 0){//空白子
if(breakFlag){
if(pointers[tempi+1][k-1].getType() == 0){
breakFlag = false;
}
break;
}
num++;
if(tempi==0||tempi==panel.ROWS-1||k==0||k==panel.CLOS-1){
break;
}
breakFlag = true;
resData.setI(tempi);
resData.setJ(k);
}else{
rClose = true;
break;
}
}
//判断右边关闭
if(j == 0||i == 0){
lClose = true;
}else{
if(i == panel.ROWS-1){
lClose = true;
}else{
temPointer = pointers[i+1][j-1];
if(temPointer.getType() != 0){
lClose = true;
}
}
}
}else{//有望左
for(int k=j-1;k>=0;k--){
tempi++;
if(tempi>panel.ROWS-1){
lClose = true;
break;
}
temPointer = pointers[tempi][k];
if(temPointer.getType() == pointer.getType()){
num++;
num2++;
if(k == 0){
lClose = true;
}
}else if(temPointer.getType() == 0){
if(breakFlag){
if(pointers[tempi-1][k+1].getType() == 0){
breakFlag = false;
}
break;
}
num++;
if(tempi==0||tempi==panel.ROWS-1||k==0||k==panel.CLOS-1){
break;
}
breakFlag = true;
resData.setI(tempi);
resData.setJ(k);
}else{//对立
lClose = true;
break;
}
}
//判断有关闭
if(j == panel.CLOS-1||i == panel.ROWS-1){
rClose = true;
}else{
if(i == 0){
rClose = true;
}else{
temPointer = pointers[i-1][j+1];
if(temPointer.getType() != 0){
rClose = true;
}
}
}
}
}
如上代码,dir==1-4表示共有四种方向,每种方向通过从右向左,从左向右计算两次,使用num来纪录连续情况子的数目,num2纪录当前棋子的总个数,将num num2的值传入setCount();方法中,进行分数计算。
接下来是setcount();方法。
private static void setCount(Data resData, int i,int j ,int dir ,int type,int num, int num2, boolean breakFlag, boolean lClose, boolean rClose, GamePanel panel) {
//计的算分数
int count = 0;
if(num <= 2){
return;
}
//分数初步设定
if(num == 3){
count = 30;
}else if(num == 4){
count = 40;
} else if (num == 5) {
count = 50;
}
//如果5子,设定分数100
if(num2 >= 5 && !breakFlag){
count=100;
resData.setCount(100);
return;
}
if(breakFlag){//如果中断
if(lClose&&rClose){
count = -1;
}
}else{
//连续
if(lClose && rClose){
count = -1;
}else if(!lClose){
count += 2;
//横向
if(dir == 1){
//从左往右
if(type == 1){
resData.setI(i);
resData.setJ(j-1);
}else{
resData.setI(i);
resData.setJ(j-num+1);
}
}else if(dir == 2){
if(type == 1){
resData.setI(i-1);
resData.setJ(j);
}else{
resData.setI(i-num+1);
resData.setJ(j);
}
}else if(dir == 3){
if(type == 1){
resData.setI(i-1);
resData.setJ(j-1);
}else{
resData.setI(i-num+1);
resData.setJ(j-num+1);
}
}else if(dir == 4){
if(type == 1){
resData.setI(i+1);
resData.setJ(j-1);
}else{
resData.setI(i+num-1);
resData.setJ(j-num+1);
}
}
}else if(!rClose){
count += 1;
//横向
if(dir == 1){
//从左往右
if(type == 1){
resData.setI(i);
resData.setJ(j+num-1);
}else{
resData.setI(i);
resData.setJ(j+1);
}
}else if(dir == 2){
if(type == 1){
resData.setI(i+num-1);
resData.setJ(j);
}else{
resData.setI(i+1);
resData.setJ(j);
}
}else if(dir == 3){
if(type == 1){
resData.setI(i+num-1);
resData.setJ(j+num-1);
}else{
resData.setI(i+1);
resData.setJ(j+1);
}
}else if(dir == 4){
if(type == 1){
resData.setI(i-num+1);
resData.setJ(j+num-1);
}else{
resData.setI(i-1);
resData.setJ(j+1);
}
}
}
}
resData.setCount(count);
}
该方法对左右是否能放子的情况进行判断,并且给出分数,以及需要方子的坐标。同样判断了四种dir方向。
此方法的末尾resData.setCount();方法对得到的分数进行封装,在二里进行介绍。
2.Data处理:
我们在上面得到棋子分数值以及应当放置的坐标,如上图。此时我们使用Data类中的setI,setJ。方法,传递坐标,对坐标进行封装,使用SetCount();传分数值,并在Data类中写入get set方法对分数进行封装。这就是Data类实现的功能。
3.判断方式:
在AI类中建立DataCount类,对分数判断大小t
class DataCount implements Comparator<Data>{
@Override
public int compare(Data o1, Data o2) {
if(o1.getCount() > o2.getCount()){
return -1;
}
return 1;
}
}
接着调用collection.sort();方法对所得分数进行排序
Collections.sort(datas,new DataCount());
排序完成后执行go();方法进行AI落子。
private static boolean go(GamePanel panel) {
List<Data> datas = new ArrayList<Data>();
Pointer[][] pointers = panel.pointers;
//循环指示器
Pointer pointer;
Data data;
for(int i=0;i<panel.ROWS;i++)
{
for(int j=0;j<panel.CLOS;j++)
{
pointer = pointers[i][j];
if(pointer==null || pointer.getType() == 0){
continue;
}
int dir = 0;
for(int k = 1;k<=4;k++) {
dir = k;
//获取权重1
data = getData(dir ,1,panel,pointer);
System.out.println(data.getCount());
if(data.getCount() !=0 && data.getCount() != -1){
datas.add(data);
}
//获取权重分2
data = getData(dir,2,panel,pointer);
if(data.getCount() != 0 && data.getCount() != -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 data2 = datas.get(0);
Pointer pointer1 = pointers[data2.getI()][data2.getJ()];
luozi(pointer1,1,panel);
return true;
}
return false;
}
方法中取排序后的datas.get(0);即分数最大的落子点进行落子操作。如果操作成功为true,否则为false。返回false时将会通过boolean flag = go(panel) || louziRandom(panel); 判断来执行随机落子。同时实现了随机落子与AI落子的组和。
4.五子连珠的判断。
以下为判断方法
static boolean has5(Pointer pointer1,GamePanel panel){
List<Data> datas = new ArrayList<Data>();
Pointer pointer;
for(int i=0;i<panel.ROWS;i++){
for(int j=0;j<panel.CLOS;j++){
pointer = panel.pointers[i][j];
if(pointer==null)continue;;
if(pointer.getType() == 0){
continue;
}
if(pointer.getType() != pointer1.getType())continue;
//循环四个方向
int dir = 1;
for(int k=1;k<=4;k++){
dir = k;
Data data = getData(dir,1,panel,pointer);
if(data.getCount()!=-1&&data.getCount()!=0){
datas.add(data);
}
data = getData(dir,1,panel,pointer);
if(data.getCount()!=-1&&data.getCount()!=0){
datas.add(data);
}
}
}
}
Collections.sort(datas,new DataCount());
if(datas.size()>0){
Data data = datas.get(0);
if(data.getCount() == 100){
return true;
}
}
return false;
}
判断五子连珠是通过”count == 100“来判断。之前在计算分数时,当num2 == 5,即连续5给珠子时,会设定count=100;因此在该方法中,只需对数组排序,如果data.get(0)中的count值==100,就可以判断有5个珠子连续,即为获胜。获胜返回bool值true,并在gamepanel的鼠标监听器方法中进行调用。
5.菜单实现
private void creatMnue() {
//创建jmb
jmb = new JMenuBar();
Font tfont = createFont();//字体取得
//jme1,2为游戏选项。
JMenu jMenu1 = new JMenu("游戏");
jMenu1.setFont(createFont());
JMenu jMenu2 = new JMenu("帮助");
jMenu2.setFont(createFont());
JMenuItem jmi1 = new JMenuItem("人机对战");
JMenuItem jmi5 = new JMenuItem("双人对战");
jmi1.setFont(tfont);
JMenuItem jmi2 = new JMenuItem("退出");//创建子菜单
jmi2.setFont(tfont);//字体
jmi5.setFont(tfont);
JMenuItem jmi3 = new JMenuItem("操作帮助");
jmi3.setFont(tfont);
JMenuItem jmi4 = new JMenuItem("胜利条件");
jmi4.setFont(tfont);//字体
jMenu1.add(jmi1);
jMenu1.add(jmi5);
jMenu1.add(jmi2);//添加子菜单1,2
jMenu2.add(jmi3);
jMenu2.add(jmi4);//添加子菜单3,4
jmb.add(jMenu1);
jmb.add(jMenu2);//添加菜单1,2
mainFrame.setJMenuBar(jmb);
//添加监听
jmi1.addActionListener( this);
jmi2.addActionListener( this);
jmi3.addActionListener( this);
jmi4.addActionListener( this);
jmi5.addActionListener( this);
//设置指令
jmi1.setActionCommand("restart");
jmi2.setActionCommand("exit");
jmi3.setActionCommand("help");
jmi4.setActionCommand("win");
jmi5.setActionCommand("restart_man");
}
6.菜单指令实现
public void actionPerformed(ActionEvent e) {
// System.out.println("菜单子项被点击");
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.showConfirmDialog(this,"你真的要退出吗?呜呜");
if(response == 0){
System.exit(0);
}
}else if("restart".equals(command)){
// if(!"end".equals(gameFlag)){
// JOptionPane.showMessageDialog(null,"正在游戏中,无法重新开始!","提示",JOptionPane.INFORMATION_MESSAGE);
// }else{
// restart();
// }
restart();
}else if("restart_man".equals(command)){
// if(!"end".equals(gameFlag)){
// JOptionPane.showMessageDialog(null,"正在游戏中,无法重新开始!","提示",JOptionPane.INFORMATION_MESSAGE);
// }else{
// restart_man();
// }
restart_man();
}else if("help".equals(command)){
JOptionPane.showMessageDialog(null,"鼠标在红色区域内落子!","提示",JOptionPane.INFORMATION_MESSAGE);
}else if("win".equals(command)){
JOptionPane.showMessageDialog(null,"五个棋子连一起就win了","提示",JOptionPane.INFORMATION_MESSAGE);
}
}
如果指令是restart或restart_man,
会调用restart();,restart_man();方法。
void restart_man() {
//重置数据
//游戏状态
//指示器
//棋子
gameFlag="start_man";
//System.out.println("++"+gameFlag);
Pointer pointer;
for(int i=0;i<ROWS;i++){
for(int j=0;j<CLOS;j++){
pointer = pointers[i][j];
pointer.setType(0);
pointer.setShow(false);
}
}
qizis.clear();
System.out.println("over" );
reset();
}
void restart() {
//重置数据
//游戏状态
//指示器
//棋子
gameFlag="start";
System.out.println("++"+gameFlag);
Pointer pointer;
for(int i=0;i<ROWS;i++){
for(int j=0;j<CLOS;j++){
pointer = pointers[i][j];
pointer.setType(0);
pointer.setShow(false);
}
}
qizis.clear();
reset();
}
这里将改变gameFlag的值,并通过reset();实现。
void reset(){
if("start_man".equals(gameFlag)){
createMouseListener1();
//System.out.println("zhixonh");
// System.out.println("jj");
}else{
// System.out.println("jjjjjj");
//System.out.println("..."+gameFlag);
createMouseListener();
}
}
7.监听器实现
通过gameFlag的值来选择鼠标监听器,从而实现对“人机对战”和“双人对战”的选择。
如果选择“人机对战”调用以下监听器
private void createMouseListener() {
MouseAdapter mouseAdapter = new MouseAdapter() {
@Override//click事件
public void mouseClicked(MouseEvent e) {
if(!"start".equals(gameFlag)) return;//flag判断是否发生
Pointer pointer;
//获取鼠标坐标
int x = e.getX();
int y = e.getY();
//循环指示器的二维数组
for(int i=0;i<ROWS;i++)
{
for(int j=0;j<ROWS;j++)
{
//得到每一个指示器对象
pointer =pointers [i][j] ;
if(pointer.isPoint(x,y) && pointer.getType() == 0){
Qizi qizi = new Qizi(pointer.getX(),pointer.getY(),2);
qizis.add(qizi);
pointer.setType(2);//被黑棋占用
//重绘
repaint();
clearAILast();
//AI走棋子
if(AI.has5(pointer,panel)){//你已经连成功了
gameWin();
}else{
AI.next(panel);
}
return;
}
}
}
}
@Override//move事件
public void mouseMoved(MouseEvent e) {
if(!"start".equals(gameFlag)) return;//flag判断是否发生
Pointer pointer;
//获取鼠标坐标
int x = e.getX();
int y = e.getY();
//循环指示器的二维数组
for(int i=0;i<14;i++)
{
for(int j=0;j<14;j++)
{
//得到每一个指示器对象
pointer =pointers [i][j] ;
if(pointer.isPoint(x,y) && pointer.getType() == 0){
pointer.setShow(true);
}
else{
pointer.setShow(false);
}
}
}
//System.out.println("mouse_move");
//重绘
repaint();
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
如果选择“双人对战”调用以下监听器
private void createMouseListener1() {
MouseAdapter mouseAdapter = new MouseAdapter() {
@Override//click事件
public void mouseClicked(MouseEvent e) {
//System.out.println("jj");
if(!"start_man".equals(gameFlag)) return;//flag判断是否发生
Pointer pointer;
//获取鼠标坐标
int x = e.getX();
int y = e.getY();
//循环指示器的二维数组int fl = 1;
for(int i=0;i<ROWS;i++)
{
for(int j=0;j<ROWS;j++)
{
//得到每一个指示器对象
pointer =pointers [i][j] ;
if(pointer.isPoint(x,y) && pointer.getType() == 0){
{
if(te == 1){
Qizi qizi = new Qizi(pointer.getX(),pointer.getY(),1);
qizis.add(qizi);
pointer.setType(1);//被黑棋占用
//System.out.println("hh1");
if(AI.has5_w(pointer,panel)){//你已经连成功了
gameWin_w();
}
te = -te;
}else if(te == -1){
Qizi qizi = new Qizi(pointer.getX(),pointer.getY(),2);
qizis.add(qizi);
pointer.setType(2);//被黑棋占用
//System.out.println("hh1");
te = -te;
if(AI.has5_w(pointer,panel)){//你已经连成功了
gameWin_w();
}
else if(AI.has5_b(pointer,panel)){//你已经连成功了
gameWin_b();
}
}
// System.out.println("heiqi");
}
repaint();
//重绘
// clearAILast();
//AI不走棋子
return;
}
}
}
}
@Override//move事件
public void mouseMoved(MouseEvent e) {
if(!"start_man".equals(gameFlag)) return;//flag判断是否发生
Pointer pointer;
//获取鼠标坐标
int x = e.getX();
int y = e.getY();
//循环指示器的二维数组
for(int i=0;i<14;i++)
{
for(int j=0;j<14;j++)
{
//得到每一个指示器对象
pointer =pointers [i][j] ;
if(pointer.isPoint(x,y) && pointer.getType() == 0){
pointer.setShow(true);
}
else{
pointer.setShow(false);
}
}
}
//System.out.println("mouse_move");
//重绘
repaint();
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
5.结尾
在本项目的完成过程中,遇到了很多困难,比如AI算法,我们上网查询了相关资料,借鉴他人代码思路并分析后,完成了算法过程,这次课程设计收获很大。