版权信息:来源于课件
Flyweight Pattern Application Programming
应用程序结构,客户端程序结构是一个继承Frame的基本窗体结构,参考代码:
FiveS.java
import java.awt.;
import java.awt.event.;
class FiveS extends Frame
{
FiveS(String s)
{
super(s);
setBounds(100,100,785,620);
setVisible(true);
}
public static void main(String[] args)
{
new FiveS(“广州大学华软软件学院 软件工程系 http://www.sise.com.cn”);
}
}
绘制五子棋棋盘;
容器类都继承一个非常重要的方法paint(Graphics g),参数类型Graphics 在java.awt包中。paint()方法有2种调用方式:
(1)自动调用,类实例时,界面发生变化时等程序将自动调用该方法,类似于VC++中的view类的OnDraw()函数。
(2)由程序员调用。程序员通过调用repaint()方法,间接调用paint()方法。VC++调用view类的OnDraw()函数是通过调用Invalidate()函数或InvaliteRect()函数实现的。在上边程序中添加paint()方法,即可实现绘制五子棋棋盘:红色部分是添加的paint()方法部分。这里棋盘线的间距定为30个像素。
import java.awt.;
import java.awt.event.;
class FiveS extends Frame
{
FiveS(String s)
{
super(s);
setBounds(100,100,785,620);
setVisible(true);
}
public void paint(Graphics g)
{
for (int i=30;i<=450;i=i+30)
{
g.drawLine(30,i+60,450,i+60);
g.drawLine(i,30+60,i,450+60);
}
for(int k=30;k<=450;k+=30)
{
g.drawString(String.valueOf(k/30),k-5,12+60);
if(k/30<10)
g.drawString(String.valueOf(k/30),12,k+5+60);
else
g.drawString(String.valueOf(k/30),5,k+5+60);
}
}
public static void main(String[] args)
{
new FiveS("广州大学华软软件学院 软件工程系 http://www.sise.com.cn");
}
}
绘制棋子。绘制棋子的编程思路:
(1) 在类中添加一个二维整数数组成员int chess[][];其中的元素对应棋盘上的落子点,如chess[0][0]对应棋盘最左上的落子点、chess[14][14]对应棋盘最右下的落子点等。chess数组中的元素,记载棋盘上的落子状态。约定数组元素值为0,代无表物棋子,1代表落子为白子,10 代表落子为黑子。
(2) 添加鼠标事件监听器MouseListener,当下棋时,在棋盘上按下鼠标按键,此时事件监听器会自动产生MouseEvent类的对象e。e.getX()和e.getY()方法会返回鼠标点击的坐标值。如鼠标点击在下图中(4,4)网格点附近坐标为(125,155),画子点的坐标应为(120,150),对应的chess数组元素应为chess[3][3]。e.getX()的值为125,e.getY()的值为155。如下算法可得到数组元素的下标:
int x=(e.getX()+15)/30-1=(125+15)/30-1=140/30-1=4-1=3;
int y=(e.getY()+15)/30-2=(155+15)/30-2=170/30-2=5-2=3;
如此时落的是黑子,对数组元素做如下处理:
chess[x][y]=10;
然后调用repaint()方法,间接调用paint()。
因此在鼠标事件处理方法中要做的操作是:
计算出对应数组元素下标 x,y值;
Chess[x][y]=10;(如落下的是黑子);
调用repaint() 方法。
(3) 在paint()方法中,遍历数组chess,根据元素值绘制棋子。
这样就能完成绘制棋子操作。
采用GOF享元模式生成棋子类的实例。
参考代码:
import java.awt.;
import java.awt.image.;
import java.awt.event.;
import java.io.;
class JavaChessFive extends Panel implements MouseListener,ActionListener
{
nutFactory nf=new nutFactory();
nut bn=nf.getImage(“white”);
nut wn=nf.getImage(“black”);
int[][] chess=new int[15][15];
boolean s=true;
JavaChessFive()
{
setLayout(null);
setSize(600,600);
setBackground(Color.GREEN);
addMouseListener(this);
}
public void paint(Graphics g)
{
for (int i=30;i<=450;i=i+30)
{
g.drawLine(30,i,450,i);
g.drawLine(i,30,i,450);
g.drawString(String.valueOf(i/30),i-5,20);
g.drawString(String.valueOf(i/30),10,i+5);
}
for(int x=0;x<15;x++)
{
for(int y=0;y<15;y++)
{
if(chess[x][y]==1)
bn.draw(g,x*30-12,y*30-12,this);
else if(chess[x][y]==10)
wn.draw(g,x*30-12,y*30-12,this);
}
}
}
public void mousePressed(MouseEvent e)
{
if (e.getModifiers()==InputEvent.BUTTON1_MASK)
{
int x=(int)e.getX();
int y=(int)e.getY();
int a=(x+15)/30;
int b=(y+15)/30;
if(s)
{
chess[a][b]=1;
}
else
{
chess[a][b]=10;
}
repaint();
s=!s;
}
}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void actionPerformed(ActionEvent e){}
}
class Test extends Frame
{
JavaChessFive p;
Test(String s)
{
super(s);
p=new JavaChessFive();
setLayout(new BorderLayout());
setBounds(100,100,600,600);
add§;
setVisible(true);
}
public static void main(String[] a)
{
new Test(“Java RMI 网络通信 广州大学华软软件学院 软件工程系”);
}
}
abstract class nut extends Canvas
{
Toolkit t=getToolkit();
Image img;
nut()
{
setSize(24,24);
}
void draw(Graphics g,int x,int y,ImageObserver obs)
{
g.drawImage(img,x,y,24,24,obs);
}
}
class white_nut extends nut
{
white_nut()
{
super();
img=t.getImage(“image/white.gif”);
}
}
class black_nut extends nut
{
black_nut()
{
super();
img=t.getImage(“image/black.gif”);
}
}
class nutFactory
{
nut img1,img2;
nutFactory()
{
img1=new white_nut();
img2=new black_nut();
}
nut getImage(String s)
{
if(s.equals(“white”)) return img1;
else if(s.equals(“black”)) return img2;
else return null;
}
}
参考代码的界面:
判断赢棋及悔棋算法
判断赢棋的操作是在落子后进行的,每落一颗子,就要调用判断赢棋的方法,当发生赢棋是给出相应的提示。
如前所述,五子棋棋盘对应一个整形二维数组(chess),棋盘网格对应数组元素,可设置数据元素值来记载棋子。如无棋子时对应数据元素值为0,红棋子对应数据元素值为1,黑棋子对应数据元素值为10;
五子棋棋盘落子的位置与二维数组元素的对应,数组元素初始化为0,红方棋子落子对应数组元素置1,黑方棋子落子对应数组元素置10.
当落子时程序进行判断五子连珠的情况,判断要在8个方向进行,8个方向分成4组,每组包含两个完全相反的方向。
当向右方向试探时,对应棋子位置的数组元素的行下标不增加(加0),列增下标加1,这样对应一个2元组(0,1);向下方向试探时,对应棋子位置的数组元素的的行下标加1,列下标加0,对应的二元组为(1,0),。。依次类推。
行 列
右 0 1
右下 1 1
下 1 0
左下 1 -1
左 0 -1
左上 -1 -1
上 -1 0
右上 -1 1
为了方便算法设计,将8个方向拆开成两个对应相反方向的二维数组,存储4个方向及对应相反方向值。一个数组为:
cc[4][2]={ 0, 1,
1, 1,
1, 0,
1, -1 };
另一个数组为:
dd[4][2]={ 0, -1,
-1, -1,
-1, 0,
-1, 1 };
当落子时,同时向4个方向及其相反方向在连续的位置上搜索同色棋子的对应的数组元素值,并求和,两个相反方向求和的结果相加,如果是>=6并且>=10,判断红棋胜出,如果相加结果为>=60,判断黑方胜出。
参考算法:
int win(int x, int y)
{
final int[][] cc={{0, 1},{ 1, 1},{ 1,0},{ 1,-1}};
final int[][] dd={{0,-1},{-1,-1},{-1,0},{-1, 1}};
int s,s1,s2,x1,y1;
if(x>=0 && x<15 && y>=0 && y<15)
{
for(int i=0;i<4;i++)
{
s=chess[x][y];
s1=chess[x][y];
s2=chess[x][y];
x1=x;y1=y;
x1+=cc[i][0];
y1+=cc[i][1];
if((x1>=0) && (x1<15) && (y1>=0)&&(y1<15))
{
while(chess[x1][y1]==s )
{
s1+=chess[x1][y1];
x1+=cc[i][0];
y1+=cc[i][1];
if(x1<0 || x1>14 || y1<0 || y1>14) break;
}
}
x1=x;
y1=y;
x1+=dd[i][0];
y1+=dd[i][1];
if((x1>0) && (x1<15) && (y1>0)&&(y1<15))
{
while(chess[x1][y1]==s)
{
s2+=chess[x1][y1];
x1+=dd[i][0];
y1+=dd[i][1];
if(x1<0 || x1>14 || y1<0 || y1>14) break;
}
}
if((s1+s2)>=6 && (s1+s2)<=10) return 1;
if((s1+s2)>=60) return 2;
}
return 0;
}
else
{
return 0;
}
}
悔棋操作需要记载下棋时落子的先后序列,可采用栈来实现,栈元素有2个整形数据成员,用来记载棋子位置对应数组元素的下标。落子进行进栈操作,悔棋进行出栈操作,把出栈元素对应的chess数组元素的值赋0;然后调用repaint()方法,刷新棋盘。
重新开始下棋。
重新开棋操作可使用菜单命令:
首先初始化chess数组;
然后清空聊天信息列表等操作。参考代码:
void init()
{
for(int i=0;i<15;i++)
{
for(int j=0;j<15;j++)
{
chess[i][j]=0;
}
}
repaint();
t1.setText("");
READY=false;
}