From 0f5573abc1008723d3919c78ca97b232363b2343 Mon Sep 17 00:00:00 2001 From: "Claw (AI Agent)" Date: Mon, 2 Mar 2026 09:24:46 +0800 Subject: [PATCH] feat: Enhanced bounty discovery algorithm with weighted scoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Features - Multi-factor scoring algorithm (0-100 scale): - Reward amount (30% weight) - Funding progress (20% weight) - bonus for 50-90% funded - Activity level (20% weight) - based on claim count - Recency (15% weight) - fresh bounties boosted - Difficulty balance (10% weight) - Medium preferred - Tag diversity (5% weight) - more tags = better ## UI Improvements - Rank badges (#1, #2, etc.) - Score breakdown in expandable details - Color-coded difficulty pills - Visual scoring factors legend - Better card layout with stats ## Write-up **Algorithm Design:** The scoring algorithm balances multiple factors to surface the most attractive bounties: 1. High rewards attract attention (30%) 2. Nearly-funded bounties create urgency (20%) 3. Popular bounties have proven value (20%) 4. Fresh opportunities get visibility (15%) 5. Medium difficulty is the sweet spot (10%) 6. Good tagging improves discoverability (5%) Closes #3 🤖 Generated by Claw (AI Agent) --- src/app/discovery/page.tsx | 201 ++++++++++++++++++++++++++++++++++--- 1 file changed, 189 insertions(+), 12 deletions(-) diff --git a/src/app/discovery/page.tsx b/src/app/discovery/page.tsx index 8954618..7ccb2a0 100644 --- a/src/app/discovery/page.tsx +++ b/src/app/discovery/page.tsx @@ -1,15 +1,110 @@ import { mockDiscovery } from "@/data/mock-discovery"; -function scoreBounty(bounty: typeof mockDiscovery[number]) { - const fundedBoost = bounty.fundedPercent >= 80 ? 20 : bounty.fundedPercent / 4; - const activityBoost = bounty.claimedCount * 5; - const recencyBoost = Math.max(0, 14 - bounty.postedDaysAgo); - return fundedBoost + activityBoost + recencyBoost + bounty.reward / 50; +/** + * Enhanced Bounty Discovery Algorithm + * + * Scoring factors (weights): + * 1. Reward amount (30%) - Higher rewards attract more attention + * 2. Funding progress (20%) - Nearly funded bounties are prioritized + * 3. Activity level (20%) - More claims = more popular + * 4. Recency (15%) - Fresh bounties get boost + * 5. Difficulty balance (10%) - Medium difficulty preferred + * 6. Tag diversity (5%) - More tags = better discoverability + * + * Total score: 0-100 (normalized) + */ + +function scoreBounty(bounty: typeof mockDiscovery[number]): number { + // Normalize each factor to 0-100 scale + + // 1. Reward score (30% weight) + // Assume max reward is $1000, normalize to 0-100 + const rewardScore = Math.min(100, (bounty.reward / 1000) * 100); + + // 2. Funding progress score (20% weight) + // Boost bounties that are 50-90% funded (sweet spot for completion) + let fundingScore = bounty.fundedPercent; + if (bounty.fundedPercent >= 50 && bounty.fundedPercent < 90) { + fundingScore += 20; // Bonus for "almost there" bounties + } else if (bounty.fundedPercent >= 90) { + fundingScore += 10; // Smaller bonus for nearly complete + } + fundingScore = Math.min(100, fundingScore); + + // 3. Activity score (20% weight) + // More claims = more interest, but diminishing returns + // Assume max 20 claims is "very active" + const activityScore = Math.min(100, (bounty.claimedCount / 20) * 100); + + // 4. Recency score (15% weight) + // Fresh bounties get higher score, decay over 30 days + let recencyScore = Math.max(0, 100 - (bounty.postedDaysAgo / 30) * 100); + // Boost brand new bounties (< 3 days) + if (bounty.postedDaysAgo < 3) { + recencyScore += 20; + } + recencyScore = Math.min(100, recencyScore); + + // 5. Difficulty score (10% weight) + // Medium difficulty is the "sweet spot" - not too easy, not too hard + // Map difficulty to score: Easy=60, Medium=100, Hard=75 + const difficultyMap: Record = { + "Easy": 60, + "Medium": 100, + "Hard": 75, + }; + const difficultyScore = difficultyMap[bounty.difficulty] || 50; + + // 6. Tag diversity score (5% weight) + // More tags = better discoverability, max 5 tags for full score + const tagScore = Math.min(100, (bounty.tags.length / 5) * 100); + + // Calculate weighted final score + const finalScore = + rewardScore * 0.30 + + fundingScore * 0.20 + + activityScore * 0.20 + + recencyScore * 0.15 + + difficultyScore * 0.10 + + tagScore * 0.05; + + return Math.round(finalScore * 10) / 10; // Round to 1 decimal +} + +// Score breakdown for display +function getScoreBreakdown(bounty: typeof mockDiscovery[number]) { + const rewardScore = Math.min(100, (bounty.reward / 1000) * 100); + let fundingScore = bounty.fundedPercent; + if (bounty.fundedPercent >= 50 && bounty.fundedPercent < 90) fundingScore += 20; + else if (bounty.fundedPercent >= 90) fundingScore += 10; + fundingScore = Math.min(100, fundingScore); + + const activityScore = Math.min(100, (bounty.claimedCount / 20) * 100); + let recencyScore = Math.max(0, 100 - (bounty.postedDaysAgo / 30) * 100); + if (bounty.postedDaysAgo < 3) recencyScore += 20; + recencyScore = Math.min(100, recencyScore); + + const difficultyMap: Record = { "Easy": 60, "Medium": 100, "Hard": 75 }; + const difficultyScore = difficultyMap[bounty.difficulty] || 50; + const tagScore = Math.min(100, (bounty.tags.length / 5) * 100); + + return { + reward: Math.round(rewardScore * 0.30 * 10) / 10, + funding: Math.round(fundingScore * 0.20 * 10) / 10, + activity: Math.round(activityScore * 0.20 * 10) / 10, + recency: Math.round(recencyScore * 0.15 * 10) / 10, + difficulty: Math.round(difficultyScore * 0.10 * 10) / 10, + tags: Math.round(tagScore * 0.05 * 10) / 10, + }; } export default function DiscoveryPage() { const ranked = [...mockDiscovery] - .map((bounty) => ({ ...bounty, score: scoreBounty(bounty) })) + .map((bounty) => ({ + ...bounty, + score: scoreBounty(bounty), + breakdown: getScoreBreakdown(bounty), + })) .sort((a, b) => b.score - a.score); return ( @@ -17,14 +112,50 @@ export default function DiscoveryPage() {

Discovery Algorithm

- Improve or replace the scoring function to rank bounties by relevance. + Enhanced scoring algorithm with weighted factors for better bounty discovery.

+ {/* Algorithm explanation */} +
+

📊 Scoring Factors

+
+
+ + Reward (30%) +
+
+ + Funding (20%) +
+
+ + Activity (20%) +
+
+ + Recency (15%) +
+
+ + Difficulty (10%) +
+
+ + Tags (5%) +
+
+
+
- {ranked.map((bounty) => ( -
-
+ {ranked.map((bounty, index) => ( +
+ {/* Rank badge */} +
+ #{index + 1} +
+ +

{bounty.title}

@@ -40,6 +171,7 @@ export default function DiscoveryPage() {
${bounty.reward}
+
Funded: {bounty.fundedPercent}% @@ -51,9 +183,54 @@ export default function DiscoveryPage() { Posted: {bounty.postedDaysAgo}d ago
-
- Score: {bounty.score.toFixed(1)} + + {/* Score display */} +
+
+ Score: {bounty.score} + /100 +
+ + {bounty.difficulty} +
+ + {/* Score breakdown (expandable) */} +
+ + View score breakdown + +
+
+
Reward
+
+{bounty.breakdown.reward}
+
+
+
Funding
+
+{bounty.breakdown.funding}
+
+
+
Activity
+
+{bounty.breakdown.activity}
+
+
+
Recency
+
+{bounty.breakdown.recency}
+
+
+
Difficulty
+
+{bounty.breakdown.difficulty}
+
+
+
Tags
+
+{bounty.breakdown.tags}
+
+
+
))}