fix(physics): disable playful biomechanics during aggressive ad skips
- Fixed a bug where `humanized_scroll(is_skip=True)` could trigger random 'Doomscroll Corrections' (scrolling backwards) or 'reading pauses'. - This caused the Anti-Stuck loop for ads to fail because the aggressive skip would occasionally just pause or scroll back into the ad. - Added strict TDD coverage in `test_physics_humanized.py` to verify `is_skip` generates strictly forward gestures without pauses.
This commit is contained in:
@@ -48,19 +48,12 @@ def humanized_scroll(device, is_skip=False, resonance_score=None):
|
||||
start_x, start_y = body.get_scroll_start()
|
||||
end_x = start_x + random.gauss(0, w * 0.008) # Slight horizontal drift
|
||||
|
||||
do_correction = random.random() < correction_prob
|
||||
|
||||
if is_skip:
|
||||
# Aggressive fast fling to skip quickly
|
||||
if do_correction:
|
||||
logger.debug(f"🪀 [Doomscroll] Correction (Prob: {correction_prob:.2f}) — Wait, what was that?")
|
||||
distance = int(h * random.uniform(0.3, 0.5))
|
||||
duration = random.uniform(100, 150)
|
||||
end_y = min(start_y + distance, h - 10) # Move down to pull UI up
|
||||
else:
|
||||
distance = int(h * random.uniform(0.6, 0.75))
|
||||
duration = random.uniform(100, 150)
|
||||
end_y = start_y - distance
|
||||
# Aggressive fast fling to skip quickly. NO CORRECTIONS.
|
||||
distance = int(h * random.uniform(0.6, 0.75))
|
||||
duration = random.uniform(150, 250) # slightly longer to ensure smooth fling registration
|
||||
end_y = start_y - distance
|
||||
do_correction = False # Force false
|
||||
else:
|
||||
# Playful, organic human scrolling
|
||||
play_choice = random.random()
|
||||
@@ -97,7 +90,7 @@ def humanized_scroll(device, is_skip=False, resonance_score=None):
|
||||
end_y = start_y - distance
|
||||
|
||||
# --- Behavioral Micro-Patterns (new human behaviors) ---
|
||||
behavior = _select_scroll_behavior()
|
||||
behavior = None if is_skip else _select_scroll_behavior()
|
||||
|
||||
if behavior == "pre_touch_dwell":
|
||||
# Finger lands on glass before swiping (50-200ms dwell)
|
||||
|
||||
@@ -46,3 +46,40 @@ def test_humanized_scroll_speeds(MockInjector):
|
||||
# Timing intervals must all be positive
|
||||
for t in timing:
|
||||
assert t > 0, f"Timing interval must be positive, got {t}"
|
||||
|
||||
|
||||
@patch("GramAddict.core.physics.humanized_input.SendEventInjector")
|
||||
def test_humanized_scroll_skip_is_strictly_forward(MockInjector):
|
||||
"""
|
||||
Ensures that when is_skip=True (e.g. for aggressive ad skipping),
|
||||
the generated gesture is purely forward (bottom to top swipe),
|
||||
and does NOT contain backwards 'Doomscroll corrections' or
|
||||
biomechanical 'reading pauses'.
|
||||
"""
|
||||
mock_injector = MagicMock()
|
||||
MockInjector.get_instance.return_value = mock_injector
|
||||
|
||||
device = MagicMock()
|
||||
device.get_info.return_value = {"displayHeight": 2400, "displayWidth": 1080}
|
||||
|
||||
from GramAddict.core.physics.humanized_input import humanized_scroll
|
||||
|
||||
# Run multiple times to overcome randomness in play_choice / do_correction
|
||||
for _ in range(50):
|
||||
mock_injector.reset_mock()
|
||||
humanized_scroll(device, is_skip=True)
|
||||
|
||||
args = mock_injector.inject_gesture.call_args
|
||||
points = args[0][0]
|
||||
timing = args[0][1]
|
||||
|
||||
# In a forward scroll (bottom to top), the Y coordinate must go from a higher number to a lower number.
|
||||
start_y = points[0][1]
|
||||
end_y = points[-1][1]
|
||||
|
||||
# End Y MUST be less than Start Y (meaning we scrolled down the feed, swiping finger up)
|
||||
assert end_y < start_y, f"Expected end_y ({end_y}) to be < start_y ({start_y}) for a skip."
|
||||
|
||||
# Verify no long pauses (reading pause adds 0.5s to 2.0s to timing)
|
||||
for t in timing:
|
||||
assert t < 500, "Found a massive pause in a skip gesture, meaning a reading_pause or dwell was incorrectly inserted!"
|
||||
|
||||
Reference in New Issue
Block a user