As a developer, I've learned that the strangest requests often lead to the most interesting technical challenges. Last month, a friend asked me a simple question: "How many combinations are there if I pick 7 red balls and 2 blue balls in the lottery?"
Twenty minutes later, I was deep in a rabbit hole of combinatorial formulas, probability theory, and the surprisingly complex world of lottery betting systems. And because apparently I enjoy reinventing wheels, I decided to build a tool to solve this problem properly.
The Problem That Started It All
The original question was straightforward: calculate how many bets you'd need to place to cover all combinations of a "复式" (compound) lottery bet. In Chinese lottery systems like Double Color Ball (双色球), you can select more numbers than the base requirement, and the system generates all possible combinations.
The math is simple combinatorics:
- For a compound bet selecting 7 red balls from 33, you need C(7,6) = 7 combinations
- Multiply by the number of blue balls you've selected
- Each combination costs ¥2
But here's where it gets interesting: there's a second betting system called "胆拖" (dan-tuo), where you designate certain numbers as "bold" (胆) that must appear in every combination, and others as "drag" (拖) that fill in the remaining slots.
This isn't just simple C(n,k) — you need to account for the bold numbers taking up slots. The formula becomes C(dragCount, 6 - boldCount) × blueCount.
Why Not Just Use Excel?
My first instinct was to throw together a quick spreadsheet. But then I thought about the users: lottery enthusiasts who might not be comfortable with Excel formulas, and who need quick answers on their phones while standing in line at the lottery shop.
I wanted something that works anywhere, requires no installation, and gives instant feedback. A browser-based tool was the obvious choice. Pure HTML, CSS, and JavaScript — no build step, no dependencies, no server.
The Math That Almost Broke Me
Here's where the "fun" began. The naive approach to calculating combinations is:
function combination(n, k) {
return factorial(n) / (factorial(k) * factorial(n - k));
}
This works fine for small numbers, but C(33,6) involves factorials that quickly exceed JavaScript's safe integer limit. I know, I know — "BigInt exists," but I wanted to keep things simple and fast.
The solution? Use the multiplicative formula with integer division at each step to avoid overflow:
function combination(n, k) {
if (k > n) return 0;
k = Math.min(k, n - k);
let result = 1;
for (let i = 1; i <= k; i++) {
result = result * (n - k + i) / i;
}
return result;
}
This works because each intermediate result is always an integer. The key insight is that you're multiplying and dividing in a specific order that keeps the numbers manageable. It's a classic combinatorial computing trick that I'd read about but never actually needed until now.
The AI Collaboration: Where It Helped and Where It Failed
This project was my first serious attempt at AI-assisted development, and it was... educational.
What AI Got Right
I described the requirements to Claude in a single, detailed prompt: the four calculation modes, the input validation rules, the output format, and the technical constraints. The AI generated a complete, working HTML file with all the JavaScript logic in one go. The core math was correct, the UI was clean, and it even included dark mode support and i18n scaffolding.
Where It Stumbled
The first version had a critical bug in the 胆拖 (dan-tuo) mode. The AI initially calculated the combinations as C(totalSelected, 6), not accounting for the fact that bold numbers are guaranteed to appear. The formula needed to be C(dragCount, 6 - boldCount), which is a subtle but crucial difference.
I caught this by manually testing edge cases:
// Test: 4 bold numbers, 3 drag numbers
// Should be C(3, 6-4) = C(3, 2) = 3 combinations
// AI's first attempt: C(7, 6) = 7 combinations (wrong!)
The AI's second attempt was correct, but it took a specific, detailed prompt to get there: "The bold numbers are always included, so the drag numbers only need to fill 6 - boldCount slots."
The Validation Logic
Another issue was input validation. The AI generated reasonable checks but missed some edge cases:
- Bold count can't be 0 (that's just a regular compound bet)
- Bold + drag must be at least 7
- Drag count must be at least 1
I had to add these constraints manually. It's not that the AI couldn't handle it — it just didn't think about all the edge cases without being prompted.
The Architecture Decisions
Pure Frontend, No Build Step
I deliberately avoided any framework or build tool. The entire tool is a single HTML file with embedded CSS and JavaScript. This means:
- Zero dependencies: No npm install, no CDN links, nothing that could break
- Instant loading: The file is tiny and loads immediately
- Works offline: Once loaded, it doesn't need a network connection
- Easy to maintain: One file, one place to look for everything
The trade-off is that I can't use modern JavaScript features like modules or JSX, but for a tool this size, it's not a limitation — it's a feature.
The Mode Switching Logic
The UI needs to show different input fields depending on the selected mode. I initially considered having separate forms for each mode, but that would mean duplicating a lot of code. Instead, I used a dynamic input system:
const CONFIGS = {
'ssq-fu': { fields: ['redCount', 'blueCount'], label: '双色球复式' },
'ssq-dt': { fields: ['boldCount', 'dragCount', 'blueCount'], label: '双色球胆拖' },
'd3-g3': { fields: ['numCount'], label: '3D组三复式' },
'd3-g6': { fields: ['numCount'], label: '3D组六复式' }
};
When the user selects a mode, the script dynamically renders the appropriate input fields. This keeps the code DRY and makes it trivial to add new modes in the future.
The 3D Lottery Modes
The 3D/排列三 lottery has two compound bet modes that trip people up:
- 组三 (Group 3): Two numbers are the same, one is different. Formula: k × (k-1)
- 组六 (Group 6): All three numbers are different. Formula: C(k, 3)
These formulas aren't obvious to most people, so the tool serves a real educational purpose here. I've had users message me saying "I never understood why 组三 gives more combinations than 组六 for the same numbers" — and the tool makes it concrete.
Performance Considerations
For this tool, performance is a non-issue. The heaviest calculation is C(33,6) ≈ 1.1 million, which JavaScript handles in microseconds. But I still made deliberate choices:
- No floating point arithmetic: All calculations use integers to avoid precision issues
- No DOM manipulation during calculation: The result is built once and inserted, not updated incrementally
- Debounced input handling: The calculate button is explicit, not automatic on every keystroke, to avoid unnecessary work
What I Learned
1. Edge Cases Are the Real Work
The math formulas are simple. The real work was thinking through all the boundary conditions:
- What happens when bold count equals 5? (You need exactly 1 drag number)
- What happens when bold count equals 0? (It's a compound bet, not dan-tuo)
- What's the maximum reasonable input? (33 red balls, 16 blue balls for Double Color Ball)
Most of these came from manual testing, not from the AI or from the spec.
2. AI Is a Great Junior Developer
Using AI for this project felt like working with a competent junior developer who's fast but needs supervision. It:
- Writes clean, well-structured code quickly
- Makes subtle logic errors that are easy to miss
- Needs specific, detailed instructions to handle edge cases
- Excels at boilerplate and repetitive patterns
The key is knowing when to let it run and when to step in. For the core math logic, I reviewed every line. For the CSS and HTML structure, I let it do its thing.
3. Sometimes the Simple Solution Is the Best
I could have made this a React app with TypeScript, testing, and CI/CD. But the simplest solution — a single HTML file — is actually the most robust. It can't have dependency issues, it can't fail to build, and it works on any device with a browser.
During this process, I built a small browser-based tool to make this workflow easier. You can find it at craftvo.app if you're curious about the result.
The Takeaway
Building this tool taught me that:
- Combinatorics in JavaScript requires careful thought — the naive factorial approach fails for real-world inputs
- AI assistance is most effective when you understand the problem domain — I caught the dan-tuo bug because I understood the math, not because the AI told me
- Small tools deserve the same engineering rigor — just because it's a single file doesn't mean it shouldn't be well-architected
And yes, my friend got his answer: 7 red balls and 2 blue balls gives you 14 combinations, costing ¥28. The tool is there if he ever needs to calculate something more complex.
Meta Description: Building a lottery bet calculator taught me about combinatorial math, JavaScript integer precision, and the realities of AI-assisted development. Here's what I learned about working with AI on real engineering problems.
Tags: javascript, webdev, ai, productivity, mathematics
Top comments (0)