| Line 273... |
Line 273... |
| 273 |
return null;
|
273 |
return null;
|
| 274 |
}
|
274 |
}
|
| 275 |
}
|
275 |
}
|
| 276 |
|
276 |
|
| 277 |
/**
|
277 |
/**
|
| 278 |
* Slider pixels to nudge when working out how far the puzzle piece travels per
|
278 |
* Calibration nudge, in slider px, sized from the gap rather than fixed.
|
| 279 |
* slider pixel.
|
- |
|
| 280 |
*
|
279 |
*
|
| 281 |
* This was 15, which was the reason the captcha only solved about 9% of the time.
|
280 |
* A fixed 15px was far too small: it closed only ~25px, so the ratio came from a
|
| 282 |
* The piece moves roughly 1.7 px per slider px, so a 15 px nudge closed the gap by
|
- |
|
| 283 |
* only ~25 px -- and Hough circle detection is worth about +/-2-3 px per circle, so
|
281 |
* measurement ~20% noise and the aim landed ~30px out. A fixed 60px then proved too
|
| 284 |
* the ratio was being derived from a measurement with ~20% noise in it. That error
|
282 |
* large in the other direction -- production data showed 85% of attempts with a gap
|
| 285 |
* then multiplied across the whole remaining travel: on a 150 px gap it landed ~30 px
|
283 |
* under 60px ended up UNUSABLE (the piece shot past the hole and the measured gap
|
| 286 |
* out, and the target hole is only ~30 px across, so it missed entirely.
|
284 |
* grew), and 45% overall.
|
| 287 |
*
|
285 |
*
|
| - |
|
286 |
* Aim to close about half the gap: the piece travels roughly ASSUMED_RATIO px per
|
| 288 |
* A 60 px nudge closes ~100 px, so the same detection noise is ~5% instead of ~20%.
|
287 |
* slider px, so nudge ~= gap / (2 * ratio), clamped. The real ratio is still derived
|
| - |
|
288 |
* from the measurement; this only sizes the probe.
|
| 289 |
*/
|
289 |
*/
|
| - |
|
290 |
private static final double ASSUMED_RATIO = 1.7;
|
| 290 |
private static final int CALIBRATION_NUDGE = 60;
|
291 |
private static final int MIN_NUDGE = 10;
|
| 291 |
|
- |
|
| 292 |
/** Stop correcting once the piece is within this many px of the hole. */
|
292 |
private static final int MAX_NUDGE = 60;
|
| 293 |
private static final double ALIGNED_PX = 2.0;
|
293 |
private static final double ALIGNED_PX = 2.0;
|
| 294 |
|
294 |
|
| - |
|
295 |
private static int nudgeFor(double gap) {
|
| - |
|
296 |
int n = (int) Math.round(gap / (2 * ASSUMED_RATIO));
|
| - |
|
297 |
return Math.max(MIN_NUDGE, Math.min(MAX_NUDGE, n));
|
| - |
|
298 |
}
|
| - |
|
299 |
|
| 295 |
/**
|
300 |
/**
|
| 296 |
* @return {sliderPxToMove, sliderPxPerCirclePx}, or null when the circles could not
|
301 |
* @return {sliderPxToMove, sliderPxPerCirclePx}, or null when the circles could not
|
| 297 |
* be read. Returned rather than stored on a field: this is a singleton and
|
302 |
* be read. Returned rather than stored on a field: this is a singleton and
|
| 298 |
* the Oppo secondary and tertiary jobs run it concurrently.
|
303 |
* the Oppo secondary and tertiary jobs run it concurrently.
|
| 299 |
*/
|
304 |
*/
|
| 300 |
private double[] calibrate(WebDriver driver, Actions actionProvider, double distance) throws Exception {
|
305 |
private double[] calibrate(WebDriver driver, Actions actionProvider, double distance) throws Exception {
|
| - |
|
306 |
int nudge = nudgeFor(distance);
|
| 301 |
actionProvider.moveByOffset(CALIBRATION_NUDGE, 0).perform();
|
307 |
actionProvider.moveByOffset(nudge, 0).perform();
|
| 302 |
|
308 |
|
| 303 |
String fileName2 = "/tmp/" + Thread.currentThread().getName() + "-moved.png";
|
309 |
String fileName2 = "/tmp/" + Thread.currentThread().getName() + "-moved.png";
|
| 304 |
double[] movedCircles = getMatCircles2(driver, fileName2);
|
310 |
double[] movedCircles = getMatCircles2(driver, fileName2);
|
| 305 |
if (movedCircles == null) return null;
|
311 |
if (movedCircles == null) return null;
|
| 306 |
|
312 |
|
| 307 |
double movedDistance = Math.abs(movedCircles[0] - movedCircles[1]);
|
313 |
double movedDistance = Math.abs(movedCircles[0] - movedCircles[1]);
|
| 308 |
double closed = distance - movedDistance;
|
314 |
double closed = distance - movedDistance;
|
| 309 |
LOGGER.info("Calibration: gap {} -> {} after {}px, closed {}",
|
315 |
LOGGER.info("Calibration: gap {} -> {} after {}px, closed {}",
|
| 310 |
distance, movedDistance, CALIBRATION_NUDGE, closed);
|
316 |
distance, movedDistance, nudge, closed);
|
| 311 |
|
317 |
|
| 312 |
// A non-positive value means the piece did not move, or moved away from the
|
318 |
// A non-positive value means the piece did not move, or moved away from the
|
| 313 |
// hole -- either way the ratio is meaningless and guessing would waste the try.
|
319 |
// hole -- either way the ratio is meaningless and guessing would waste the try.
|
| 314 |
if (closed <= 0) return null;
|
320 |
if (closed <= 0) return null;
|
| 315 |
|
321 |
|
| 316 |
double sliderPerCircle = CALIBRATION_NUDGE / closed;
|
322 |
double sliderPerCircle = nudge / closed;
|
| 317 |
int moveBy = (int) Math.round(movedDistance * sliderPerCircle);
|
323 |
int moveBy = (int) Math.round(movedDistance * sliderPerCircle);
|
| 318 |
return new double[]{moveBy, sliderPerCircle};
|
324 |
return new double[]{moveBy, sliderPerCircle};
|
| 319 |
}
|
325 |
}
|
| 320 |
|
326 |
|
| 321 |
/**
|
327 |
/**
|