java擲骰子游戲聯機版
阿新 • • 發佈:2019-02-10
目前只實現了一個人的客戶端,由服務端進行判斷輸贏,返回結果!人數,可以自行增加!
我使用多執行緒的方式,即每次押注都會通知伺服器端新開執行緒,在參與人數比較少的情況下,不會又問題!但是如果參與人數比較多,由於我的基準值,也就是初始金錢值使用的是同一個靜態變數,必然有可能會造成金錢變化的不及時,並且,所有人用的也都是同一個金錢值,當然也不太合理!
還有許多控制元件的功能並沒有完全實現!
總算是完成了自己的第一個GUI程式吧!
java 的學習至此也要告一段落了,下一階段該學什麼呢?
//客戶端 package cgame; import java.awt.*; import java.awt.event.*; public class ClientGame extends Frame implements WindowListener{ public TextArea ta; public ClientGame(){ super("骰子游戲客戶端"); MenuUI bar=new MenuUI(this);//設定框架選單欄 //註冊時間監聽//第一個this表示自己,給自己註冊監聽器,是誰,還是自己(第二個this) //註冊監聽器的意思就是說,被監聽物件有動作時,自動執行監聽器的對應函式 this.addWindowListener(this); //設定主介面 initUI(); this.setSize(400,400); this.setVisible(true); } private void initUI(){ //初始化上部 Panel pNorth=initNorth(); this.add(pNorth,BorderLayout.NORTH); //初始化右側 Panel pEast=initEast(); this.add(pEast, BorderLayout.EAST); //初始化中部 Panel pCenter=initCenter(); this.add(pCenter, BorderLayout.CENTER); } //初始化上部函式,返回上部面板 private Panel initNorth(){ Panel p=new Panel();//新建一個面板物件 //設定佈局管理器 BorderLayout b1=new BorderLayout(); p.setLayout(b1); //建立控制元件 Label lbTitle=new Label("歡迎加入骰子大戰"); //新增一個多行文字空間 ta=new TextArea();//換成類屬性,這個每個版塊都可以呼叫了 ta.append("系統提示資訊:\n"); ta.append("初始金錢:1000\n"); //設定不可編輯 ta.setEditable(false); //新增到容器 p.add(lbTitle,BorderLayout.NORTH); p.add(ta,BorderLayout.CENTER); //設定背景色 p.setBackground(Color.GRAY); return p; } //初始化右側函式,返回右側面板 private Panel initEast(){ Panel p=new Panel(); //設定佈局管理器為邊界式 BorderLayout bl=new BorderLayout(); p.setLayout(bl); //建立控制元件 Label lbTitle=new Label("當前線上:"); List listUser=new List(); listUser.add("所有人"); listUser.add("Tom"); listUser.add("Mike"); //新增控制元件到容器 p.add(lbTitle, BorderLayout.NORTH); p.add(listUser,BorderLayout.CENTER); //設定背景色 p.setBackground(Color.CYAN); return p; } //初始化中部函式,返回中部面板 private Panel initCenter(){ Panel p=new Panel(); //設定佈局管理器 GridLayout gl=new GridLayout(4,4); p.setLayout(gl); //建立控制元件物件,並依次新增 String[] content={ "回顯功能","200","192.168.1.108","Txt3", "回顯功能","Label1","Label2","Label3", "Label4","Label5","Label6","Label7", "壓大","壓小","聊天","Button3", }; //為了方便呼叫標籤元件,構造Label元件放在構造txt前面 Label[] DicNum=new Label[8]; int j=0; for(int i=4;i<12;i++,j++){ DicNum[j]=new Label(content[i]); p.add(DicNum[j]); } //構造Txt元件 TextField[] userIP=new TextField[4]; for(int i=0;i<4;i++){ userIP[i]=new TextField(content[i]); p.add(userIP[i]); } //註冊Text0的監視器,這裡實際是userIP[0] TextToLabel ttl=new TextToLabel(userIP[0],DicNum[0],ta);//將事件源文字框傳過去 userIP[0].addActionListener(ttl); //構造 button元件 j=0; Button[] btn=new Button[4];//這裡僅僅只是宣告,還不算初始化 for(int i=12;i<16;i++,j++){ btn[j]=new Button(content[i]); p.add(btn[j]); } //為壓大和壓小按鈕註冊監聽器,將文字框1都傳過去,按鈕和多行文字控制元件也要傳過去 //文字框1押注金額0-3,傳遞的引數:文字框1:輸押注金額,文字框2:輸ip, //按鈕:壓大小,多行文字:顯示提示 DicGame dg=new DicGame(userIP[1],userIP[2],btn[0],btn[1],ta); btn[0].addActionListener(dg); btn[1].addActionListener(dg); btn[2].addActionListener(dg); return p; } public static void main(String[] args) { // TODO 自動生成的方法存根 ClientGame ss=new ClientGame(); } @Override public void windowActivated(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowClosed(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowClosing(WindowEvent arg0) { // TODO 自動生成的方法存根 System.exit(0); } @Override public void windowDeactivated(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowDeiconified(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowIconified(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowOpened(WindowEvent arg0) { // TODO 自動生成的方法存根 } } package cgame; import java.awt.*; import java.util.*; import java.awt.event.*; import java.io.*; import java.net.*; public class DicGame implements ActionListener { Label lab1; TextArea ta; TextField txt1,txt2;//txt1用於押注金額,txt2用於輸入ip Button btn0,btn1,btn2; static int money=1000;//初始金額 public DicGame(TextField txt1,TextField txt2,Button btn0,Button btn1,TextArea ta){ this.txt1=txt1; this.txt2=txt2; this.btn0=btn0; this.btn1=btn1; this.btn2=btn2; this.ta=ta; } @Override public void actionPerformed(ActionEvent e) { // TODO 自動生成的方法存根 Socket socket=null;//客戶端 宣告網路連線物件 InputStream is=null; OutputStream os=null; //伺服器端IP地址在txt2中,通過函式來得到 String serverIP=addIP(txt2); //伺服器埠號 int port=10000; //傳送內容就限定為byte型的0和1,壓大是1,壓小是0 //兩個按鈕註冊的是同一個監聽物件,就需要得到事件源,判斷是哪個按鈕事件 String data=null; Button b=(Button)e.getSource(); if(b==btn0) data="1";//壓大 else if(b==btn1) data="0";//壓小 //獲得押注金額,txt1內容轉換為字串 String stake=txt1.getText(); //將多個String連線成一個字串,大小和押注金額 String sdata=linkData(data,stake); showLabel(sdata); try{ //建立連線 socket=new Socket(serverIP,port); //傳送資料 //就簡單理解為,此操作後,os就是連線輸出流,輸出內容寫入到輸出流os中即可 os=socket.getOutputStream();//得到網路連線的輸出流 os.write(sdata.getBytes()); //接受資料 is=socket.getInputStream();//同上理解,得到連線輸入流 byte[]d=new byte[1024]; int n=is.read(d); //輸出反饋資料 String s=new String(d,0,n);//反饋資料即為此s字串 showLabel(s); //解析s字串,並輸出結果 showResult(s); }catch(Exception e1){ e1.printStackTrace(); }finally{ try{ //關閉流和連線 is.close(); os.close(); socket.close(); }catch(Exception e2){} } } String addIP(TextField txt2){ return txt2.getText();//這裡對IP地址的非法情況不做判斷,由連線異常來反應 } //將傳送資料都整合到一個字串裡面 String linkData(String str1,String str2){ //StringBuffer sb=new StringBuffer(str1); //sb.append(str2); return str1+" "+str2;//String本來就過載了+,可以直接使用 } //反饋資料解析並顯示函式 void showResult(String s){ StringTokenizer an=new StringTokenizer(s," ");//空格作為分隔依據 String str1=an.nextToken(); String str2=an.nextToken();//我這種簡單的網路協議就不需要用迴圈了 //資料解析完成後顯示 showLabel(str1);showLabel("顯示麼?"); if(str1=="0"){ showLabel("輸"); }else { showLabel("贏"); } showLabel("剩餘金錢:"); showLabel(str2); } //將字串在多行文字上顯示 public void showLabel(String s){ ta.append(s+'\n'); } } package cgame; import java.awt.*; import java.awt.event.*; import javax.swing.JOptionPane; public class MenuUI implements ActionListener { MenuItem versionMi; MenuItem authorMi; Frame frm; public MenuUI(Frame frm){ this.frm=frm;//傳遞框架物件,不然彈出對話方塊的時候,找不到依賴對話方塊 MenuBar bar=new MenuBar();//建立選單欄 //建立選單 Menu fileMenu=new Menu("檔案"); Menu helpMenu=new Menu("幫助"); Menu aboutMenu=new Menu("關於"); //建立選單項 MenuItem openMi=new MenuItem("開啟"); MenuItem printMi=new MenuItem("列印"); MenuItem exitMi=new MenuItem("退出"); MenuItem helpMi=new MenuItem("幫助"); versionMi=new MenuItem("版本"); authorMi=new MenuItem("作者"); //新增到選單 fileMenu.add(openMi); fileMenu.add(printMi); fileMenu.add(exitMi); aboutMenu.add(versionMi); aboutMenu.add(authorMi); helpMenu.add(helpMi); helpMenu.add(aboutMenu); //新增到選單欄 bar.add(fileMenu); bar.add(helpMenu); //設定選單欄 frm.setMenuBar(bar); //註冊同一個監聽器,方法內部判斷髮生事件的選單項,然後進行不同的邏輯處理 versionMi.addActionListener(this); authorMi.addActionListener(this); } public void actionPerformed(ActionEvent e){ //獲得發生事件的控制元件 MenuItem mi=(MenuItem)e.getSource(); //判斷,彈出有模式對話方塊 if(mi==versionMi){ JOptionPane.showMessageDialog(frm, "v1.2\n單機擲骰子","版本", JOptionPane.INFORMATION_MESSAGE); } else if(mi==authorMi){ JOptionPane.showMessageDialog(frm, "Andy\n座右銘:勤耕不輟,方得始終!","作者", JOptionPane.INFORMATION_MESSAGE); } } } package cgame; import java.awt.*; import java.awt.event.*; public class TextToLabel implements ActionListener { TextField txt1; Label Lab1; TextArea ta; //構造方法要將事件源文字框傳過來 public TextToLabel(TextField txt1,Label Lab1,TextArea ta){ this.txt1=txt1; this.Lab1=Lab1; this.ta=ta; } @Override public void actionPerformed(ActionEvent e) { // TODO 自動生成的方法存根 String word=txt1.getText();//獲得輸入文字 Lab1.setText(word); ta.append(word+'\n'); System.out.println(word); } } //********************************************************* //伺服器端 package sgame; import java.awt.*; import java.awt.event.*; public class DicGame implements ActionListener { Label lab1; TextArea ta; TextField txt2; Button btn0,btn1; static int money=1000;//初始金額 public DicGame(TextField txt2,Label lab1,Button btn0,Button btn1,TextArea ta){ this.lab1=lab1; this.txt2=txt2; this.btn0=btn0; this.btn1=btn1; this.ta=ta; } @Override public void actionPerformed(ActionEvent e) { // TODO 自動生成的方法存根 //showLabel(changeChar(money),ta);//顯示初始金額 int diceNum=0; int type=0; //判斷押寶型別 //獲得按鈕物件 Button b=(Button)e.getSource(); if(b==btn0) type=1;//1壓大 else if(b==btn1) type=2;//2壓小 int cMoney=changeInt(); boolean success; //遊戲過程,應該是不需要死迴圈的 //輸入非法,則程式直接完成了,GUI執行緒本身是死迴圈了,輸入正確執行else if(!checkCMoney(money,cMoney)){ showLabel("輸入非法",ta); } //輸入有效後,開始擲骰子 else { diceNum=doDice();//擲骰子 //判斷勝負 success=isSuccess(type,diceNum); //金錢變化 money=changeMoney(money,success,cMoney); //遊戲結束 if(money<=0){ showLabel("請充值!",ta); } } } //定義一個將整形轉換為字串的函式 public String changeChar(int i){ return Integer.toString(i); } //將字串在多行文字上顯示 public void showLabel(String s,TextArea ta){ ta.append(s+'\n'); } //將文字框獲得內容轉為整形數 public int changeInt(){ String s=txt2.getText(); return Integer.parseInt(s); } //判定下注金額是否非法 public static boolean checkCMoney(int money,int cMoney){ if(cMoney<=0) return false; else if(cMoney<=money) return true; else return false; } //擲骰子,返回點數和 public static int doDice(){ int n=(int)(Math.random()*6)+1; int n1=(int)(Math.random()*6)+1; int n2=(int)(Math.random()*6)+1; return n+n1+n2; } //勝負判斷 public boolean isSuccess(int type,int diceNum){ //計算莊家型別 int bankerType=0; if(diceNum<=9){ bankerType=2; }else{ bankerType=1; } if(bankerType==type){ showLabel("贏",ta); return true; } else{ showLabel("輸",ta); return false; } } //金錢變化 public int changeMoney(int money,boolean success,int cMoney){ if(success){ money+=cMoney; }else{ money-=cMoney; } showLabel("當前金錢:",ta); showLabel(changeChar(money),ta); return money; } } package sgame; import java.awt.*; import java.io.*; import java.net.*; import java.util.*; public class LogicThread extends Thread { Socket socket; TextArea ta; InputStream is; OutputStream os; int dicNum,result,nowMoney;//返回結果為result nowMoney public LogicThread(Socket socket,TextArea ta){ this.socket=socket; this.ta=ta; start(); } public void run(){ byte[]b=new byte[1024]; try{ //得到連線中的輸入輸出流 os=socket.getOutputStream(); is=socket.getInputStream(); //讀取資料 int n=is.read(b); //邏輯處理 String str=sHandle(b,n);//n代表實際讀取位元組數 //傳送str內容 os.write(str.getBytes()); ta.append("&&&&&&&&"); ta.append(str); System.out.println("&*&*&*執行緒到這裡就該結束了"); }catch(Exception e){ e.printStackTrace(); }finally{ close(); } } //關閉流和連線 private void close(){ try{ //關閉流和連線 System.out.println("&*&*&*&*&*"); os.close(); is.close(); socket.close(); }catch(Exception e){} } //邏輯處理函式 String sHandle(byte[] b,int n){ //首先要將b陣列解析出來 String str=new String(b,0,n);//將位元組陣列轉換為字串 ta.append(str+'\n'); StringTokenizer ad=new StringTokenizer(str," ");//空格分隔 String str1=ad.nextToken();//實際則代表了客戶端發來的壓大小資訊 String str2=ad.nextToken();//代表客戶所押金額 ta.append("********"); ta.append(str1+'\n'); ta.append(str2+'\n'); //判斷客戶所押為大還是小,並得到押注金額,將字串轉變為整型數 int type=Integer.parseInt(str1); int cMoney=Integer.parseInt(str2); System.out.println(type); System.out.println(cMoney); ta.append("*********"); //判斷押注金額是否非法,呼叫DicGame類中的靜態變數和方法 boolean success; System.out.println("靜態變數金錢:"+DicGame.money); if(!DicGame.checkCMoney(DicGame.money,cMoney)){ ta.append("輸入非法");//將字串在多行文字空間中顯示 result=0; nowMoney=DicGame.money; } else{//輸入有效開始判斷大小 dicNum=DicGame.doDice(); //判斷勝負 success=isSuccess(type,dicNum); //金錢變化,這裡金錢使用的是靜態變數,但是還是值傳遞的方式, //每次不管怎麼變,原來的靜態變數沒有變,所以總是以1000為基準 nowMoney=changeMoney(DicGame.money,success,cMoney); //遊戲結束 if(nowMoney<=0) ta.append("請充值!\n"); } //遊戲至此就已經完成,轉換要返回的資料並返回 return Integer.toString(result)+" "+Integer.toString(nowMoney); } //勝負判斷 public boolean isSuccess(int type,int dicNum){ //計算莊家型別 int bankerType=0; if(dicNum<=9){ bankerType=0; }else{ bankerType=1; } if(bankerType==type){ ta.append("贏\n"); result=1; return true; } else{ ta.append("輸\n"); result=0; return false; } } //金錢變化 public int changeMoney(int money,boolean success,int cMoney){ if(success){ money+=cMoney; }else{ money-=cMoney; } System.out.println("變化後的金錢:"); System.out.println(money); ta.append("剩餘金錢:\n");; //將剩餘金錢轉換為字串並顯示 ta.append(Integer.toString(money)); DicGame.money=money;//金錢變化後,要修改基準值 return money; } } package sgame; import java.awt.*; import java.awt.event.*; import javax.swing.JOptionPane; public class MenuUI implements ActionListener { MenuItem versionMi; MenuItem authorMi; Frame frm; public MenuUI(Frame frm){ this.frm=frm;//傳遞框架物件,不然彈出對話方塊的時候,找不到依賴對話方塊 MenuBar bar=new MenuBar();//建立選單欄 //建立選單 Menu fileMenu=new Menu("檔案"); Menu helpMenu=new Menu("幫助"); Menu aboutMenu=new Menu("關於"); //建立選單項 MenuItem openMi=new MenuItem("開啟"); MenuItem printMi=new MenuItem("列印"); MenuItem exitMi=new MenuItem("退出"); MenuItem helpMi=new MenuItem("幫助"); versionMi=new MenuItem("版本"); authorMi=new MenuItem("作者"); //新增到選單 fileMenu.add(openMi); fileMenu.add(printMi); fileMenu.add(exitMi); aboutMenu.add(versionMi); aboutMenu.add(authorMi); helpMenu.add(helpMi); helpMenu.add(aboutMenu); //新增到選單欄 bar.add(fileMenu); bar.add(helpMenu); //設定選單欄 frm.setMenuBar(bar); //註冊同一個監聽器,方法內部判斷髮生事件的選單項,然後進行不同的邏輯處理 versionMi.addActionListener(this); authorMi.addActionListener(this); } public void actionPerformed(ActionEvent e){ //獲得發生事件的控制元件 MenuItem mi=(MenuItem)e.getSource(); //判斷,彈出有模式對話方塊 if(mi==versionMi){ JOptionPane.showMessageDialog(frm, "v1.2\n單機擲骰子","版本", JOptionPane.INFORMATION_MESSAGE); } else if(mi==authorMi){ JOptionPane.showMessageDialog(frm, "Andy\n座右銘:勤耕不輟,方得始終!","作者", JOptionPane.INFORMATION_MESSAGE); } } } package sgame; import java.awt.*; import java.net.*; import java.awt.event.*; public class ServiceGame extends Frame implements WindowListener{ static public TextArea ta;//設定為公共屬性,大家都可以呼叫來顯示提示資訊 public ServiceGame(){ super("骰子游戲服務端"); MenuUI bar=new MenuUI(this);//設定框架選單欄 //註冊時間監聽//第一個this表示自己,給自己註冊監聽器,是誰,還是自己(第二個this) //註冊監聽器的意思就是說,被監聽物件有動作時,自動執行監聽器的對應函式 this.addWindowListener(this); //設定主介面 initUI(); this.setSize(400,400); this.setVisible(true); } private void initUI(){ //初始化上部 Panel pNorth=initNorth(); this.add(pNorth,BorderLayout.NORTH); //初始化右側 Panel pEast=initEast(); this.add(pEast, BorderLayout.EAST); //初始化中部 Panel pCenter=initCenter(); this.add(pCenter, BorderLayout.CENTER); } //初始化上部函式,返回上部面板 private Panel initNorth(){ Panel p=new Panel();//新建一個面板物件 //設定佈局管理器 BorderLayout b1=new BorderLayout(); p.setLayout(b1); //建立控制元件 Label lbTitle=new Label("歡迎加入骰子大戰"); //新增一個多行文字空間 ta=new TextArea();//換成類屬性,這個每個版塊都可以呼叫了 ta.append("系統提示資訊:\n"); ta.append("初始金錢:1000\n"); //設定不可編輯 ta.setEditable(false); //新增到容器 p.add(lbTitle,BorderLayout.NORTH); p.add(ta,BorderLayout.CENTER); //設定背景色 p.setBackground(Color.GRAY); return p; } //初始化右側函式,返回右側面板 private Panel initEast(){ Panel p=new Panel(); //設定佈局管理器為邊界式 BorderLayout bl=new BorderLayout(); p.setLayout(bl); //建立控制元件 Label lbTitle=new Label("當前線上:"); List listUser=new List(); listUser.add("所有人"); listUser.add("Tom"); listUser.add("Mike"); //新增控制元件到容器 p.add(lbTitle, BorderLayout.NORTH); p.add(listUser,BorderLayout.CENTER); //設定背景色 p.setBackground(Color.CYAN); return p; } //初始化中部函式,返回中部面板 private Panel initCenter(){ Panel p=new Panel(); //設定佈局管理器 GridLayout gl=new GridLayout(4,4); p.setLayout(gl); //建立控制元件物件,並依次新增 String[] content={ "Txt1","Txt2","Txt3","Txt4", "Label1","Label2","Label3","Label4", "Label5","Label6","Label7","Label8", "壓大","壓小","Button3","Button4", }; //為了方便呼叫標籤元件,構造Label元件放在構造txt前面 Label[] DicNum=new Label[8]; int j=0; for(int i=4;i<12;i++,j++){ DicNum[j]=new Label(content[i]); p.add(DicNum[j]); } //構造Txt元件 TextField[] userIP=new TextField[4]; for(int i=0;i<4;i++){ userIP[i]=new TextField(content[i]); p.add(userIP[i]); } //註冊Text1的監視器,這裡實際是userIP[0] TextToLabel ttl=new TextToLabel(userIP[0],DicNum[0],ta);//將事件源文字框傳過去 userIP[0].addActionListener(ttl); //構造 button元件 j=0; Button[] btn=new Button[4];//這裡僅僅只是宣告,還不算初始化 for(int i=12;i<16;i++,j++){ btn[j]=new Button(content[i]); p.add(btn[j]); } //為壓大和壓小按鈕註冊監聽器,將標籤2,3,4和文字框1都傳過去,按鈕也要傳過去 DicGame dg=new DicGame(userIP[1],DicNum[1],btn[0],btn[1],ta); btn[0].addActionListener(dg); btn[1].addActionListener(dg); return p; } public static void main(String[] args) { // TODO 自動生成的方法存根 ServiceGame ss=new ServiceGame();//這一步只是生成了介面 //就是說只是開啟了一個執行緒來做介面這件事情,主函式,也就是主執行緒,還要往下執行 //System.out.println("hello"); ServerSocket serverSocket=null; Socket socket=null; //監聽埠號 int port=10000; try{ //建立連線 serverSocket=new ServerSocket(port);//監聽程式碼 ta.append("伺服器已啟動\n"); int i=1; while(i>0){ //獲得連線到伺服器端的客戶端連線,釋放伺服器埠,擁塞方法 socket=serverSocket.accept(); //啟動執行緒 System.out.println("執行緒開始"); LogicThread tt=new LogicThread(socket,ta); System.out.println("第"+i+"次迴圈"); i++; System.out.println("執行緒結束了!"); } }catch(Exception e){ e.printStackTrace(); }finally{ try{ //關閉連線 serverSocket.close(); }catch(Exception e){} } } @Override public void windowActivated(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowClosed(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowClosing(WindowEvent arg0) { // TODO 自動生成的方法存根 System.exit(0); } @Override public void windowDeactivated(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowDeiconified(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowIconified(WindowEvent arg0) { // TODO 自動生成的方法存根 } @Override public void windowOpened(WindowEvent arg0) { // TODO 自動生成的方法存根 } } package sgame; import java.awt.*; import java.awt.event.*; public class TextToLabel implements ActionListener { TextField txt1; Label Lab1; TextArea ta; //構造方法要將事件源文字框傳過來 public TextToLabel(TextField txt1,Label Lab1,TextArea ta){ this.txt1=txt1; this.Lab1=Lab1; this.ta=ta; } @Override public void actionPerformed(ActionEvent e) { // TODO 自動生成的方法存根 String word=txt1.getText();//獲得輸入文字 Lab1.setText(word); ta.append(word+'\n'); System.out.println(word); } }