版權信息:來源於課件
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;
}