Functional Scope (In-Scope)
- Geo-Based Candidate Search: Filter profiles by distance using standard Haversine formulas.
- Real-time Mutual Match Checking: Detect matching likes instantly and notify both users dynamically.
- Elo Score Matchmakers: Balance recommendation pools by ranking candidates with similar Elo ratings.
- Daily Swipe Limits: Limit free-tier accounts to a maximum of 100 swipes per day.
Explicit Boundaries (Out-of-Scope)
Clean reference designs demonstrating matchmaking engines in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.*;
import java.util.stream.Collectors;
enum SwipeDirection { LIKE, PASS }
// Represents a user profile with coordinates and ELO rating
class UserProfile {
private final String id;
private final String name;
private final String gender;
private final double latitude;
private final double longitude;
private final int age;
private int eloScore;
public UserProfile(String id, String name, String gender, int age, double lat, double lon, int eloScore) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.latitude = lat;
this.longitude = lon;
this.eloScore = eloScore;
}
public String getId() { return id; }
public String getName() { return name; }
public String getGender() { return gender; }
public int getAge() { return age; }
public double getLatitude() { return latitude; }
public double getLongitude() { return longitude; }
public int getEloScore() { return eloScore; }
public void updateElo(int opponentElo, SwipeDirection direction) {
double expected = 1.0 / (1.0 + Math.pow(10, (opponentElo - this.eloScore) / 400.0));
double actual = (direction == SwipeDirection.LIKE) ? 1.0 : 0.0;
this.eloScore += (int) (32 * (actual - expected));
}
}
// Representing a match between User A and User B
class Match {
private final String id;
private final String userA;
private final String userB;
private final long timestamp;
public Match(String userA, String userB) {
// Sort IDs to ensure uniqueness regardless of swiper order
if (userA.compareTo(userB) < 0) {
this.userA = userA;
this.userB = userB;
} else {
this.userA = userB;
this.userB = userA;
}
this.id = this.userA + ":" + this.userB;
this.timestamp = System.currentTimeMillis();
}
public String getId() { return id; }
public String getUserA() { return userA; }
public String getUserB() { return userB; }
public long getTimestamp() { return timestamp; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Match)) return false;
Match match = (Match) o;
return id.equals(match.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}
// Observer Pattern: Match listener to dispatch notifications
interface MatchListener {
void onMatchCreated(Match match, UserProfile profileA, UserProfile profileB);
}
// Haversine formula calculation utility
class GeoUtils {
private static final double EARTH_RADIUS_KM = 6371.0;
public static double distance(double lat1, double lon1, double lat2, double lon2) {
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS_KM * c;
}
}
// Engine managing all profile lookups, swipes, and ELO matchmaking
class SwipeEngine {
private final Map<String, UserProfile> profiles = new ConcurrentHashMap<>();
// Maps: User -> Set of User IDs they have swiped on (avoid showing swiped profiles again)
private final Map<String, Set<String>> swipedProfiles = new ConcurrentHashMap<>();
// Maps: User -> Set of User IDs who swiped LIKE on them
private final Map<String, Set<String>> likesReceived = new ConcurrentHashMap<>();
// Daily swipe limits mapping (User -> DateString -> Count)
private final Map<String, Map<String, AtomicInteger>> dailySwipeCaps = new ConcurrentHashMap<>();
// Active unique matches
private final Map<String, Match> activeMatches = new ConcurrentHashMap<>();
// Match event listeners
private final List<MatchListener> listeners = new CopyOnWriteArrayList<>();
private final ReentrantLock matchLock = new ReentrantLock();
private static final int DAILY_FREE_SWIPE_LIMIT = 100;
public void registerProfile(UserProfile profile) {
profiles.put(profile.getId(), profile);
}
public void registerMatchListener(MatchListener listener) {
listeners.add(listener);
}
// Recommendation Engine: Return profiles sorted by proximity of ELO scores and Geo distances
public List<UserProfile> getCandidates(String userId, double maxDistanceKm) {
UserProfile user = profiles.get(userId);
if (user == null) return Collections.emptyList();
Set<String> alreadySwiped = swipedProfiles.computeIfAbsent(userId, k -> ConcurrentHashMap.newKeySet());
return profiles.values().stream()
.filter(p -> !p.getId().equals(userId))
.filter(p -> !alreadySwiped.contains(p.getId()))
.filter(p -> GeoUtils.distance(user.getLatitude(), user.getLongitude(), p.getLatitude(), p.getLongitude()) <= maxDistanceKm)
.sorted(Comparator.comparingInt((UserProfile p) -> Math.abs(p.getEloScore() - user.getEloScore()))
.thenComparingDouble(p -> GeoUtils.distance(user.getLatitude(), user.getLongitude(), p.getLatitude(), p.getLongitude())))
.collect(Collectors.toList());
}
// Main Swipe Workflow
public boolean recordSwipe(String swiperId, String swipeeId, SwipeDirection direction) {
UserProfile swiper = profiles.get(swiperId);
UserProfile swipee = profiles.get(swipeeId);
if (swiper == null || swipee == null) {
System.out.println("Invalid profiles: " + swiperId + " -> " + swipeeId);
return false;
}
// Daily limit check
String today = new java.text.SimpleDateFormat("yyyy-MM-dd").format(new Date());
Map<String, AtomicInteger> userCaps = dailySwipeCaps.computeIfAbsent(swiperId, k -> new ConcurrentHashMap<>());
AtomicInteger counter = userCaps.computeIfAbsent(today, k -> new AtomicInteger(0));
if (counter.get() >= DAILY_FREE_SWIPE_LIMIT) {
System.out.println("[REJECTED] Daily swipe limit reached for user: " + swiperId);
return false;
}
// Track swipe
counter.incrementAndGet();
swipedProfiles.computeIfAbsent(swiperId, k -> ConcurrentHashMap.newKeySet()).add(swipeeId);
if (direction == SwipeDirection.LIKE) {
// Register like
likesReceived.computeIfAbsent(swipeeId, k -> ConcurrentHashMap.newKeySet()).add(swiperId);
// Double ELO scoring update
swiper.updateElo(swipee.getEloScore(), SwipeDirection.LIKE);
// Thread-safe lock to prevent duplicate matches if users swipe at the exact same millisecond
matchLock.lock();
try {
Set<String> swipeeLikes = likesReceived.get(swiperId);
if (swipeeLikes != null && swipeeLikes.contains(swipeeId)) {
Match match = new Match(swiperId, swipeeId);
if (!activeMatches.containsKey(match.getId())) {
activeMatches.put(match.getId(), match);
// Notify listeners
for (MatchListener listener : listeners) {
listener.onMatchCreated(match, swiper, swipee);
}
}
}
} finally {
matchLock.unlock();
}
} else {
// Pass
swiper.updateElo(swipee.getEloScore(), SwipeDirection.PASS);
}
return true;
}
public Collection<Match> getMatches() {
return activeMatches.values();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
SwipeEngine engine = new SwipeEngine();
// Register default Match Listener
engine.registerMatchListener((match, profileA, profileB) -> {
System.out.println("[ALERT] Match Event Triggered!");
System.out.println(" Mutual Match! " + profileA.getName() + " (ELO: " + profileA.getEloScore() +
") <==> " + profileB.getName() + " (ELO: " + profileB.getEloScore() + ")");
});
// Set up test users (San Francisco bay area location coordinates)
UserProfile user1 = new UserProfile("u1", "Alice", "Female", 25, 37.7749, -122.4194, 1000); // SF Center
UserProfile user2 = new UserProfile("u2", "Bob", "Male", 26, 37.7849, -122.4094, 1020); // 1.4 km from SF
UserProfile user3 = new UserProfile("u3", "Charlie", "Male", 28, 37.3382, -121.8863, 1500); // San Jose (70km away)
UserProfile user4 = new UserProfile("u4", "Diana", "Female", 24, 37.7760, -122.4180, 990); // Close, close ELO
engine.registerProfile(user1);
engine.registerProfile(user2);
engine.registerProfile(user3);
engine.registerProfile(user4);
System.out.println("=== 1. GEOGRAPHIC PROXIMITY CANDIDATES FOR ALICE (MAX 5KM) ===");
List<UserProfile> candidates = engine.getCandidates("u1", 5.0);
for (UserProfile candidate : candidates) {
double distance = GeoUtils.distance(user1.getLatitude(), user1.getLongitude(), candidate.getLatitude(), candidate.getLongitude());
System.out.println(" - " + candidate.getName() + " | Distance: " + String.format("%.2f", distance) + " km | ELO: " + candidate.getEloScore());
}
System.out.println("\n=== 2. SWIPE AND REAL-TIME MATCH DETECTION ===");
// Alice likes Bob
engine.recordSwipe("u1", "u2", SwipeDirection.LIKE);
// Bob likes Alice
engine.recordSwipe("u2", "u1", SwipeDirection.LIKE);
System.out.println("\n=== 3. CONCURRENT SWIPE ATTACKS ===");
ExecutorService executor = Executors.newFixedThreadPool(2);
// Diana and Bob like each other simultaneously
executor.submit(() -> engine.recordSwipe("u4", "u2", SwipeDirection.LIKE));
executor.submit(() -> engine.recordSwipe("u2", "u4", SwipeDirection.LIKE));
executor.shutdown();
executor.awaitTermination(2, TimeUnit.SECONDS);
System.out.println("\n=== 4. TESTING SWIPE CAP LIMITS ===");
UserProfile userLimit = new UserProfile("uLimit", "LimitGuy", "Male", 29, 37.7749, -122.4194, 1000);
engine.registerProfile(userLimit);
for (int i = 0; i < 105; i++) {
boolean status = engine.recordSwipe("uLimit", "u1", SwipeDirection.PASS);
if (!status) {
System.out.println("Blocked on swipe attempt number: " + (i + 1));
break;
}
}
}
}