Last Updated: 3/10/2026
Development Guide
This guide will help you contribute to the TFT Rolling Odds Calculator, modify it for new TFT sets, or adapt it for your own purposes.
Getting Started
Prerequisites
- A modern web browser (Chrome, Firefox, Safari, or Edge)
- A text editor (VS Code, Sublime Text, Atom, etc.)
- Basic knowledge of HTML, CSS, and JavaScript
- Understanding of TFT game mechanics
- (Optional) Git for version control
Local Development Setup
- Clone the repository
git clone https://github.com/enteigss/TFT-Rolling-Odds-Calculator.git
cd TFT-Rolling-Odds-Calculator- Open in browser
Simply open index.html in your web browser. No build process required!
# On macOS
open index.html
# On Linux
xdg-open index.html
# On Windows
start index.html- Make changes
Edit the files in your text editor and refresh the browser to see changes.
Project Structure
TFT-Rolling-Odds-Calculator/
├── index.html # HTML structure and UI
├── style.css # Styling and layout
├── script.js # All JavaScript logic
├── markov.png # Diagram for documentation
└── README.md # Project documentationUpdating for New TFT Sets
When a new TFT set is released, you’ll need to update the game data constants.
Step 1: Update Pool Sizes
Find the totalUnits constant in script.js:
const totalUnits = [22, 20, 17, 10, 9];Update with the new set’s pool sizes for costs 1-5.
Example for Set 11:
// If Set 11 has different pool sizes
const totalUnits = [29, 22, 18, 12, 10];Step 2: Update Champion Counts
Find the distinctChamps constant:
const distinctChamps = [13, 13, 13, 13, 8];Update with the number of unique champions at each cost.
How to count:
- Go to the TFT wiki or patch notes
- Count unique champions at each cost tier
- Update the array
Step 3: Update Shop Odds
Find the costProbs array:
const costProbs = [
[1, 0, 0, 0, 0], // Level 1
[1, 0, 0, 0, 0], // Level 2
[0.75, 0.25, 0, 0, 0], // Level 3
// ... continues for all 11 levels
];Each row represents a level (1-11) Each column represents a cost tier (1-5)
Example: At level 7, the odds are:
- 19% for 1-cost
- 35% for 2-cost
- 35% for 3-cost
- 10% for 4-cost
- 1% for 5-cost
[0.19, 0.35, 0.35, 0.10, 0.01], // Level 7Where to find shop odds:
- Official Riot patch notes
- TFT wiki
- Community resources like lolchess.gg
Step 4: Update Headliner Probabilities
If the set uses headliners (or similar mechanic), update:
const headlinerProbs = [
[0.077, 0, 0, 0, 0], // Level 2
[0.077, 0, 0, 0, 0], // Level 3
// ... continues
];If the set doesn’t have headliners:
Set all values to 0:
const headlinerProbs = [
[0, 0, 0, 0, 0], // Level 2
[0, 0, 0, 0, 0], // Level 3
// ... all zeros
];Step 5: Update the UI
Update the changelog in index.html:
<section id="changelog">
<ul>
<li><strong>[Update as of DD/MM/YY]</strong>:
Set 11 bag sizes and shop odds. XX/XX/XX/XX/XX for each cost,
XX/XX/XX/XX/XX distinct champions for each cost
</li>
</ul>
</section>Step 6: Test
Verify your changes with known scenarios:
Test 1: Single shop probability
Level 8, 4-cost unit, full pool, 2 gold
Manual calculation: 0.18 × (12/144) = 0.015 = 1.5%
Calculator should show ~1.5% for "at least 1"Test 2: Edge cases
- All copies taken (should show 0%)
- Impossible cost at low level (should show 0%)
- Full pool at high level (should show high %)
Code Modification Guide
Adding Input Validation
Current code lacks validation. Here’s how to add it:
function validateInputs() {
const cost = parseInt(document.getElementById("costRange").value);
const lvl = parseInt(document.getElementById("lvlRange").value);
const copies = parseInt(document.getElementById("copiesText").value);
const pool = parseInt(document.getElementById("poolText").value);
const gold = parseInt(document.getElementById("goldText").value);
// Validate ranges
if (copies < 0 || copies > totalUnits[cost - 1]) {
alert("Copies out must be between 0 and " + totalUnits[cost - 1]);
return false;
}
if (pool < 0) {
alert("Pool size cannot be negative");
return false;
}
if (gold < 0 || gold % 2 !== 0) {
alert("Gold must be a positive even number");
return false;
}
return true;
}
// Call before updating graph
function updateGraph() {
if (!validateInputs()) return;
// ... rest of function
}Changing the Matrix Size
Currently tracks up to 9 copies. To track more:
- Change matrix size in all functions:
// Change from 10 to desired size (e.g., 20)
const MATRIX_SIZE = 20;
function getTransitionMatrix(cost, lvl, a, b){
const mat = [];
for (let i = 0; i < MATRIX_SIZE; i++) {
// ... update loop
}
}- Update chart labels:
const labels = Array.from({length: MATRIX_SIZE - 1}, (_, i) => String(i + 1));- Update headliner transition matrix size
Adding Exact Probability Display
Currently shows only cumulative. To add exact probabilities:
function updateGraph() {
const probs = getProbs(
parseInt(cost.value),
parseInt(lvl.value),
parseInt(copies.value),
parseInt(pool.value),
parseInt(gold.value)
);
chart.data.datasets = [
{
label: 'Probability of getting at least x units',
data: probs[1].slice(1),
backgroundColor: 'rgba(54, 162, 235, 0.5)'
},
{
label: 'Probability of getting exactly x units',
data: probs[0].slice(1),
backgroundColor: 'rgba(255, 99, 132, 0.5)'
}
];
chart.update();
}Optimizing Matrix Power Calculation
Current implementation is O(n). Use exponentiation by squaring for O(log n):
function powerOptimized(matrix, n) {
if (n === 1) return matrix;
if (n === 0) return identityMatrix(matrix.length);
if (n % 2 === 0) {
const half = powerOptimized(matrix, n / 2);
return multiply(half, half);
} else {
return multiply(matrix, powerOptimized(matrix, n - 1));
}
}
function identityMatrix(size) {
const mat = [];
for (let i = 0; i < size; i++) {
mat[i] = [];
for (let j = 0; j < size; j++) {
mat[i][j] = (i === j) ? 1 : 0;
}
}
return mat;
}Styling Customization
Changing Color Scheme
Edit style.css:
/* Change background color */
body {
background-color: #1a1a2e; /* Dark blue */
}
/* Change slider color */
.slider::-webkit-slider-thumb {
background: #ff6b6b; /* Red accent */
}Responsive Design
Add media queries for mobile:
@media (max-width: 768px) {
.slidecontainer {
width: 95%;
}
h1 {
font-size: 1.5em;
}
canvas {
height: 40vh !important;
}
}Testing
Manual Testing Checklist
- All sliders move smoothly
- Text inputs accept numbers
- Chart updates on every input change
- Probabilities sum to reasonable values
- Edge cases handled (0 gold, full pool, etc.)
- Works on mobile browsers
- Works offline after initial load
Automated Testing Setup
To add unit tests (requires Node.js):
- Install Jest:
npm init -y
npm install --save-dev jest- Create test file (
script.test.js):
// Import functions (need to export them first)
const { getTransitionProb, multiply } = require('./script');
test('transition probability at level 1, 1-cost, full pool', () => {
const prob = getTransitionProb(1, 1, 0, 0);
expect(prob).toBeCloseTo(1.0 / 13, 3);
});
test('matrix multiplication identity', () => {
const A = [[1, 2], [3, 4]];
const I = [[1, 0], [0, 1]];
const result = multiply(A, I);
expect(result).toEqual(A);
});- Run tests:
npm testContributing
Workflow
- Fork the repository on GitHub
- Clone your fork locally
- Create a feature branch
git checkout -b feature/your-feature-name - Make your changes
- Test thoroughly
- Commit with clear messages
git commit -m "Add: Feature description" - Push to your fork
git push origin feature/your-feature-name - Create a Pull Request on GitHub
Commit Message Guidelines
Add: New feature
Fix: Bug fix
Update: Data update (e.g., new set)
Refactor: Code restructuring
Docs: Documentation changes
Style: Formatting, no code changeCode Style
- Use 2-space indentation
- Use
constfor constants,letfor variables - Use descriptive function names
- Add comments for complex logic
- Keep functions small and focused
Common Issues
Issue: Chart not displaying
Cause: Chart.js CDN not loaded
Solution: Check internet connection or use local Chart.js:
<script src="./chart.min.js"></script>Issue: Incorrect probabilities
Cause: Outdated game data
Solution: Verify totalUnits, distinctChamps, and costProbs match current patch
Issue: Slow performance with high gold
Cause: Many matrix multiplications
Solution: Implement optimized matrix power (see above)
Issue: Mobile display issues
Cause: Fixed dimensions
Solution: Add responsive CSS (see Styling section)
Deployment
GitHub Pages
- Push changes to
mainbranch - Go to repository Settings → Pages
- Select source:
mainbranch, root directory - Site will be live at
https://username.github.io/repo-name/
Custom Domain
- Add
CNAMEfile with your domain:yourdomain.com - Configure DNS:
A record: 185.199.108.153 A record: 185.199.109.153 A record: 185.199.110.153 A record: 185.199.111.153
Other Hosting Options
- Netlify: Drag and drop folder
- Vercel: Connect GitHub repo
- Any static host: Upload files via FTP
Advanced Modifications
Adding Multiple Unit Tracking
To track probabilities for multiple units simultaneously:
- Expand state space (currently 10 states, would need 10^n for n units)
- Modify transition matrix to multi-dimensional
- Update UI to handle multiple inputs
Note: Computational complexity increases exponentially
Adding Expected Value Calculation
Show expected number of copies:
function getExpectedValue(cost, lvl, a, b, gold) {
const probs = getProbs(cost, lvl, a, b, gold)[0]; // Exact probabilities
let expected = 0;
for (let i = 0; i < probs.length; i++) {
expected += i * probs[i];
}
return expected.toFixed(2);
}Adding Scenario Saving
Use localStorage to save/load scenarios:
function saveScenario(name) {
const scenario = {
cost: document.getElementById("costRange").value,
lvl: document.getElementById("lvlRange").value,
copies: document.getElementById("copiesText").value,
pool: document.getElementById("poolText").value,
gold: document.getElementById("goldText").value
};
localStorage.setItem(name, JSON.stringify(scenario));
}
function loadScenario(name) {
const scenario = JSON.parse(localStorage.getItem(name));
if (scenario) {
document.getElementById("costRange").value = scenario.cost;
// ... set other inputs
updateGraph();
}
}Resources
TFT Data Sources
- Official Riot Patch Notes
- TFT Wiki
- lolchess.gg - Meta and stats
Mathematics
Web Development
Getting Help
- GitHub Issues: Report bugs or request features
- Pull Requests: Submit improvements
- Community: TFT subreddit, Discord servers
License
This project is open source. Check the repository for license details.
Related Articles
- Technical Implementation - Detailed code explanation
- Mathematical Background - Theory behind calculations
- Game Mechanics Reference - TFT data reference