Functional Specifications
- Dynamic Geofenced Pricing: Adjusts fares dynamically per coordinate zone depending on realtime supply/demand dynamics.
- Gradual Price Smoothness (Ramp-Down): Multipliers decrement smoothly over multiple update cycles rather than sharp sudden drops.
- Driver Attraction Feedbacks: Higher multiplier incentives trigger system events that relocate nearby supply into high-value cells.
Out of Scope
- Complex Route Math: Complex traffic routing is delegated to external maps microservices.
- Customer Credit Check: Driver payout or card pre-auth states are decoupled.
Production reference implementations demonstrating ratio curves, cached indexes, and ramp down smoothing checks:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
class GeoZone {
private final String zoneId;
private final String zoneName;
private final String h3Index;
public GeoZone(String zoneId, String zoneName, String h3Index) {
this.zoneId = zoneId;
this.zoneName = zoneName;
this.h3Index = h3Index;
}
public String getZoneId() { return zoneId; }
public String getZoneName() { return zoneName; }
public String getH3Index() { return h3Index; }
}
class SupplyDemandSnapshot {
private final int activeDrivers;
private final int pendingRequests;
private final long timestamp;
public SupplyDemandSnapshot(int activeDrivers, int pendingRequests) {
this.activeDrivers = activeDrivers;
this.pendingRequests = pendingRequests;
this.timestamp = System.currentTimeMillis();
}
public int getActiveDrivers() { return activeDrivers; }
public int getPendingRequests() { return pendingRequests; }
public long getTimestamp() { return timestamp; }
public double getRatio() {
if (activeDrivers == 0) {
return pendingRequests > 0 ? 5.0 : 1.0; // Avoid division by zero
}
return (double) pendingRequests / activeDrivers;
}
}
class SurgeRuleConfig {
private final double baseMultiplier = 1.0;
private final double maxMultiplier = 4.5;
private final double surgeThreshold = 1.2; // Ratio at which surge starts
private final double multiplierCoeff = 0.5; // Multiplier step size
private final double maxRampDownStep = 0.2; // Max decrease in multiplier per interval
public double getBaseMultiplier() { return baseMultiplier; }
public double getMaxMultiplier() { return maxMultiplier; }
public double getSurgeThreshold() { return surgeThreshold; }
public double getMultiplierCoeff() { return multiplierCoeff; }
public double getMaxRampDownStep() { return maxRampDownStep; }
}
class SurgeCalculator {
private final SurgeRuleConfig config;
public SurgeCalculator(SurgeRuleConfig config) {
this.config = config;
}
public double calculateMultiplier(SupplyDemandSnapshot snapshot, double previousMultiplier) {
double ratio = snapshot.getRatio();
double targetMultiplier = config.getBaseMultiplier();
if (ratio > config.getSurgeThreshold()) {
double excess = ratio - config.getSurgeThreshold();
targetMultiplier = config.getBaseMultiplier() + (excess * config.getMultiplierCoeff());
targetMultiplier = Math.min(targetMultiplier, config.getMaxMultiplier());
}
// Apply gradual ramp-down policy to avoid sharp price drops
if (targetMultiplier < previousMultiplier) {
double maxDrop = config.getMaxRampDownStep();
if (previousMultiplier - targetMultiplier > maxDrop) {
targetMultiplier = previousMultiplier - maxDrop;
}
}
return Math.max(1.0, targetMultiplier);
}
}
class SurgeCache {
private final Map<String, Double> zoneMultipliers = new ConcurrentHashMap<>();
private final Map<String, SupplyDemandSnapshot> snapshots = new ConcurrentHashMap<>();
private final SurgeCalculator calculator;
public SurgeCache(SurgeCalculator calculator) {
this.calculator = calculator;
}
public void updateSnapshot(String zoneId, int activeDrivers, int pendingRequests) {
SupplyDemandSnapshot newSnapshot = new SupplyDemandSnapshot(activeDrivers, pendingRequests);
snapshots.put(zoneId, newSnapshot);
// Dynamic re-calculation
double prev = zoneMultipliers.getOrDefault(zoneId, 1.0);
double next = calculator.calculateMultiplier(newSnapshot, prev);
zoneMultipliers.put(zoneId, next);
}
public double getMultiplier(String zoneId) {
return zoneMultipliers.getOrDefault(zoneId, 1.0);
}
public SupplyDemandSnapshot getSnapshot(String zoneId) {
return snapshots.get(zoneId);
}
}
class PriceEstimator {
private final SurgeCache surgeCache;
public PriceEstimator(SurgeCache surgeCache) {
this.surgeCache = surgeCache;
}
public double estimateFare(String zoneId, double baseFare) {
double multiplier = surgeCache.getMultiplier(zoneId);
return baseFare * multiplier;
}
}
public class Main {
public static void main(String[] args) {
System.out.println("=== JAVA SURGE PRICING SIMULATION ===");
SurgeRuleConfig config = new SurgeRuleConfig();
SurgeCalculator calculator = new SurgeCalculator(config);
SurgeCache cache = new SurgeCache(calculator);
PriceEstimator estimator = new PriceEstimator(cache);
String zoneId = "ZONE_1";
System.out.println("Initial state: 10 drivers, 5 requests (low demand)...");
cache.updateSnapshot(zoneId, 10, 5);
System.out.println("Zone 1 Multiplier: " + cache.getMultiplier(zoneId) + "x");
System.out.println("Fare for $15 base: $" + estimator.estimateFare(zoneId, 15.0));
System.out.println("\nSpike demand: 2 drivers, 20 requests (severe mismatch)...");
cache.updateSnapshot(zoneId, 2, 20);
System.out.println("Zone 1 Multiplier: " + cache.getMultiplier(zoneId) + "x");
System.out.println("Fare for $15 base: $" + estimator.estimateFare(zoneId, 15.0));
System.out.println("\nDemand resolves instantly: 20 drivers, 0 requests...");
cache.updateSnapshot(zoneId, 20, 0);
System.out.println("Zone 1 Multiplier: " + cache.getMultiplier(zoneId) + "x (smooth gradual ramp-down)");
System.out.println("Fare for $15 base: $" + estimator.estimateFare(zoneId, 15.0));
System.out.println("=== END OF JAVA SIMULATION ===");
}
}