import java.net.*;
import java.io.*;
import java.util.*;


/**
  Kennzeichnet Fehler im Benutzerprotokoll
*/
class UserProtocolException extends Exception {
  /**
    Konstruiert einen Protokollfehler ohne n&auml;here Angaben.
  */
  public UserProtocolException() {
    super();
  }

  /**
    Konstruiert einen Protokollfehler mit weiteren Angaben.

    @param s n&auml;here Fehlerumst&auml;nde
  */
  public UserProtocolException(String s) {
    super(s);
  }
}


/**
  Erwartet eine TCP-Verbindungsanfrage (auf einem spezifizierbaren Port).
  Der Server bietet einen Zahlenratespieldienst an und obliegt
  folgendem Protokoll:
  <OL>
    <LI>Ausgabe der maximal zu erratenden Zahl (100).
    <LI>Warten auf Rateversuch (Timeout: 10s).
    <LI>Ausgabe, ob gr&ouml;&szlig;er (">"), kleiner ("<") oder
        gleich ("=") der zu erratenden Zahl.
    <LI>Weiter mit 2., falls nicht gleich.
  </OL>

  @see Client
*/
class Server {
  /**
    Defaultport f&uuml;r den angebotenen Dienst.
  */
  public final static int DEFAULT_PORT = 6789;
  protected ServerSocket listen_socket;
  protected Socket client_socket;
  protected BufferedReader client_in;
  protected PrintWriter client_out;

  /**
    Wartet auf Kommunikationsw&uuml;nsche, akzeptiert einen
    Kommunikationswunsch und st&ouml;&szlig;t den Dialog an.

    @param port Dienstportnummer
  */
  public Server(int port) {
    try {
      listen_socket = new ServerSocket(port);

      while (true) {
        Socket client_socket = listen_socket.accept();

        client_socket.setSoTimeout(10000);
        try {
          client_in = new BufferedReader(
                        new InputStreamReader(client_socket.getInputStream()));
          client_out = new PrintWriter(client_socket.getOutputStream(), true);

          dialog();
        } catch (IOException e) {
            System.err.println(e);
          } finally {
              try {
                if (client_in != null)
                  client_in.close();
              } catch (IOException e) {
                }
              if (client_out != null)
                client_out.close();
              try {
                client_socket.close();
              } catch (IOException e) {
                }
            }
      }
    } catch (IOException e) {
        System.err.println(e);
      } finally {
          try {
            if (listen_socket != null)
              listen_socket.close();
          } catch (IOException e) {
            }
        }
  }

  /**
    Ausgabe der Zahlenobergrenze auf die TCP-Verbindung.
  */
  void putmax() {
    client_out.println(100);
  }

  /**
    Warten auf einen Rateversuch.  Der Timeout nach 10 Sekunden wird
    &uuml;ber den Sockettimeout realisiert.  Alternativ w&auml;re
    eine Schleife der Form
    <UL>
      <LI>bestimme aktuelle Uhrzeit at
      <LI>setze Timeoutzeit to auf at + 10s
      <LI>pr&uuml;fe, ob der Eingabestrom bereit ist (ready()) oder
          der Timeout abgelaufen
    </UL>
    denkbar.  Ebenso ein &Uuml;berwachungsthread f&uuml;r jede Verbindung,
    der die Verbindung nach 10s unterbricht.

    @return Rateversuch
    @exception UserProtocolException Formatfehler beim Lesen der geratenen Zahl
    @exception java.io.IOException Verbindungsfehler
  */
  int gettrial() throws UserProtocolException, IOException {
    String line;
    int trial;

    line = client_in.readLine();
    if (line == null)
      throw new UserProtocolException("Client closed connection.");
    try {
      trial = Integer.parseInt(line);
    } catch (NumberFormatException e) {
        throw new UserProtocolException("Protocol error.");
      }
    return trial;
  }

  /**
    Ausgabe des Antwortstrings auf die TCP-Verbindung.

    @param a Antwortstring
  */
  void putanswer(String a) {
    client_out.println(a);
  }

  /**
    Protokollabwicklung

    @exception java.io.IOException Fehler in der Daten&uuml;bertragung
  */
  protected void dialog() throws IOException {
    int number;
    int trial;

    try {
      putmax();

      // Bestimme eine Zufallszahl zwischen 1 und 100
      // (0 <= Math.random() < 1, wie &uuml;blich)
      number = (int)(Math.random() * 100.0) + 1;

      do {
        trial = gettrial();

        if (number == trial)
          putanswer("=");
        else
          if (number < trial)
            putanswer("<");
          else
            putanswer(">");
      } while (number != trial);
    } catch (UserProtocolException e) {
        System.err.println(e);
      }
  }

  public static void usage() {
    System.out.println("Usage: java Server [<port>]");
    System.exit(1);
  }

  /**
    Das Hauptprogramm ermittelt den gew&uuml;nschten Dienstport
    aus der Kommandozeile (bei keiner Angabe wird DEFAULT_PORT
    verwendet) und st&ouml;&szlig;t den Server an.

    @param args Kommandozeilenargumente
    @see DEFAULT_PORT
    @see Server
  */
  public static void main(String[] args) {
    int port = DEFAULT_PORT;

    if (args.length > 1)
      usage();
    if (args.length == 1) {
      try {
        port = Integer.parseInt(args[0]);
      } catch (NumberFormatException e) {
          usage();
        }
    }
    new Server(port);
  }
}


/**
  Stellt eine Client-TCP-Verbindung zu einem Host-Rechner (auf einem
  spezifizierbaren Port) her.  Der Client erwartet einen
  Zahlenratespielserver und gehorcht folgendem Protokoll:
  <OL>
    <LI> Erwarten einer Zahl auf der TCP-Verbindung.
    <LI> Schreiben einer Zahl auf die TCP-Verbindung.
    <LI> Erwarten eines ">", "<", "=" auf der TCP-Verbindung.
    <LI> Weiter mit 2., falls nicht "=" kam.
  </OL>
*/
class Client {
  /**
    Defaultport, auf dem ein passendes Serverprotokoll laufen sollte.
  */
  public static final int DEFAULT_PORT = 6789;
  protected Socket socket;
  protected BufferedReader in;
  protected PrintWriter out;

  /**
    K&uuml;mmert sich um Verbindungsauf- und abbau.  St&ouml;&szlig;t
    die Kommunikation und den Dialog an.

    @param host Hostname
    @param port Portnummer
  */
  public Client(String host, int port) {
    try {
      socket = new Socket(host, port);

      try {
        in = new BufferedReader(
               new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(socket.getOutputStream(), true);

        dialog();
      } catch (IOException e) {
          System.err.println(e);
        } finally {
            try {
              if (in != null)
                in.close();
            } catch (IOException e) {
              }
            if (out != null)
              out.close();
            try {
              socket.close();
            } catch (IOException e) {
              }
          }
    } catch (IOException e) {
        System.err.println(e);
      } finally {
          try {
            if (socket != null)
              socket.close();
          } catch (IOException e) {
            }
        }
  }

  /**
    Holt die Zahlenobergrenze von der TCP-Verbindung.

    @return Zahlenobergrenze
    @exception UserProtocolException Formatfehler der Zahlenobergrenze
    @exception java.io.IOException Verbindungsfehler
  */
  int getmax() throws UserProtocolException, IOException {
    String line;

    line = in.readLine();
    if (line == null)
      throw new UserProtocolException("Server closed connection.");
    try {
      return (Integer.parseInt(line));
    } catch (NumberFormatException e) {
        throw new UserProtocolException("Protocol error.");
      }
  }

  /**
    Holt einen Rateversuch von der Standardeingabe.

    @param trialno Nummer des Rateversuchs
    @return Rateversuch
    @exception java.io.IOException Fehler beim Lesen von der Standardeingabe
  */
  int gettrial(int trialno) throws IOException {
    BufferedReader stdin = new BufferedReader(
                             new InputStreamReader(System.in));
    String line;
    int trial = 0;

    do {
      System.out.print("Dein " + trialno + ". Versuch? ");
      System.out.flush();
      line = stdin.readLine();
      try {
        trial = Integer.parseInt(line);
      } catch (NumberFormatException e) {
          trial = 0;
        }
    } while (trial == 0);
    return trial;
  }

  /**
    Ausgabe des Rateversuch auf die TCP-Verbindung.

    @param trial Rateversuch
  */
  void puttrial(int trial) {
    out.println(trial);
  }

  /**
    Holt die Serverantwort von der TCP-Verbindung.  &Uuml;bersetzt
    die Serverantwort ("=", "<", ">") in Standard-strcmp-Format
    (0, -1, 1).

    @return 0: gleich, -1: kleiner, +1: gr&ouml;&szlig;er
    @exception UserProtocolException andere Serverantwort als "=", "<", ">"
    @exception java.io.IOException Fehler beim Lesen von der TCP-Verbindung
  */
  int getanswer() throws UserProtocolException, IOException {
    String line;

    line = in.readLine();
    if (line == null)
      throw new UserProtocolException("Server closed connection.");
    if (line.startsWith("="))
      return 0;
    else
      if (line.startsWith("<"))
        return -1;
      else
        if (line.startsWith(">"))
          return 1;
        else
          throw new UserProtocolException("Protocol error.");
  }

  /**
    Protokollabwicklung, sowohl auf der TCP-Verbindung als auch
    mit dem Benutzer.
    <OL>
      <LI>Informieren des Benutzers &uuml;ber die Zahlenobergrenze.
      <LI>Abfrage eines Rateversuch (versehen mit einer Nummer).
      <LI>Informieren des Benutzers &uuml;ber die Serverantwort.
      <LI>Weiter mit 2., falls Zahl nicht erraten.
    </OL>

    @exception java.io.IOException TCP-Verbindungsst&ouml;rung
  */
  protected void dialog() throws IOException {
    String line;
    int max;
    int trial;

    try {
      max = getmax();

      System.out.println(
        "Ich habe mir eine Zahl zwischen 1 und " + max + " ausgedacht!");
      System.out.println("Kannst Du sie eraten?");

      for (int trialno = 1; trialno != 0; ) {
        trial = gettrial(trialno);

        puttrial(trial);

        switch (getanswer()) {
          case 0: {
            int maxtrialno = (int)Math.ceil(Math.log(max)/Math.log(2.0));

            System.out.println("Congratulations!");
            System.out.println("Du hast " + trialno + " Versuche ben\"otigt!");
            if (trialno > maxtrialno)
              System.out.println(
                "Es w\"are in " + maxtrialno + " zu schaffen gewesen");
            trialno = 0;
            break;
          }
          case -1: {
            System.out.println("Meine Zahl ist kleiner!");
            trialno++;
            break;
          }
          case 1: {
            System.out.println("Meine Zahl ist gr\"o\"ser!");
            trialno++;
            break;
          }
        }
      }
    } catch (UserProtocolException e) {
        System.err.println(e);
      }
  }

  public static void usage() {
    System.out.println("Usage: java Client <hostname> [<port>]");
    System.exit(1);
  }

  /**
    Das Hauptprogramm erschlie&szlig;t den Hostnamen und eventuell den Port
    aus den Kommandozeilenargumenten (wird kein Port angegeben,
    so wird DEFAULT_PORT verwendet).  St&ouml;&szlig;t den Client an.

    @param args Kommandozeilenargumente
    @see DEFAULT_PORT
    @see Client
  */
  public static void main(String[] args) {
    int port = DEFAULT_PORT;

    if ((args.length != 1) && (args.length != 2))
      usage();
    if (args.length != 1) {
      try {
        port = Integer.parseInt(args[1]);
      } catch (NumberFormatException e) {
          usage();
        }
    }

    new Client(args[0], port);
    System.exit(0);
  }
}


