← Home
```go
package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	ask := func(x, y int) string {
		fmt.Printf("%d %d\n", x, y)
		s, _ := reader.ReadString('\n')
		s = strings.TrimSpace(s)
		if strings.Contains(s, "!") {
			os.Exit(0)
		}
		return s
	}

	ask(0, 0)

	sA := ""
	sB := ""
	sSame := ""

	type step struct {
		p, q int
		resp string
	}

	solve1D := func(isX bool) int {
		var history []step
		mappingIsM1 := true

		P := 0
		Q := 1000000
		if !isX {
			P = 0
		}

		L := 0
		R := 1000000

		recompute := func(isM1 bool) (int, int) {
			cL, cR := 0, 1000000
			for _, st := range history {
				m := float64(st.p+st.q) / 2.0
				if st.resp == sSame && sSame != "" {
					return int(m), int(m)
				}
				isCloser := false
				if isM1 {
					isCloser = (st.resp == sA)
				} else {
					isCloser = (st.resp == sB || (sB == "" && st.resp != sA && st.resp != sSame))
				}

				if isCloser {
					if st.q > st.p {
						cL = int(math.Max(float64(cL), math.Floor(m)+1))
					} else {
						cR = int(math.Min(float64(cR), math.Ceil(m)-1))
					}
				} else {
					if st.q > st.p {
						cR = int(math.Min(float64(cR), math.Ceil(m)-1))
					} else {
						cL = int(math.Max(float64(cL), math.Floor(m)+1))
					}
				}
			}
			return cL, cR
		}

		for L < R {
			var resp string
			if len(history) == 0 {
				if isX {
					resp = ask(Q, 0)
				} else {
					resp = ask(0, Q) // Assuming X is already found, we could pass X, but 0 is fine
				}
				if sA == "" {
					sA = resp
				}
			} else {
				if isX {
					resp = ask(Q, 0)
				} else {
					resp = ask(0, Q)
				}
				if resp != sA && sB == "" {
					if strings.Contains(resp, "!") {
						os.Exit(0)
					}
					// If we haven't identified sB and sSame, we assume this is sB unless it leads to a contradiction
					// We'll just assign it to sB for now.
					sB = resp
				}
			}

			history = append(history, step{p: P, q: Q, resp: resp})
			P = Q

			L, R = recompute(mappingIsM1)

			if L > R {
				mappingIsM1 = !mappingIsM1
				L, R = recompute(mappingIsM1)
				Q = L
				if isX {
					ask(Q, 0)
				} else {
					ask(0, Q)
				}
				P = Q
				continue
			}

			if L == R {
				break
			}

			M := float64(L+R) / 2.0
			Q = int(math.Round(2*M)) - P

			if Q < 0 || Q > 1000000 {
				mappingIsM1 = !mappingIsM1
				L, R = recompute(mappingIsM1)
				Q = L
				if isX {
					ask(Q, 0)
				} else {
					ask(0, Q)
				}
				P = Q
			}
		}
		return L
	}

	x := solve1D(true)
	
	ask(x, 0)
	
	solveY := func() int {
		var history []step
		mappingIsM1 := true

		P := 0
		Q := 1000000
		L := 0
		R := 1000000

		recompute := func(isM1 bool) (int, int) {
			cL, cR := 0, 1000000
			for _, st := range history {
				m := float64(st.p+st.q) / 2.0
				isCloser := false
				if isM1 {
					isCloser = (st.resp == sA)
				} else {
					isCloser = (st.resp == sB)
				}
				if isCloser {
					if st.q > st.p {
						cL = int(math.Max(float64(cL), math.Floor(m)+1))
					} else {
						cR = int(math.Min(float64(cR), math.Ceil(m)-1))
					}
				} else {
					if st.q > st.p {
						cR = int(math.Min(float64(cR), math.Ceil(m)-1))
					} else {
						cL = int(math.Max(float64(cL), math.Floor(m)+1))
					}
				}
			}
			return cL, cR
		}

		for L < R {
			resp := ask(x, Q)
			if resp != sA && resp != sB {
				sSame = resp
				return (P + Q) / 2
			}

			history = append(history, step{p: P, q: Q, resp: resp})
			P = Q

			L, R = recompute(mappingIsM1)

			if L > R {
				mappingIsM1 = !mappingIsM1
				L, R = recompute(mappingIsM1)
				Q = L
				ask(x, Q)
				P = Q
				continue
			}

			if L == R {
				break
			}

			M := float64(L+R) / 2.0
			Q = int(math.Round(2*M)) - P

			if Q < 0 || Q > 1000000 {
				mappingIsM1 = !mappingIsM1
				L, R = recompute(mappingIsM1)
				Q = L
				ask(x, Q)
				P = Q
			}
		}
		return L
	}

	y := solveY()
	ask(x, y)
}
```