import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.function.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.imageio.*;
import java.math.*;
// has a rectangular window (visionW*visionH)
// walking range per frame is a circle with radius "range"
import static x30_pkg.x30_util.DynamicObject;
import java.awt.geom.*;
import java.text.NumberFormat;
class main {
abstract static class G22Walker {
// configuration (set)
int range = 1; // how far we can walk in each frame
double roundingPolicy = .9; // for making the range circle (between 0 and 1)
int visionW = 3, visionH = 3; // window rectangle size (both should be odd)
// current position (set/get)
Pt position; // current position (center), not null
// status variables (read)
boolean stuck = false; // can neither stay nor move anywhere for some reason
int guessWork; // how many "optimal" new positions we chose from
void move(Img image) {
stuck = false;
guessWork = 0;
// How far can I move until I hit the image's borders
Rect movement1 = rectFromPoints(visionW/2, visionH/2,
image.getWidth()-(visionW+1)/2, (visionH+1)/2);
// How far can I walk in one frame
Rect movement2 = rectAroundPt(position, range*2+1);
Rect movement = intersectRects(movement1, movement2);
if (empty(movement)) { stuck = true; return; }
printVars("movement", movement, "movement1", movement1, "movement2", movement2);
if (hasRememberedImage())
{ rememberImage(image); return; }
Rect r = window_uncropped();
MultiBest bestPositions = new MultiBest();
int mx1 = movement.x1(), mx2 = movement.x2();
int my1 = movement.y1(), my2 = movement.y2();
for (int y = my1; y < my2; y++)
for (int x = mx1; x < mx2; x++) {
var newPos = pt(x, y);
bestPositions.put(newPos, scorePosition(newPos, image));
}
var best = bestPositions.get();
guessWork = l(best);
position = random(best);
// It's actually a question how often we grab a fresh image.
// For now, every time.
rememberImage(image);
}
// only works when we already have a rememberedImage
abstract double scorePosition(Pt pos, Img image);
abstract void rememberImage(Img image);
abstract boolean hasRememberedImage();
int visionX1() { return position.x-visionW; }
int visionY1() { return position.y-visionH; }
Rect window_uncropped() { return window_uncropped(position); }
Rect window_uncropped(Pt center) {
return rectAroundPt(center, visionW, visionH);
}
}
static Rect rectFromPoints(int x1, int y1, int x2, int y2) {
return pointsRect(x1, y1, x2, y2);
}
static Rect rectFromPoints(Pt a, Pt b) {
return pointsRect(a.x, a.y, b.x, b.y);
}
static Rect rectAroundPt(Pt p, int w) { return rectAroundPt(p, w, w); }
static Rect rectAroundPt(Pt p, int w, int h) {
return rectAround(p, w, h);
}
static Rect rectAroundPt(int x, int y, int w, int h) {
return rectAround(x, y, w, h);
}
static Rect intersectRects(Rect a, Rect b) {
int x = max(a.x, b.x), y = max(a.y, b.y);
int x2 = min(a.x+a.w, b.x+b.w), y2 = min(a.y+a.h, b.y+b.h);
return new Rect(x, y, x2-x, y2-y);
}
static Rect intersectRects(Rect a, int x1, int y1, int w, int h) {
if (a == null
|| a.x >= x1 && a.y >= y1 && a.x2() < x1+w && a.y2() < y1+h)
return a;
return rectFromPoints(
max(a.x, x1), max(a.y, y1),
min(a.x2(), x1+w), min(a.y2(), y1+h));
}
static boolean empty(Collection c) { return c == null || c.isEmpty(); }
static boolean empty(Iterable c) { return c == null || !c.iterator().hasNext(); }
static boolean empty(CharSequence s) { return s == null || s.length() == 0; }
static boolean empty(Map map) { return map == null || map.isEmpty(); }
static boolean empty(Object[] o) { return o == null || o.length == 0; }
static boolean empty(BitSet bs) { return bs == null || bs.isEmpty(); }
static boolean empty(Object o) {
if (o instanceof Collection) return empty((Collection) o);
if (o instanceof String) return empty((String) o);
if (o instanceof Map) return empty((Map) o);
if (o instanceof Object[]) return empty((Object[]) o);
if (o instanceof byte[]) return empty((byte[]) o);
if (o == null) return true;
throw fail("unknown type for 'empty': " + getType(o));
}
static boolean empty(Iterator i) { return i == null || !i.hasNext(); }
static boolean empty(double[] a) { return a == null || a.length == 0; }
static boolean empty(float[] a) { return a == null || a.length == 0; }
static boolean empty(int[] a) { return a == null || a.length == 0; }
static boolean empty(long[] a) { return a == null || a.length == 0; }
static boolean empty(byte[] a) { return a == null || a.length == 0; }
static boolean empty(short[] a) { return a == null || a.length == 0; }
static boolean empty(IMultiMap mm) { return mm == null || mm.size() == 0; }
static boolean empty(File f) { return getFileSize(f) == 0; }
static boolean empty(Rect r) { return !(r != null && r.w != 0 && r.h != 0); }
// Use like this: printVars(+x, +y);
// Or: printVars("bla", +x);
// Or: printVars bla(, +x);
static void printVars(Object... params) {
printVars_str(params);
}
static Pt pt(int x, int y) {
return new Pt(x, y);
}
static Pt pt(int x) {
return new Pt(x, x);
}
static int l(Object[] a) { return a == null ? 0 : a.length; }
static int l(boolean[] a) { return a == null ? 0 : a.length; }
static int l(byte[] a) { return a == null ? 0 : a.length; }
static int l(short[] a) { return a == null ? 0 : a.length; }
static int l(long[] a) { return a == null ? 0 : a.length; }
static int l(int[] a) { return a == null ? 0 : a.length; }
static int l(float[] a) { return a == null ? 0 : a.length; }
static int l(double[] a) { return a == null ? 0 : a.length; }
static int l(char[] a) { return a == null ? 0 : a.length; }
static int l(Collection c) { return c == null ? 0 : c.size(); }
static int l(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible
static int l(Map m) { return m == null ? 0 : m.size(); }
static int l(CharSequence s) { return s == null ? 0 : s.length(); }
static long l(File f) { return f == null ? 0 : f.length(); }
static int l(IMultiMap mm) { return mm == null ? 0 : mm.size(); }
static int random(int n) { return random(n, defaultRandomGenerator()); }
static int random(int n, Random r) {
return random(r, n);
}
static int random(Random r, int n) {
return n <= 0 ? 0 : getRandomizer(r).nextInt(n);
}
static double random(double max) {
return random()*max;
}
static double random() {
return defaultRandomGenerator().nextInt(100001)/100000.0;
}
static double random(double min, double max) {
return min+random()*(max-min);
}
// min <= value < max
static int random(int min, int max) {
return min+random(max-min);
}
static int random(int min, int max, Random r) {
return random(r, min, max);
}
static int random(Random r, int min, int max) {
return min+random(r, max-min);
}
static A random(List l) {
return oneOf(l);
}
static A random(Collection c) {
if (c instanceof List) return random((List) c);
int i = random(l(c));
return collectionGet(c, i);
}
static Pair random(Map map) {
return entryToPair(random(entries(map)));
}
static Rect pointsRect(int x1, int y1, int x2, int y2) {
return new Rect(x1, y1, x2-x1, y2-y1);
}
static Rect rectAround(Pt p, int w) { return rectAround(p, w, w); }
static Rect rectAround(Pt p, int w, int h) {
return new Rect(p.x-w/2, p.y-h/2, w, h);
}
static Rect rectAround(int x, int y, int w, int h) {
return new Rect(x-w/2, y-h/2, w, h);
}
static int max(int a, int b) { return Math.max(a, b); }
static int max(int a, int b, int c) { return max(max(a, b), c); }
static long max(int a, long b) { return Math.max((long) a, b); }
static long max(long a, long b) { return Math.max(a, b); }
static double max(int a, double b) { return Math.max((double) a, b); }
static float max(float a, float b) { return Math.max(a, b); }
static double max(double a, double b) { return Math.max(a, b); }
static int max(Collection c) {
int x = Integer.MIN_VALUE;
for (int i : c) x = max(x, i);
return x;
}
static double max(double[] c) {
if (c.length == 0) return Double.MIN_VALUE;
double x = c[0];
for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]);
return x;
}
static float max(float[] c) {
if (c.length == 0) return Float.MAX_VALUE;
float x = c[0];
for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]);
return x;
}
static byte max(byte[] c) {
byte x = -128;
for (byte d : c) if (d > x) x = d;
return x;
}
static short max(short[] c) {
short x = -0x8000;
for (short d : c) if (d > x) x = d;
return x;
}
static int max(int[] c) {
int x = Integer.MIN_VALUE;
for (int d : c) if (d > x) x = d;
return x;
}
static > A max(A a, A b) {
return cmp(a, b) >= 0 ? a : b;
}
static int min(int a, int b) {
return Math.min(a, b);
}
static long min(long a, long b) {
return Math.min(a, b);
}
static float min(float a, float b) { return Math.min(a, b); }
static float min(float a, float b, float c) { return min(min(a, b), c); }
static double min(double a, double b) {
return Math.min(a, b);
}
static double min(double[] c) {
double x = Double.MAX_VALUE;
for (double d : c) x = Math.min(x, d);
return x;
}
static float min(float[] c) {
float x = Float.MAX_VALUE;
for (float d : c) x = Math.min(x, d);
return x;
}
static byte min(byte[] c) {
byte x = 127;
for (byte d : c) if (d < x) x = d;
return x;
}
static short min(short[] c) {
short x = 0x7FFF;
for (short d : c) if (d < x) x = d;
return x;
}
static int min(int[] c) {
int x = Integer.MAX_VALUE;
for (int d : c) if (d < x) x = d;
return x;
}
static RuntimeException fail() { throw new RuntimeException("fail"); }
static RuntimeException fail(Throwable e) { throw asRuntimeException(e); }
static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); }
static RuntimeException fail(Object... objects) { throw new Fail(objects); }
static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }
static String getType(Object o) {
return getClassName(o);
}
static long getFileSize(String path) {
return path == null ? 0 : new File(path).length();
}
static long getFileSize(File f) {
return f == null ? 0 : f.length();
}
// Use like this: printVars_str(+x, +y);
// Or: printVars("bla", +x);
// Or: printVars bla(+x);
static void printVars_str(Object... params) {
print(renderVars_str(params));
}
static int iteratorCount_int_close(Iterator i) { try {
int n = 0;
if (i != null) while (i.hasNext()) { i.next(); ++n; }
if (i instanceof AutoCloseable) ((AutoCloseable) i).close();
return n;
} catch (Exception __e) { throw rethrow(__e); } }
static Random defaultRandomGenerator() {
{ Random r = customRandomizerForThisThread(); if (r != null) return r; }
return ThreadLocalRandom.current();
}
static Random getRandomizer(Random r) {
return r != null ? r : defaultRandomGenerator();
}
static A oneOf(List l) {
if (empty(l)) return null;
int n = l.size();
return n == 1 ? first(l) : l.get(defaultRandomizer().nextInt(n));
}
static char oneOf(String s) {
return empty(s) ? '?' : s.charAt(random(l(s)));
}
static String oneOf(String... l) {
return oneOf(asList(l));
}
static A collectionGet(Collection c, int idx) {
if (c == null || idx < 0 || idx >= l(c)) return null;
if (c instanceof List) return listGet((List) c, idx);
Iterator it = c.iterator();
for (int i = 0; i < idx; i++) if (it.hasNext()) it.next(); else return null;
return it.hasNext() ? it.next() : null;
}
static Pair entryToPair(Map.Entry e) {
return mapEntryToPair(e);
}
static Set> entries(Map map) {
return _entrySet(map);
}
static int cmp(Number a, Number b) {
return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue());
}
static int cmp(double a, double b) {
return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(int a, int b) {
return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(long a, long b) {
return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(Object a, Object b) {
if (a == null) return b == null ? 0 : -1;
if (b == null) return 1;
return ((Comparable) a).compareTo(b);
}
static RuntimeException asRuntimeException(Throwable t) {
if (t instanceof Error)
_handleError((Error) t);
return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static String getClassName(Object o) {
return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName();
}
static volatile StringBuffer local_log = new StringBuffer(); // not redirected
static boolean printAlsoToSystemOut = true;
static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot
// in bytes - will cut to half that
static volatile int print_log_max = 1024*1024;
static volatile int local_log_max = 100*1024;
static boolean print_silent = false; // total mute if set
static Object print_byThread_lock = new Object();
static volatile ThreadLocal