一.基本步骤
1.画出棋盘
2.可以交替画出黑白棋子
3.保证棋子画在棋盘交点处
4.添加开始与结束按钮
5.实现悔棋
6.判断输赢
7.AI下棋(做一个自己下不过的AI超酷的!!)
二.个人优化
1.棋子3D化
2.闪屏问题的处理
三.问题解决
1.this究竟是个什么?
2.super.paint(g);能咋?
3.HashMap是什么?
四.具体代码实现
1.画棋盘
1)弄一个窗体
public class Gov0 extends JFrame implements Data{
public void getBoard(){
//this 在这里指的是在主方法里创建的对象
//接着利用JFrame中的paint给棋盘画线
//如果新建一个JFrame的对象则画线画不上
JFrame jf=this;
jf.setSize(900,900);
jf.setTitle("五子棋v0");
jf.setDefaultCloseOperation(EXIT_ON_CLOSE);
jf.setVisible(true);
}
this是什么?
this指当前调用方法的对象
在该类中,this均指调用getBoard这个对象
public static void main(String[]args){
new Gov0().getBoard();
}
在重写JFrame的paint方法时,先创建一个Data接口,把要用的数据写进去,以便于多个类可以重复使用
public interface Data {
public static int SIZE=40;
public static int x0=50;
public static int y0=50;
public static int COLUMN=15;
public static int ROW=15;
}
2)在窗体上画线
public void paint(Graphics g) {
for(int i=0;i<=ROW;i++){
g.drawLine(x0,y0+SIZE*i,x0+COLUMN*SIZE,y0+SIZE*i);
g.drawLine(x0+SIZE*i,y0,x0+SIZE*i,y0+SIZE*ROW);
}
//不需要在主方法中用对象调用paint方法
}
2.画出棋子
1)画出黑白交替的实心圆
int flag=1;
public void mousePressed(MouseEvent e) {
int x=e.getX();
int y=e.getY();
if(flag==1){
g.setColor(Color.black);
g.fillOval(x-SIZE/2,y-SIZE/2,SIZE,SIZE);
flag=2;
}
else if(flag==2){
g.setColor(Color.WHITE);
g.fillOval(x-SIZE/2,y-SIZE/2,SIZE,SIZE);
flag=1;
}
怎么实现画出3D的棋子呢?
if(flag==1){
for(int i=20;i>=0;i--) {
Color color=new Color(120-6*i,120-6*i,120-6*i);
g.setColor(color);
g.fillOval(x-i,y-i,2*i,2*i);
}
flag=2;
}
else if(flag==2){
for(int i=0;i<=20;i++) {
Color color = new Color(135+6*i,135+6*i,135+6*i);
g.setColor(color);
g.fillOval(x-20+i,y-20+i,40-2*i,40-2*i);
}
flag=1;
}
利用循环创造颜色的渐变,需要注意的是
1>在画圆的时候要保证先画大圆再画小圆
2>黑棋两边黑中间灰形成3D,白棋两边灰中间白形成3D
总结来说,两边颜色深,中间颜色浅形成3D
3>为保证鼠标位于圆的中心,需要利用x0=x-width/2
3.保证棋子画在棋盘交点处
1)将坐标换算为新的棋盘
int c=(x-x0+SIZE/2)/SIZE;
int r=(y-y0+SIZE/2)/SIZE;
int nx=x0+c*SIZE;
int ny=y0+r*SIZE;
if(flag==1){
for(int i=20;i>=0;i--) {
Color color=new Color(120-6*i,120-6*i,120-6*i);
g.setColor(color);
g.fillOval(nx-i,ny-i,2*i,2*i);
}
flag=2;
}
2)处理一些细节问题
1>棋子越界问题
2>重复下棋问题
if(x>670||x<30||y<30||y>670){
JOptionPane.showMessageDialog(null,"此处不可下棋!!");
return;
}
if(chessArr[r][c]!=0){
JOptionPane.showMessageDialog(null,"此处不可以下棋!!");
return;
}
4.实现开始与结束游戏
public void actionPerformed(ActionEvent e) {
String btnstr=e.getActionCommand();
JButton btn=(JButton) e.getSource();
if(btnstr.equals("开始游戏")){
//开始游戏时把上一局的棋子清理掉
ui0.repaint();
//刷新即可清屏
flag=1;
btn.setText("结束游戏");
}
else if(btnstr.equals("结束游戏")){
flag=0;
btn.setText("开始游戏");
}
}
怎么解决闪屏问题?
闪屏的出现是因为什么嘞?
与上一个项目中绘制图片缓慢的原因相似
棋盘上的线和之后加入悔棋之后需要绘制的棋子都是一个一个画的,太慢了,就出现了闪屏
那要怎么解决嘞?
也与上一个项目解决方案类似
我们需要先画好一张需要呈现的棋盘的样子,然后一次性画出来
BufferedImage buffimg=new BufferedImage(700,700,BufferedImage.TYPE_INT_ARGB);
Graphics g1=buffimg.createGraphics();
//buffimg自己创造出一个画笔画在自己的画布上
//g再画出buffimg即可
//给棋盘划线
for(int i=0;i<=ROW;i++){
g1.drawLine(x0,y0+SIZE*i,x0+COLUMN*SIZE,y0+SIZE*i);
g1.drawLine(x0+SIZE*i,y0,x0+SIZE*i,y0+SIZE*ROW);
}
g.drawImage(buffimg,0,0,null);
当解决了闪屏问题之后,会发现清屏不只是repaint那么简单了
super.paint(g);能咋?
在解决闪屏问题时去掉了super.paint(g);
super.paint(g);可以让微小的组件如开始游戏按钮一起刷新
不是组件的东西如画出的棋子会因为刷新而全部消失
而去掉super.paint(g);之后
1)新画出来的图片只新画了棋盘,棋子并没有消失
2)按钮不加repaint就不能在开始游戏与结束游戏之间切换了
g1.setColor(Color.orange);
g1.fillRect(0,0,700,700);
其实是因为必须要画一个有颜色的棋盘才可以把棋子盖上
5.实现悔棋
首先要找到最后一枚棋子
则需要对下的每一枚棋子进行储存
在实现悔棋时需要知道最后一枚棋子的行和列
在重新下棋时需要知道悔掉的棋子是黑棋还是白棋
创建一个Chess类,以实现用数组的形式存储棋子的数据
public class Chess {
int flag;
int r;
int c;
public Chess(int flag, int r, int c) {
this.flag = flag;
this.r = r;
this.c = c;
}
在下棋的位置记录棋子
count++;
Chess chess=new Chess(flag,r,c);
chesslist[count-1]=chess;
else if(btnstr.equals("悔棋")){
if(chessnum==0){
JOptionPane.showMessageDialog(null,"没有棋子!!");
return;
}
Chess chess=chesslist[chessnum-1];
chessArr [chess.r][chess.c]=0;
flag=chesslist[chessnum-1].flag;
//得到下一次下棋时flag的值
//清理这枚棋子的数据
chesslist[chessnum-1] =null;
chessnum--;
System.out.println("count0="+chessnum);
//最后刷新
ui0.repaint();
}
千万要注意
1)获取最后一枚棋子的数据以及删除最后一枚棋子的数据的顺序
2)在记录棋子时count++的位置(不能加在循环内!!)
6.判断输赢
在行方向上计算连续棋子的个数
public static int row(int [][]chessArr,int r,int c){
//向右找棋子个数
int num=chessArr[r][c];
for(int i=c+1;i<chessArr[0].length;i++){
if(chessArr[r][i]!=num){
break;
}
else{
count++;
}
}
//System.out.println("向右"+count+"枚棋子");
//向左找棋子个数
for(int i=c-1;i>=0;i--){
if(chessArr[r][i]!=num){
break;
}
else{
count++;
}
}
count++;
System.out.println("行方向上连续有"+count+"枚棋子");
return count;
}
列方向上的类似
在左上到右下的方向上计算连续棋子的个数
public static int leftUpToRightDown(int [][]chessArr,int r,int c){
//右下找棋子个数
count=0;
int num=chessArr[r][c];
for(int i=1;i<Math.min((chessArr.length-r),(chessArr.length-c));i++){
if(chessArr[r+i][c+i]!=num){
break;
}
else{
count++;
}
}
System.out.println("向右下"+count+"枚棋子");
//向左上找棋子个数
for(int i=1;i<=Math.min(r,c);i++){
if(chessArr[r-i][c-i]!=num){
break;
}
else{
count++;
}
}
count++;
System.out.println("左上到右下方向上连续有"+count+"枚棋子");
return count;
}
当棋子连成5个之后,判断输赢
public static boolean isWin(int [][]chessboard,int r,int c){
if(row(chessboard,r,c)==5||
column(chessboard,r,c)==5||
leftUpToRightDown(chessboard,r,c)==5||
rightUpToLeftDown(chessboard,r,c)==5
){
return true;
}
return false;
}
把写好的判断输赢加在下完棋的位置上
if(Win.isWin(chessArr,r,c)){
if(flag==1){
JOptionPane.showMessageDialog(null,"白棋胜利!!");
flag=0;
}
else if(flag==2){
JOptionPane.showMessageDialog(null,"黑棋胜利!!");
}
}
7.AI下棋
1)先用HashMap把连子情况与对应的得分给存起来
static HashMap<String,Integer> codemap=new HashMap<>();
static {
codemap.put("010",10);
codemap.put("0110",50);
codemap.put("01110",500);
codemap.put("011110",5000);
codemap.put("020",10);
codemap.put("0220",50);
codemap.put("02220",500);
codemap.put("022220",5000);
codemap.put("01",8);
codemap.put("011",40);
codemap.put("0111",400);
codemap.put("01111",5000);
codemap.put("02",8);
codemap.put("022",40);
codemap.put("0222",400);
codemap.put("02222",5000);
}
2)把各个方向连子情况的得分分别计算出来(以右边为例)
public static int right(int r,int c,int[][]chessArr){
//右边第一颗棋子
if(c+1>=chessArr.length){
return 0;
}
//为边界
else if(chessArr[r][c+1]==0){
return 0;
}
//为0
int r1=chessArr[r][c+1];
//记录下右边第一颗棋子是1还是2
String codestr="0"+r1;
for(int i=c+2;i<chessArr.length;i++){
//判断并记录右边第2,3,4,,,,颗棋子
if(chessArr[r][i]==0){
codestr+=0;
break;
}
else if(chessArr[r][i]!=0){
if(chessArr[r][i]==r1){
codestr+=r1;
}
else {
break;
}
}
}
System.out.println("连子情况为"+codestr);
int wnum=codemap.get(codestr);
System.out.println("得分为"+wnum);
if(wnum==0){
return 0;
}
return wnum;
}
3)写一个判断AI应该下棋在什么位置的方法
public int [] getMax(int [][]chessboard){
int [][]codeArr=new int[chessboard.length][chessboard[0].length];
for(int i=0;i<chessboard.length;i++){
for(int j=0;j<chessboard.length;j++){
//是0的位置计算下此处的分数
int num=chessboard[i][j];
if(num==0){
int score=0;
score+=right(i,j,chessboard);
score+=left(i,j,chessboard);
score+=up(i,j,chessboard);
score+=down(i,j,chessboard);
score+=leftup(i,j,chessboard);
score+=rightdown(i,j,chessboard);
score+=rightup(i,j,chessboard);
score+=leftdown(i,j,chessboard);
codeArr[i][j]=score;
}
}
}
int max=0;
int r=0,c=0;
for(int i=0;i<chessboard.length;i++){
for (int j=0;j<chessboard.length;j++){
if(codeArr[i][j]>max){
max=codeArr[i][j];
x=i;
y=j;
}
}
}
return new int{r,c} ;
}
4)把AI下棋与按钮监听结合
else if(AI==1){
if(flag==1){
//下棋
for(int i=20;i>=0;i--) {
Color color = new Color(120 - 6 * i, 120 - 6 * i, 120 - 6 * i);
g.setColor(color);
g.fillOval(nx - i, ny - i, 2 * i, 2 * i);
}
//把棋子存入chessArr
chessArr[r][c]=1;
//判断输赢
if(Win.isWin(chessArr,r,c)){
JOptionPane.showMessageDialog(null,"玩家胜利!!");
return;
}
flag=2;
//AI下棋
int []location=AIgo.getMax(chessArr);
int nr=location[0];
int nc=location[1];
System.out.println("nc:"+nc+"nr:"+nr);
for(int i=0;i<=20;i++) {
Color color = new Color(135 + 6 * i, 135 + 6 * i, 135 + 6 * i);
g.setColor(color);
int nx1=x0+nc*SIZE;
int ny1=y0+nr*SIZE;
g.fillOval(nx1 - 20 + i, ny1 - 20 + i, 40 - 2 * i, 40 - 2 * i);
}
chessArr[nr][nc]=2;
if(Win.isWin(chessArr,nr,nc)){
JOptionPane.showMessageDialog(null,"AI胜利!!");
return;
}
flag=1;
}
}
5)实现AI悔棋
else if(AI==1){
Chess chess1 =chesslist[chessnum-1];
System.out.println("chessnum="+chessnum);
chessArr[chess1.r][chess1.c]=0;
System.out.println("r="+chess1.r+"c="+chess1.c);
System.out.println(chessArr[chess1.r][chess1.c]);
flag=chess1.flag;
chesslist[chessnum-1]=null;
chessnum--;
System.out.println("chessnum="+chessnum);
Chess chess2=chesslist[chessnum-1];
chessArr[chess2.r][chess2.c]=0;
System.out.println("r="+chess2.r+"c="+chess2.c);
System.out.println(chessArr[chess2.r][chess2.c]);
//System.out.println("归0");
flag=chess2.flag;
chesslist[chessnum-1]=null;
chessnum--;
ui0.repaint();
}