编写一个五子棋代码量不会很大,但这好歹也算是个项目,所以还是需要花点时间的。看别人的代码需要耐心,要看懂别人的代码就更需要耐心了,作为一个程序员也就是需要这样的耐心。当然,在代码里我会尽量的多加一些注释来提高代码的可读性,唯一的要求就是要读者们一步一步的跟着我来,顺着我的思路一步一步的深入,这样才能最终达到我写这篇博客和你看这篇博客的目的。
那下面呢,我就具体介绍一下人人五子棋是如何实现的。
1)首先我们要有一个五子棋界面,就是画出许多行横线和许多行竖线
private void DrawCheesTable(Graphics g) {
// TODO Auto-generated method stub
//画出指定的行数
for( int i = 0; i < Config.ROWS; i++)
g.drawLine(Config.X0, Config.Y0 + Config.SIZE * i,
Config.X0 + Config.SIZE *( Config.COLUMNS - 1), Config.Y0 +Config.SIZE * i);
//画出指定的列数
for( int i = 0; i < Config.COLUMNS; i++)
g.drawLine(Config.X0 + Config.SIZE * i,Config.Y0,
Config.X0 + Config.SIZE * i,Config.Y0 +( Config.ROWS - 1) * Config.SIZE);
}
看到上面的代码里面有一些奇怪的符号吧,不急,那是我事先定义好的保存常量数据的类。另外,for语句中画线所用到的数据是如何给定的,留给读者自己分析了。
2)创建一个保存常量的类,为了方便我们改变棋子大小,棋盘大小
棋盘画好了,现在就来实现释放鼠标时画填充圆。鼠标释放,就用到 public void mouseReleased(MouseEvent e) {}函数,于是就要监听器类extends java.awt.event.MouseAdapter了。
public interface Config {
//棋盘的初始点坐标
public static final int X0 = 50;
public static final int Y0 = 50;
//多少列多少行
public static final int ROWS = 11;
public static final int COLUMNS = 14;
//棋子的大小
//格子的大小
public static final int CHESS_SIZE = 40;
public static final int SIZE = 40;
}
3)鼠标释放方法
public void mouseReleased(MouseEvent e) {
//得到鼠标释放时的点的坐标
int x1 = e.getX();
int y1 = e.getY();
//按下点后我们就去找 它在拿个交点附近
for(int i = 0; i < Config.ROWS; i++){
for(int j = 0; j < Config.COLUMNS; j++){
//得到每一个交点的值
xx = Config.X0 + Config.SIZE * j;
yy = Config.Y0 + Config.SIZE * i;
//如果这个交点可以放棋子
if(FiveChress_array[i][j] == 0)
//判断离谁更近
if(x1 > xx - Config.SIZE/3 && x1 < xx + Config.SIZE/ 3 && y1 > yy - Config.SIZE /3 && y1 < yy + Config.SIZE /3 )
//花黑棋
if(count){
System.out.println("再黑棋中xx的值是 " +xx +"\t" + "在黑棋中yy的值是 " + yy);
//画出来的是黑棋
g.setColor(java.awt.Color.BLACK);
g.fillOval(xx - Config.CHESS_SIZE/2, yy - Config.CHESS_SIZE/2, Config.CHESS_SIZE, Config.CHESS_SIZE);
//下一次是画白棋
count = false;
//用数组记录每个交点的属性 1黑棋 0空白 -1白棋
FiveChress_array[i][j] = 1;
x = j;
y = i;
//画出了点酒结束 不再循环
break;
}else{
//画白棋
System.out.println("再白棋中xx的值是 " +xx +"\t" + "在白棋中yy的值是 " + yy);
g.setColor(java.awt.Color.WHITE);
g.fillOval(xx - Config.CHESS_SIZE/2, yy - Config.CHESS_SIZE/2, Config.CHESS_SIZE, Config.CHESS_SIZE);
count = true;
FiveChress_array[i][j] = -1 ;
x = j;
y = i;
//画出了点酒结束 不再循环
break;
}
}
}
//调用判断是否赢棋函数
win_or_not(x,y);
}
这其中包含了一个判断鼠标按下点离哪个棋盘交点最近的算法,留给读者自己分析了。里面同时有使用很多的变量它们的声明如下
//定义画板对象
private java.awt.Graphics g;
//每个棋子的基本属性 true表示要放黑棋 false表示要放白棋
private boolean count = true;
//再创建两个数来表示交点坐标 使得画出来的圈圆心会在交点
private int xx,yy;
//创建(x,y)来表示按下的那个点所放在的焦点坐标 比如(1,3) 表示第一行第三个交点
private int x,y;
//创建一个数组记录每个可以放棋子的点的状态 0表示空 1表示黑棋 -1表示白棋
private int [][] FiveChress_array = new int[Config.ROWS][Config.COLUMNS];
4)鼠标监听器类的构造函数
//构造函数
public ChessListener(java.awt.Graphics g1 , int [][] FiveChress_array){
//地址传递
this.g = g1;
this.FiveChress_array = FiveChress_array;
}
现在我们的棋子可以下了,而且可以下在正确的位置上,下面要做的是实现win_or_not()函数了,该函数里我们要实现行列左倾斜,右倾斜的判断。
5)实现win_or_not()函数
private void win_or_not(int x, int y) {
//如果在行列左斜右斜的方向上有了五个连续的同色棋子 就可以判断出输赢了
System.out.println("进入win_or_not函数");
if(check_row(x,y) || check_columns(x,y) || check_left(x,y) || check_right(x,y) ){
if(!count){
//黑棋赢了
JOptionPane.showMessageDialog(null,"我靠 黑棋赢了");
System.out.println("我靠 黑棋赢了");
}else{
//白棋赢了
JOptionPane.showMessageDialog(null,"我靠 白棋赢了");
System.out.println("我靠 白棋赢了");
}
}
}
在if判断里调用了判断行、列、左斜、右斜的函数。其中JOptionPane.showMessageDialog(null,"");是用来弹出一个对话框突出显示输赢。
6)check_row函数的实现
private boolean check_row(int x, int y) {
int num = 0; //表示相同颜色的棋子个数
//开始向右找相邻的相同颜色的
for(int i = x ; i < Config.COLUMNS ; i++){
if(FiveChress_array[y][x] == FiveChress_array[y][i]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
//开始向左找相邻的相同颜色的
for(int i = x ; i > 0 ; i--){
if(FiveChress_array[y][x] == FiveChress_array[y][i-1]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
System.out.println("在check_row函数里num = " + num);
//如果有了五个或五个以上的连续相同颜色就给出判断 true
if(num >= 5)
return true;
else
return false;
}
7)check_columns函数的实现
private boolean check_columns(int x, int y) {
int num = 0; //表示相同颜色的棋子个数
//开始向右找相邻的相同颜色的
for(int i = y ; i < Config.ROWS ; i++){
if(FiveChress_array[y][x] == FiveChress_array[i][x]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
//开始向左找相邻的相同颜色的
for(int i = y ; i > 0 ; i--){
if(FiveChress_array[y][x] == FiveChress_array[i-1][x]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
System.out.println("在check_columns函数中num = " + num);
//如果有了五个或五个以上的连续相同颜色就给出判断 true
if(num >= 5)
return true;
else
return false;
}
8)check_left函数的实现
private boolean check_left(int x, int y) {
int num = 0; //表示相同颜色的棋子个数
//定义ix,jy记录下(y,x);
int ix = x;
int jy = y;
//开始向右上找相邻的相同颜色的
for(int i = x, j = y;(Config.COLUMNS - i) < j ? (i < Config.COLUMNS) : j >= 0 ; i++,j--){
if(FiveChress_array[y][x] == FiveChress_array[j][i]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
//开始向左下角找相邻的相同颜色的
for(int i = ix, j = jy ; i <(Config.ROWS - j) ? i>=0 : (j < Config.ROWS) ; i--,j++){
if(FiveChress_array[jy][ix] == FiveChress_array[j][i]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
//在两个for循环里有两次和自己比较颜色
num--;
System.out.println("在check_left函数里num = " + num);
//如果有了五个或五个以上的连续相同颜色就给出判断 true
if(num >= 5)
return true;
else
return false;
}
9)check_right函数的实现
private boolean check_right(int x, int y) {
int num = 0; //表示相同颜色的棋子个数
//定义i,j记录下(y,x);
int ix = x;
int jy = y;
//开始向右下角找相邻的相同颜色的
for(int i = x, j = y;(Config.COLUMNS - i) < (Config.ROWS - j) ? (i < Config.COLUMNS) : (j < Config.ROWS) ; i++,j++){
if(FiveChress_array[jy][ix] == FiveChress_array[j][i]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
//开始向左上角找相邻的相同颜色的
for(int i = ix, j = jy ; i < j ? i >= 0 : j >= 0 ; i--,j--){
if(FiveChress_array[y][x] == FiveChress_array[j][i]){
//找到了 就加一
num ++;
}else{
//找到了有一个不是 就跳出
break;
}
}
//在两个for循环里有两次和自己比较颜色
num--;
System.out.println("在check_right函数里num = " + num);
//如果有了五个或五个以上的连续相同颜色就给出判断 true
if(num >= 5)
return true;
else
return false;
}
这些个方法都大同小异,主要注意它们的坐标,和递增递减关系,行和列一定不能表示错。x的改变就是列数的改变,y的改变就是行数的改变。
写到这里基本上人人五子棋就完成了,下面要做的就是重绘,因为之前我们已经有用一个二维数组及路线每一个交点的属性,现在这个也就比较好实现了。
10)paint函数的重写
public void paint(Graphics g1){
//先绘出各个组件
super.paint(g1);
//绘出棋盘
DrawCheesTable(g1);
//重绘棋子
DrawChees(g1);
}
11)重绘棋子函数的实现
private void DrawChees(Graphics g1) {
// TODO Auto-generated method stub
for(int i = 0; i < Config.ROWS; i++){
for(int j = 0; j < Config.COLUMNS; j++){
//得到每一个交点的值
xx = Config.X0 + Config.SIZE * j;
yy = Config.Y0 + Config.SIZE * i;
if(FiveChress_array[i][j] == 1){
g.setColor(Color.BLACK);
g.fillOval(xx - Config.CHESS_SIZE/2, yy - Config.CHESS_SIZE/2, Config.CHESS_SIZE, Config.CHESS_SIZE);
}else if(FiveChress_array[i][j] == -1){
g.setColor(Color.WHITE);
g.fillOval(xx - Config.CHESS_SIZE/2, yy - Config.CHESS_SIZE/2, Config.CHESS_SIZE, Config.CHESS_SIZE);
}
}
}
}
现在人人五子棋就完成了。
但,这还是无比不够的。首先非常重要的界面不够美观,悔棋,重新开始,判断输赢后就不可以再下棋等等都还没有实现,留给读者们自己扩展了。