最近学习java swing,将上次的人机五子棋做了改进,将其界面化,在调试过程中还发现了上次代码中的一些问题。
问题一:无法应对A落在自己左上方的棋子
问题二:计算机不能应对落在边界的棋子,不能应对单个棋子
(即,对于代码中的k值,若该方向上无法再落子,则应该放弃这个k值最大的一串棋子)
package vaniot.com;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class WuZiQi3 extends JPanel{
public WuZiQi3(){
}
//窗口
public void init(JFrame frame,int formWidth,int formHeight){
//设置当前窗体可见,默认不可见
frame.setVisible(true);
//设置当前窗体的宽和高
frame.setSize(formWidth+14, formHeight+35);
frame.setTitle("五子棋人机对弈");
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//通过Dimension类的对象dim可以获取到屏幕的宽和高
int screenWidth = dim.width;
int screenHeight = dim.height;
System.out.println("当前屏幕的分辨率为:"+screenWidth+"*"+screenHeight);
int x = (screenWidth-formWidth)/2;
int y = (screenHeight-formHeight)/2;;
//设置当前窗体出现在窗口中坐标位置,即x轴的坐标值和y轴的坐标值
frame.setLocation(x, y);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
JFrame frame=new JFrame();
WuZiQi3 wzq=new WuZiQi3();
wzq.init(frame,800,600);
WuZiQi3 qp=new WuZiQi3();
//初始化棋盘
qp.initQiPan();
qp.positionOfMouse(frame);
//qp.playGame(frame);
frame.add(qp);
}
private static final long serialVersionUID = 1L;
int flagB=0;
int ii=0;int jj=0;//鼠标点击的坐标
int ss=0;int tt=0;//计算机上一步落子位置
public int[][] Qipan1=new int[Y_HEIGHT][X_WIDTH];
public int[][] Qipan2=new int[Y_HEIGHT][X_WIDTH];
public int[][] Qipan3=new int[Y_HEIGHT][X_WIDTH];
public int[][] Qipan4=new int[Y_HEIGHT][X_WIDTH];
//假定对应的宽度为40列,对应的是横轴的坐标x
public static final int X_WIDTH = 40;//假定对应的高度为30列,对应的是纵轴的坐标y
public static final int Y_HEIGHT = 30;
//定义一个30*40二维数组 ,表示有30行40列,其中行对应的是y轴的坐标值,列对应的是x轴的坐标值
int[][] QiPan = new int[Y_HEIGHT][X_WIDTH];
//初始化棋盘
void initQiPan(){
for(int i=0;i=0)&&(QiPan[I][J-1]==0)){
//向右被占用,向左没有被占用且没到边边上
QiPan[I][J-1]=2;
ss=I;tt=J-1;
}
else{//A向左被占用或到边边上,再搜寻自己的‘#’
computerPlayer();
}
//这里与上篇有调整-------------------------
//纠正了原程序中玩家A落子在A上一步的左方,而计算机不落子的错误
}
else if(J-1>=0){//向右到边界了,再向左看是否有位置
if(QiPan[I][J-1]==0){//向左没有被占用
QiPan[I][J-1]=2;
ss=I;tt=J-1;
}
else{//A向左被占用,再搜寻自己的‘#’
computerPlayer();
}
}
else{//向左也到边界或者向左被占用,搜寻自己的‘#’或就近任意落子
computerPlayer();
}
}
else if(flag[2]==2){// ↓
if(I+flag[4]<30){//向下没有到边边上
if(QiPan[I+flag[4]][J]==0){
QiPan[I+flag[4]][J]=2;
ss=I+flag[4];tt=J;//记录计算机这一步落在哪里
}
else if((I-1>=0)&&(QiPan[I-1][J]==0)){
//向下已被占用,向上未到边界且未被占用
QiPan[I-1][J]=2;
ss=I-1;tt=J;
}
else{
computerPlayer();
}
}
else if(I-1>=0){//向下到边界了,再向上看是否有位置
if(QiPan[I-1][J]==0){//向左没有被占用
QiPan[I-1][J]=2;
ss=I-1;tt=J;
}
else{//A向上被占用,再搜寻自己的‘#’
computerPlayer();
}
}
else{//向上也到边界或者向上被占用,搜寻自己的‘#’或就近任意落子
computerPlayer();
}
}
else if(flag[2]==3){// ↙
if((I+flag[4]<30)&&(J-flag[4]>=0)){//向左下没有到边边上
if(QiPan[I+flag[4]][J-flag[4]]==0){//左下没有被占用
QiPan[I+flag[4]][J-flag[4]]=2;
ss=I+flag[4];tt=J-flag[4];//记录计算机这一步落在哪里
}
else if((QiPan[I-1][J+1]==0)&&(I-1>=0)&&(J+1<40)){
//左下已被占用,右上没到边边上且没有被占用
QiPan[I-1][J+1]=2;
ss=I-1;tt=J+1;
}
else{//右上到边边上或被占用
computerPlayer();
}
}
else if((I-1>=0)&&(J+1<40)){//向左下到边界了,向右上没有到边界
if(QiPan[I-1][J+1]==0){//向右上没有被占用
QiPan[I-1][J+1]=2;
ss=I-1;tt=J+1;
}
else{//A向右上被占用,再搜寻自己的
computerPlayer();
}
}
else{//向右上也到边界或者向右上被占用,搜寻自己的‘#’或就近任意落子
computerPlayer();
}
}
else if(flag[2]==4){// ↘
if((I+flag[4]<30)&&(J+flag[4]<40)){//向右下没有到边边上
if(QiPan[I+flag[4]][J+flag[4]]==0){//右下没有被占用
QiPan[I+flag[4]][J+flag[4]]=2;
ss=I+flag[4];tt=J+flag[4];//记录计算机这一步落在哪里
}
else if((QiPan[I-1][J-1]==0)&&(I-1>=0)&&(J-1>=0)){
//右下已被占用,左下没到边边上且没有被占用
QiPan[I-1][J-1]=2;
ss=I-1;tt=J-1;
}
//这里与上篇有调整-------------------------
//纠正了原程序中玩家A落子在A上一步的右上方,而计算机不落子的错误
else{//右下已被占用,左下到边边上或被占用
computerPlayer();//搜寻自己的k最大值
}
}
else if((I-1>=0)&&(J-1>=0)){//向右下到边界了,再向左上看是否有位置
if(QiPan[I-1][J-1]==0){//向左上没有被占用
QiPan[I-1][J-1]=2;
ss=I-1;tt=J-1;
}
else{//A向右上被占用,再搜寻自己的‘#’
computerPlayer();
}
//这里与上篇有调整-------------------------
//纠正了原程序中玩家A落子在A上一步的左上方,而计算机不落子的错误
}
else{//向右上也到边界或者向右上被占用,搜寻自己的‘#’或就近任意落子
computerPlayer();
}
}
repaint();//落子完毕,更新棋盘
flag=scanner(ss,tt);//重新计算棋盘布局
if(flag[3]==1){
repaint();
String message="计算机胜出!";
JOptionPane.showMessageDialog(qipan, message);
System.exit(0);
}
flagB=1;
}
}
});
}
public void computerPlayer(){
//A的k值最大处都已经落子,此时,寻找计算机自己的k最大值
int[] flag={0,0,0,0,0};
flag=scanner(ss,tt);//计算机上一步落子处
int I=flag[0];
int J=flag[1];//(I,J)处,k值最大
if(flag[2]==1){
if((J+flag[4]<40)&&(QiPan[I][J+flag[4]]==0)){//向右没有到边边上,且没有被占用
QiPan[I][J+flag[4]]=2;//计算机向右落子
ss=I;tt=J+flag[4];
}
else if(J-1>=0){//向右到边边上或者向右被占用,向左没有到边边上
if(QiPan[I][J-1]==0){//向左没有被占用
QiPan[I][J-1]=2;
ss=I;tt=J-1;
}
else{//向左被占用了,就近随机落子
for(int i=0;i<30-I;i++){
for(int j=0;j<40-J;j++){
if(QiPan[I+i][J+j]==0){
QiPan[I+i][J+j]=2;
j=40-J;i=30-I;//跳出循环
}
}
}
}
}
}
else if(flag[2]==2){
if((I+flag[4]<30)&&(QiPan[I+flag[4]][J]==0)){//向下没有到边边上,且没有被占用
QiPan[I+flag[4]][J]=2;
ss=I+flag[4];tt=J;
}
else if(I-1>=0){//向下到边界或被占用,向上没有到边界
if(QiPan[I-1][J]==0){
QiPan[I-1][J]=2;
ss=I-1;tt=J;
}
else{//向上被占用了,就近随机落子
for(int i=0;i<30-I;i++){
for(int j=0;j<40-J;j++){
if(QiPan[I+i][J+j]==0){
QiPan[I+i][J+j]=2;
j=40-J;i=30-I;//跳出循环
}
}
}
}
}
}
else if(flag[2]==3){
if((I+flag[4]<30)&&(J-flag[4]>=0)){//向左下没有到边边上
QiPan[I+flag[4]][J-flag[4]]=2;
ss=I+flag[4];tt=J-flag[4];
}
else if((I-1>=0)&&(J+1<40)){//向右上没有到边边上
if(QiPan[I-1][J+1]==0){
QiPan[I-1][J+1]=2;
ss=I-1;tt=J+1;
}
else{//向右上被占用了,就近随机落子
for(int i=0;i<30-I;i++){
for(int j=0;j<40-J;j++){
if(QiPan[I+i][J+j]==0){
QiPan[I+i][J+j]=2;
j=40-J;i=30-I;//跳出循环
}
}
}
}
}
}
else if(flag[2]==4){
if((I+flag[4]<30)&&(J+flag[4]<40)&&(QiPan[I+flag[4]][J+flag[4]]==0)){
//向右下没有到边边上且没有被占用
QiPan[I+flag[4]][J+flag[4]]=2;
ss=I+flag[4];tt=J+flag[4];
}
else if((I-1>=0)&&(J-1>=0)){
//向右下到边界或者被占用,向左上没有到边界
if(QiPan[I-1][J-1]==0){
QiPan[I-1][J-1]=2;
ss=I-1;tt=J-1;
}
else{//向左上被占用了,就近随机落子
for(int i=0;i<30-I;i++){
for(int j=0;j<40-J;j++){
if(QiPan[I+i][J+j]==0){
QiPan[I+i][J+j]=2;
j=40-J;i=30-I;//跳出循环
}
}
}
}
}
}
}
//scanner较上篇有调整-------------------------
//在找寻k值最大的点的同时,需要考察该方向上是否还有落子位置
//若没有,则放弃这个k值最大点
public int[] scanner(int iii,int jjj){
//每落一子,扫描全盘,看是否有连成功的
int[] m=new int[5];//返回k值最大的坐标、方向以及是否连成5子、相应坐标的k值
//从(0,0)开始,先向右扫描
int flag0=0;
for(int i=0;i<30;i++){
for(int j=0;j<40;j++){//不越界的情况下作比较
int k=1;
int k0=0;
while(k0==0){//k=4时,成功
if(j+k<40){
if((QiPan[i][j]==QiPan[i][j+k])&&(QiPan[i][j]!=0)){
k++;
}
else k0=1;
}
else break;
}
if(k==5){
flag0=1;//成功左右连成5子
}
if(j+k<40){//右没有到边界
if(QiPan[i][j+k]==0){//右有位置,可以
Qipan1[i][j]=k;
}
else if(j-1>=0){//右没有到边界,但是向右被占用,左没有到边界
if(QiPan[i][j-1]==0){//左有位置,可以
Qipan1[i][j]=k;
}
else Qipan1[i][j]=0;//左也被占用,放弃该位置,改点k值记为0
}
else Qipan1[i][j]=0;//左到边界,放弃该位置
}
else{//右到边界
if(j-1>=0){//左没有到边界
if(QiPan[i][j-1]==0){//左有位置,可以
Qipan1[i][j]=k;
}
else Qipan1[i][j]=0;//左被占用,放弃该位置
}
else Qipan1[i][j]=0;//左也到边界,放弃该位置
}
}
}
//向下扫描
for(int j=0;j<40;j++){
for(int i=0;i<30;i++){
int k=1;
int k0=0;
while(k0==0){//k=4时,成功
if(i+k<30){
if((QiPan[i][j]==QiPan[i+k][j])&&(QiPan[i][j]!=0)){
k++;
}
else k0=1;//跳出循环
}
else break;
}
if(k==5){
flag0=1;//成功上下连成5子
}
if(i+k<30){//下没有到边界
if(QiPan[i+k][j]==0){//下有位置,可以
Qipan2[i][j]=k;
}
else if(i-1>=0){//下没有到边界,但是向下被占用,上没有到边界
if(QiPan[i-1][j]==0){//上有位置,可以
Qipan2[i][j]=k;
}
else Qipan2[i][j]=0;//上也被占用,放弃该位置
}
else Qipan2[i][j]=0;//上到边界,放弃该位置
}
else{//下到边界
if(i-1>=0){//上没有到边界
if(QiPan[i-1][j]==0){//上有位置,可以
Qipan2[i][j]=k;
}
else Qipan2[i][j]=0;//上被占用,放弃该位置
}
else Qipan2[i][j]=0;//上也到边界,放弃该位置
}
}
}
//右上&左下方向
for(int i=0;i<30;i++){
for(int j=0;j<40;j++){//j可以直接从4开始,4之前的这个方向连不成5子
int k=1;
int k0=0;
while(k0==0){//k=4时,成功
if((i+k<30)&&(j-k>=0)){
if((QiPan[i][j]==QiPan[i+k][j-k])&&(QiPan[i][j]!=0)){
k++;
}
else k0=1;
}
else break;
}
if(k==5){
flag0=1;//成功右上&左下连成5子
}
if((i+k<30)&&(j-k>=0)){//左下没有到边界
if(QiPan[i+k][j-k]==0){//左下有位置,可以
Qipan3[i][j]=k;
}
else if((i-1>=0)&&(j+1<40)){//左下没有到边界,但是被占用,右上没有到边界
if(QiPan[i-1][j+1]==0){//右上有位置,可以
Qipan3[i][j]=k;
}
else Qipan3[i][j]=0;//右上也被占用,放弃该位置
}
else Qipan3[i][j]=0;//右上到边界,放弃该位置
}
else{//左下到边界
if((i-1>=0)&&(j+1<40)){//右上没有到边界
if(QiPan[i-1][j+1]==0){//右上有位置,可以
Qipan3[i][j]=k;
}
else Qipan3[i][j]=0;//右上被占用,放弃该位置
}
else Qipan3[i][j]=0;//右上也到边界,放弃该位置
}
}
}
//左上&右下方向
for(int i=0;i<30;i++){
for(int j=0;j<40;j++){//j到a-4,a-4之后的这个方向连不成5子
int k=1;
int k0=0;
while(k0==0){//k=4时,成功
if((i+k<30)&&(j+k<40)){
if((QiPan[i][j]==QiPan[i+k][j+k])&&(QiPan[i][j]!=0)){
k++;
}
else k0=1;
}
else break;
}
if(k==5){
flag0=1;//成功左上&右下连成5子
}
if((i+k<30)&&(j+k<40)){//右下没有到边界
if(QiPan[i+k][j+k]==0){//右下有位置,可以
Qipan4[i][j]=k;
}
else if((i-1>=0)&&(j-1>=0)){//右下没有到边界,但是被占用,左上没有到边界
if(QiPan[i-1][j-1]==0){//左上有位置,可以
Qipan4[i][j]=k;
}
else Qipan4[i][j]=0;//左上也被占用,放弃该位置
}
else Qipan4[i][j]=0;//左上到边界,放弃该位置
}
else{//右下到边界
if((i-1>=0)&&(j-1>=0)){//左上没有到边界
if(QiPan[i-1][j-1]==0){//左上有位置,可以
Qipan4[i][j]=k;
}
else Qipan4[i][j]=0;//左上被占用,放弃该位置
}
else Qipan4[i][j]=0;//左上也到边界,放弃该位置
}
}
}
int[][] m1=new int[Y_HEIGHT][X_WIDTH];//存储每点的k值
int[][] m2=new int[Y_HEIGHT][X_WIDTH];//存储每点k值最大的那个方向
for(int i=0;i<30;i++){
for(int j=0;j<40;j++){
//同一个点,四个方向k值最大的那个方向
//m2记录方向
m1[i][j]=max(Qipan1[i][j],Qipan2[i][j],Qipan3[i][j],Qipan4[i][j]);
if(m1[i][j]==Qipan1[i][j]){
m2[i][j]=1;// →
}
else if(m1[i][j]==Qipan2[i][j]){
m2[i][j]=2;// ↓
}
else if(m1[i][j]==Qipan3[i][j]){
m2[i][j]=3;// ↙
}
else{
m2[i][j]=4;// ↘
}
}
}
//整个棋盘上哪个点的k值最大
int m3=0;
int I=0,J=0;//(I,J)处k值最大
for(int i=0;i<30;i++){
for(int j=0;j<40;j++){
if(m1[i][j]>m3){
m3=m1[i][j];
I=i;J=j;
}
else if(m1[i][j]==m3){//如果两个k相等,取离落子近的那个点
int dd1=(I-iii)*(I-iii)+(J-jjj)*(J-jjj);
int dd2=(i-iii)*(i-iii)+(j-jjj)*(j-jjj);
if(dd1>dd2){
m3=m1[i][j];
I=i;J=j;
}
}
}
}
m[0]=I;m[1]=J;//确定哪个点的k值最大
m[2]=m2[I][J];//方向
m[3]=flag0;//是否连成5子
m[4]=m3;//该点的k值
return m;
}
public int max(int x,int y,int z,int w){
if(x>y){
if(x>z) return x>w?x:w;
else return z>w?z:w;
}
else{
if(y>z) return y>w?y:w;
else return z>w?z:w;
}
}
}