import java.io.*; import java.util.*; public class maarten_linear_left_right_unoptimized { public static void main(String[] args) { Scanner sc = new Scanner(new BufferedReader(new InputStreamReader(System.in))); new maarten_linear_left_right_unoptimized().run(sc); sc.close(); } public void run(Scanner sc) { int w = sc.nextInt(), h = sc.nextInt(); double x1 = -1, y1 = -1, x2 = -1, y2 = -1; // First, do a binary search on the left and right edges of the screen. // Binary search returns an array {y} if horizon is found, or {y, y+1} if horizon is not found. double[] leftY = binarySearch(sc, 1, h); System.err.println(Arrays.toString(leftY)); double[] rightY = binarySearch(sc, w, h); System.err.println(Arrays.toString(rightY)); System.err.println("--------------"); // In case the horizon was found on one or two sides, we can already fix the point(s) on that(/those) side(s). if (leftY.length == 1) { x1 = 1; y1 = leftY[0]; } if (rightY.length == 1) { x2 = w; y2 = rightY[0]; } // The rest of my comments will assume that "up" means (slopeUp ? "up" : "down"), and vice versa for "down". double slope = (Arrays.stream(rightY).average().getAsDouble() - Arrays.stream(leftY).average().getAsDouble()) / (w - 1); boolean slopeUp = slope > 0; System.err.println(slope); // In case the horizon was not found on the left, keep walking right and "up" until a horizon point is found. if (y1 < 0) { double x = 1, y = leftY[slopeUp ? 1 : 0]; while (y1 < 0) { System.out.printf("? %d %d\n", (int) x, (int) y); String pixel = sc.next(); if (pixel.equals("horizon")) { x1 = x; y1 = y; } else { boolean isSky = pixel.equals("sky"); if (slope > 0) { if (isSky) x++; else y++; } else { if (isSky) y--; else x++; } } } } System.err.println("--------------"); // In case the horizon was not found on the right, keep walking left and "down" until a horizon point is found. if (y2 < 0) { double x = w, y = rightY[slopeUp ? 0 : 1]; // other way around, 'cause we're going to the left while (y2 < 0) { System.out.printf("? %d %d\n", (int) x, (int) y); String pixel = sc.next(); if (pixel.equals("horizon")) { x2 = x; y2 = y; } else { // inc/dec and sea/sky are other way around, 'cause we're going to the left boolean isSky = pixel.equals("sky"); if (slope > 0) { if (isSky) y--; else x--; } else { if (isSky) x--; else y++; } } } } System.out.printf("! %d %d %d %d\n", (int) x1, (int) y1, (int) x2, (int) y2); } public double[] binarySearch(Scanner sc, int x, int h) { int low = 1; int high = h; while (low + 1 < high) { int mid = (high + low) / 2; System.out.printf("? %d %d\n", x, mid); switch (sc.next()) { case "sky": high = mid; break; case "sea": low = mid; break; case "horizon": return new double[]{mid}; } } return new double[]{low, high}; } // double dot(double x1, double y1, double x2, double y2) { // return x1 * y2 - y1 * x2; // } // // boolean above(double xl, double yl, double xr, double yr, double xp, double yp) { // return dot(xl - xp, yl - yp, xr - xp, yr - yp) >= 0; // } // // boolean below(l, r, p) { // return dot(l[0] - p[0], l[1] - p[1], r[0] - p[0], r[1] - p[1]) <= 0; // } }