import java.io.*; import java.net.*; import java.util.Random; public abstract class TTTPlayer { //Suppose this could be protected, but "name" could be //such a common variable name it's best done this way. private String name=""; public String GetName() {return name;} private String address="cycle1"; private int port= 6666; public String GetAddress() {return address;} public int GetPort() {return port;} private Socket echoSocket=null; private PrintWriter ToServer = null; private BufferedReader FromServer = null; private BufferedReader stdIn = null; //You'll probably want direct access to these. protected int[] BOARD; protected int N=4; protected int BOARD_SIZE=N*N*N; protected boolean DEBUG=false; //These constructors are the equivalent of both //(3dttt:connect) and (3dttt:login ) in 3dttt.lisp. //Look in connect() and login() for the details. public TTTPlayer() throws IOException { BOARD=new int[N*N*N]; //Try connecting to the server connect(); //The equivalent of (3dttt:login ) in 3dttt.lisp //No name given, so generate one name="Player-"+((new Random()).nextInt(100)); login(); stdIn = new BufferedReader(new InputStreamReader(System.in)); } public TTTPlayer(String na) throws IOException { BOARD=new int[N*N*N]; connect(); //The equivalent of (3dttt:login ) in 3dttt.lisp name=na; login(); stdIn = new BufferedReader(new InputStreamReader(System.in)); } public TTTPlayer(String n, String a, int p) throws IOException { address=a; port=p; BOARD=new int[N*N*N]; connect(); //The equivalent of (3dttt:login ) in 3dttt.lisp //No name given, so generate one name=n; login(); stdIn = new BufferedReader(new InputStreamReader(System.in)); } public TTTPlayer(int nu) throws IOException { N=nu; BOARD=new int[N*N*N]; BOARD_SIZE=N*N*N; //Try connecting to the server connect(); //The equivalent of (3dttt:login ) in 3dttt.lisp //No name given, so generate one name="Player-"+((new Random()).nextInt(100)); login(); stdIn = new BufferedReader(new InputStreamReader(System.in)); } public TTTPlayer(String n, String a, int p, int nu) throws IOException { address=a; port=p; N=nu; BOARD=new int[N*N*N]; BOARD_SIZE=N*N*N; connect(); //The equivalent of (3dttt:login ) in 3dttt.lisp //No name given, so generate one name=n; login(); stdIn = new BufferedReader(new InputStreamReader(System.in)); } public void SetDebug(boolean val){ DEBUG=val; } private int connect() throws IOException{ try { echoSocket = new Socket(address, port); ToServer = new PrintWriter(echoSocket.getOutputStream(), true); FromServer = new BufferedReader(new InputStreamReader(echoSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println("Don't know about 3D TTT game server host."); System.exit(1); } catch (IOException e) { System.err.println("Couldn't get I/O for " + "the connection to the 3D TTT game server host."); System.exit(1); } return 0; } //Return 0 on success, -1 on error. Return value //only for debugging purposes. private int login() throws IOException{ ToServer.println("NAME "+name); ToServer.flush(); String line=""; line=FromServer.readLine(); String[] split_line=line.split(" "); if(split_line[0].equalsIgnoreCase("ERROR")){ System.out.println(line); return -1; } else if(split_line[0].equalsIgnoreCase("OKAY")) { //You're unique so we don't have to do anything. return 0; } else { System.out.println("Unexpected data "+line); return -1; } } //NOTE!!! Not logging out can clog up the server. //Please log out. public void logout() throws IOException{ ToServer.println("QUIT"); ToServer.flush(); try{ ToServer.close(); FromServer.close(); stdIn.close(); echoSocket.close(); } catch (IOException e){ System.err.println("Had trouble logging out.\nError:"+e); System.exit(1); } } public void ListPlayers() throws IOException{ ToServer.println("LIST"); ToServer.flush(); String line=FromServer.readLine(); do{ System.out.println(line); line=FromServer.readLine(); } while(!line.equals("")); } //Return a move from the opponent. protected Move GetMove() throws IOException{ String line=FromServer.readLine(); String[] split_line=line.split(" "); if(split_line[0].equalsIgnoreCase("ERROR")){ System.out.println(line); return null; } else if(split_line[0].equalsIgnoreCase("MOVE")){ //The next element should be an Integer. //When the server returns a move, it is in the form MOVE N. //with the period at the end. We must take out the period. return new Move(Integer.parseInt( split_line[1].substring(0,split_line[1].indexOf(".")) )); } else if(split_line[0].equalsIgnoreCase("WIN")){ return new Move(1,"win"); } else if(split_line[0].equalsIgnoreCase("TIE")){ return new Move(0,"tie"); } else if(split_line[0].equalsIgnoreCase("LOSE")){ return new Move(-1,"lose"); } else{ System.out.println("Unexpected data:: "+line); } return null; } //Send a move to the server. //Return a Move object with // 1, "win" on win // 0, "tie" on tie // 1, "okay" on okay // -9 on error protected Move Move(Move move) throws IOException{ if(move==null) return new Move(-9); if(DEBUG) System.out.println("I'm trying to move to "+move.GetMove()); BOARD[move.GetMove()]=1; ToServer.println("MOVE "+move.GetMove()); ToServer.flush(); String line=FromServer.readLine(); String[] sline=line.split(" "); while(line.equals("ERROR Not your turn!")){ line=FromServer.readLine(); } if(sline[0].equalsIgnoreCase("ERROR")){ System.out.println(line); return new Move(-9); } else if(sline[0].equalsIgnoreCase("OKAY")){ if(DEBUG) System.out.println("I moved to "+move.GetMove()); return new Move(1,"okay"); } else if(sline[0].equalsIgnoreCase("TIE")){ return new Move(0,"tie"); } else if(sline[0].equalsIgnoreCase("WIN")){ return new Move(1,"win"); } else{ System.out.println("Unexpected data: "+line); return new Move(-9); } } //Take a name of a player and tries to play a game against that player. //On error, will print out error message and return -9. //On win, will return 1. //On tie, will return 0. //On loss, will return -1. public int PlayGame(String pname) throws IOException{ ToServer.println("PLAY "+pname); ToServer.flush(); String line=FromServer.readLine(); String[] sline=line.split(" "); if(sline[0].equalsIgnoreCase("ERROR")){ System.out.println(line); return -9; } else if(sline[0].equalsIgnoreCase("OKAY")){ if(sline[1].equalsIgnoreCase("Waiting.")){ Move move=GetMove(); if(move!=null){ //or in Lisp code, (if (numberp move)...) return Play(move); } else{ System.out.println(move); } } else{//it is "YOUR" int gameplayresult=Play(null); return gameplayresult; } } else { System.out.println("Unexpected data "+line); return -9; } return -9; } public int Play(Move m) throws IOException{ NewGame(); Move val; Move move=m; while(true){ if(move!=null && move.GetMove()>=0 && move.GetMove()< N*N*N){ BOARD[move.GetMove()]=-1; } //On the given input move, decide on a move to make, //and then actually make that move. val=Move(MakeMove(move)); if(val!=null){ //error checking for (not (numberp val)) String note=!val.IsNoteNull()?val.GetNote():""; if(!note.equals("okay")){ //(not (numberp val)) if(note.equals("win")) return 1; else if(note.equals("tie")) return 0; else return -1; } move=GetMove(); if(move.GetMove()==-9){ System.out.println("Invalid move data "+move); return -9; } if(move.GetMove()==-1){ return -1; } } else { } } } // The required user defined functions. abstract void NewGame(); // Here we have access to the internal representation of the board. // Return a Move. abstract Move MakeMove(Move move); } //Object for Moves. Work around for the static typing. class Move{ // For valid moves, 0 <= move < N*N*N private int move; // The variable note is to keep track of what this Move object // represents. // "" <==> a regular move. Otherwise, note can be // "okay", "win", "tie" or "lose". Then you know how to interpret // the value of this Move object. private String note; public Move(int m){ move=m; note=""; } public Move(int m, String n){ move=m; note=n; } public String GetNote(){ return note; } public boolean IsNoteNull(){ return note.equals(""); } public int GetMove(){ return move; } }