詳細
プロトコル(1行単位) 1. 「ユーザ名の登録」 サーバ -> クライアント: プログラム名 クライアント -> サーバ: ユーザ名(ここで嘘をつくと,勝っても正しく記録されない) 2. 「出す手を伝える」 クライアント -> サーバ: 手(0,1,2) サーバ -> クライアント: 相手の手 3. 50回の勝負が終わる前は2に戻る. 4. 結果の表示 サーバ -> クライアント : 5. 切断 サーバの側から接続を切断する.ヒント
プロトコルを理解するために,
telnet 133.11.45.231 3331とやって,名前の入力,[0-2]の数字の入力をやってみるのも良い.
// Randomクラスを利用するので java.util.*をインポート import java.util.*; // 入出力関係のクラスを利用するので java.util.*をインポート import java.io.*; // ネットワーク関係のクラスを利用するので java.net.*をインポート import java.net.*; class Janken{ static void janken(BufferedReader in,PrintWriter out) throws IOException{ Random r=new Random(); String s=in.readLine(); System.out.println("対戦相手は"+s); // 自分の手は最初は乱数で int myHand=r.nextInt(3); // 自分の手と相手の手を記録するための配列 // 今は記録するだけで戦略に生かしていない. int myHands[]=new int[50]; int opHands[]=new int[50]; // 50回の繰り返し for(int i=0;i< 50;i++){ // 自分の手を送る out.println(myHand); // サーバからの入力をもらう if((s=in.readLine())==null){ System.exit(1); } // 相手の手を受け取る int opHand=Integer.parseInt(s); // 記録する myHands[i]=myHand; opHands[i]=opHand; if(myHand==opHand+1 || myHand==opHand-2){ // 負けたとき // 前回,相手が出した手を出す myHand=opHand; } else{ // 勝ったか引き分けのとき // 乱数で次の手を決定する myHand=r.nextInt(3); } } while((s=in.readLine())!=null) System.out.println(s); } public static void main(String args[]) throws IOException{ if(args.length<2) usage(); Socket sock=new Socket(args[0],Integer.parseInt(args[1])); // サーバから送られてくるメッセージの文字コードはUTF-8なので, // その変換をおこなうInputStreamReaderを作る BufferedReader in=new BufferedReader(new InputStreamReader(sock.getInputStream(),"UTF-8")); PrintWriter out=new PrintWriter(sock.getOutputStream(),true); out.println(System.getProperty("user.name")); janken(in,out); } static void usage(){ System.err.println("使い方: java Janken ホスト名 ポート番号"); System.exit(1); } }
ux103$ java Janken 133.11.45.231 3331 対戦相手はKodomo1 Kodomo1 22222222222222222222222222222222222222222222222222 ktanaka 10202202021021110212102111102022020202202020202122 Tue Nov 15 16:25:30 JST 2005 Kodomo1 が ktanakaに54 点で勝ちましたプロトコルが決まっているので,スレッドを使う必要はない.
このサンプルプログラムは,教育用計算機システムの計算機上で実行することを前提に書かれている.自宅のPC等で実行する場合は,
out.println(System.getProperty("user.name"));の部分を変更して,
out.println("g5XXXXX");のようにしないと,サーバに自分のユーザ名がクリアしたという記録が残らない.
// 入出力ストリームを使うので,java.io.* を import import java.io.*; // ソケットを使うので java.net.* を import import java.net.*; // randomを使う import java.util.*; class JankenGame implements Runnable{ Socket sock[]=new Socket[2]; BufferedReader[] ins=new BufferedReader[2]; PrintWriter[] outs=new PrintWriter[2]; String names[]=new String[2]; JankenServer js; JankenGame(JankenServer js,Socket sock0,Socket sock1){ this.js=js; this.sock[0]=sock0; this.sock[1]=sock1; } public void run(){ int i,j; try{ for(i=0;i<2;i++){ outs[i] = new PrintWriter(sock[i].getOutputStream(),true); ins[i] = new BufferedReader(new InputStreamReader(sock[i].getInputStream())); names[i]=ins[i].readLine(); } for(i=0;i<2;i++) outs[i].println(names[1-i]); String[] s=new String[2]; int[][] gameRec=new int[2][50]; gameLoop: for(j=0;j<50;j++){ for(i=0;i<2;i++){ if((s[i]=ins[i].readLine())==null){ break gameLoop; } gameRec[i][j]=Integer.parseInt(s[i]); } for(i=0;i<2;i++){ outs[i].println(gameRec[1-i][j]); } } if(j==50){ String str=JankenRobot.recToStr(names[0],gameRec[0])+JankenRobot.recToStr(names[1],gameRec[1]); int w; for(w=0,i=0;i<50;i++) w+=JankenRobot.win(gameRec[0][i],gameRec[1][i]); str=str+new Date()+"\n"; if(w>0){ str=str+names[0]+" が "+names[1]+"に"+(w+50)+" 点で勝ちました"; } else if(w<0){ str=str+names[1]+" が "+names[0]+"に"+(50-w)+" 点で勝ちました"; } else str=str+names[0]+" と "+names[1]+"は引き分けでした"; for(i=0;i<2;i++) outs[i].println(str); js.println(str); } else{ for(i=0;i<2;i++) outs[i].println("プログラムが途中で中断しました"); js.println("プログラムが途中で中断しました"); } sock[0].close(); sock[1].close(); } catch(IOException e){ System.err.println(e); } } } class JankenRobot implements Runnable{ Socket sock; JankenServer js; int type; PrintWriter out; BufferedReader in; String[] robotNames={"","Kodomo1","Kodomo2","Kodomo3","Otona1","Otona2"}; JankenRobot(JankenServer js,Socket sock,int type){ this.js=js; this.sock=sock; this.type=type; } // m0がm1に勝つ時は 1, 引き分けが0,敗けは -1 static int win(int m0, int m1){ if(m0-m1== -1 || m0-m1== 2) return 1; else if(m0==m1) return 0; else return -1; } static String recToStr(String name,int[] game){ String s=(name+" ").substring(0,8); int i; for(i=0;i<50;i++) s=s+game[i]; return s+"\n"; } Random r=new Random(); int ir; int robot(int i,int[][] rec){ if(i==0){ ir=r.nextInt(3); return ir; } switch(type){ case 1: /* Kodomo1: いつも同じものを出す */ return ir; case 2: // Kodomo2 /* 削除 */ case 3: // Kodomo3 /* 削除 */ case 4:{ // Otona1 /* 削除 */ } case 5:{ /* Otona2: 直前の自分の手,相手の手を元に次の手を推察する */ int j; int hist[]=new int[3]; if(i==1) return (rec[1][0]+2)%3; for(j=1;j< i;j++){ if(rec[0][j-1]==rec[0][i-1] && rec[1][j-1]==rec[1][i-1]) hist[rec[1][j]]++; } if(hist[0]>=hist[1] && hist[0]>=hist[2]) return 2; else if(hist[1]>=hist[2]) return 0; else return 1; } } return 0; } public void run(){ try{ out = new PrintWriter(sock.getOutputStream(),true); in = new BufferedReader(new InputStreamReader(sock.getInputStream())); out.println(robotNames[type]); int i; String name=in.readLine(); String s=null; int[][] gameRec=new int[2][50]; for(i=0;i<50;i++){ int a=robot(i,gameRec); if((s=in.readLine())==null) break; int b=Integer.parseInt(s); gameRec[0][i]=a; gameRec[1][i]=b; out.println(a); } if(i==50){ String str=recToStr(robotNames[type],gameRec[0])+recToStr(name,gameRec[1]); int w; for(w=0,i=0;i<50;i++) w+=win(gameRec[0][i],gameRec[1][i]); str=str+new Date()+"\n"; if(w>0){ str=str+robotNames[type]+" が "+name+"に"+(w+50)+" 点で勝ちました"; } else if(w<-10){ str=str+name+" が "+robotNames[type]+"に"+(50-w)+" 点で勝ちました. じゃんけんロボット("+type+")はクリアしました\n"; } else{ str=str+name+" が "+robotNames[type]+"に"+(50-w)+" 点で勝ちました. じゃんけんロボット("+type+")をクリアするには60点必要です\n"; } out.println(str); js.println(str); } else{ out.println("プログラムが途中で中断しました"); js.println("プログラムが途中で中断しました"); } sock.close(); } catch(IOException e){ System.err.println(e); } } } // 呼び出し方 // java JankenServer 対戦モード ポート番号 // 対戦モードは // 0 自由対戦 // 1-5 対応するプログラム class JankenServer{ // コンストラクタ public JankenServer(int type, int port){ Socket sock,waitingSock=null; try{ // ServerSocketを作成 ServerSocket servsock=new ServerSocket(port); // 無限ループ,breakが来るまで while(true){ // クライアントからのアクセスをうけつけた. sock=servsock.accept(); if(type==0){ if(waitingSock==null){ waitingSock=sock; } else{ new Thread(new JankenGame(this,waitingSock,sock)).start(); waitingSock=null; } } else if(type<=5){ new Thread(new JankenRobot(this,sock,type)).start(); waitingSock=null; } } } catch(IOException ioe){ System.out.println(ioe); } } synchronized void println(String s){ System.err.println(s); } public static void main(String args[]){ // インスタンスを1つだけ作る. new JankenServer(Integer.parseInt(args[0]),Integer.parseInt(args[1])); } }