```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)
}
```